Accessing variables#
In the SDK, Variables, Aliases, RPV are presented to the client side through an interface called a watchable
, e.g. something you can watch.
Some watchables are available only when the server has loaded the SFD matching the device firmware (alias, var), others are available as
soon as a device is connected (RPV)
Type |
Description |
SFD required |
Variable |
A variable maps to a static or global variable declared in the embedded device firmware. The variable address, type, size and endianness is defined in the loaded SFD |
Yes |
Runtime Published Values (RPV) |
Readable and writable elements identified by a numerical ID (16 bits) and declared by the device during the handshake phase with the server. |
No |
Alias |
Abstract writable/readable entity that maps to either a variable or a RPV. Used to keep a consistent firmware interface with existing scripts using this SDK |
Yes |
Basics#
The first step to access a watchable, is to first tell the server that we want to subscribe to update event on that watchable.
To do so, we use the watch
method and specify the path of the watchable. The path
depends on the firmware and must generally be known in advance.
The path is dependent on the firmware and is typically known beforehand.
It’s also possible to query the server for a list of available watchables, a feature utilized by the GUI.
For scripts based on the SDK, it is generally assumed that the elements to be accessed are predetermined and won’t necessitate user input for selection.
- ScrutinyClient.watch(path)[source]#
Starts watching a watchable element identified by its display path (tree-like path)
- Parameters:
path (str) – The path of the element to watch
- Raises:
OperationFailure – If the watch request fails to complete
TypeError – Given parameter not of the expected type
- Returns:
A handle that can read/write the watched element.
- Return type:
Once an element is being watched, the server starts polling for the value of that element.
Each time the value is updated, the server broadcast a value update to all subscribers, in this case, our client.
Concurrently, a background thread is actively listening listen for these updates and accordingly
modifies the value that the WatchableHandle
refers to
- class scrutiny.sdk.watchable_handle.WatchableHandle[source]#
A handle to a server watchable element (Variable / Alias / RuntimePublishedValue) that gets updated by the client thread.
- __init__(client, display_path)[source]#
- Parameters:
client (ScrutinyClient) –
display_path (str) –
- Return type:
None
- get_enum()[source]#
Returns the enum associated with this watchable
- Raises:
BadEnumError – If the watchable has no enum assigned
- Return type:
- parse_enum_val(val)[source]#
Converts an enum value name (string) to the underlying integer value
- Parameters:
val (str) – The enumerator name to convert
- Raises:
BadEnumError – If the watchable has no enum assigned or the given value is not a valid enumerator
TypeError – Given parameter not of the expected type
- Return type:
int
- property display_path: str#
Contains the watchable full tree path
- property name: str#
Contains the watchable name, e.g. the basename in the display_path
- property type: WatchableType#
The watchable type. Variable, Alias or RuntimePublishedValue
- property datatype: EmbeddedDataType#
The data type of the device element pointed by this watchable. (sint16, float32, etc.)
- property value: int | float | bool#
The last value received for this watchable. Can be written
- property value_bool: bool#
The value casted as bool
- property value_int: int#
The value casted as int
- property value_float: float#
The value casted as float
- property value_enum: str#
The value converted to its first enum name (alphabetical order). Returns a string. Can be written with a string
- property last_update_timestamp: datetime | None#
Time of the last value update.
None
if not updated at least once. Not reliable for change detection
- property last_write_timestamp: datetime | None#
Time of the last successful write operation.
None
if never written
- property update_counter: int#
Number of value update gotten since the creation of the handle. Can be safely used for change detection
- class scrutiny.sdk.WatchableType[source]#
(Enum) Type of watchable available on the server
- Variable = 1#
A variable found in the device firmware debug symbols
- RuntimePublishedValue = 2#
A readable/writable element identified by a 16bits ID. Explicitly defined in the device firmware source code
- Alias = 3#
A symbolic link watchable that can refers to a
Variable
or aRuntimePublishedValue
After getting a handle to the watchable, the value
property and its derivative (
value_int
,
value_float
,
value_bool
,
value_enum
) undergo automatic updates. These values are invalid until their initial update,
meaning that after the call to watch
, there is a period of time where accessing the
value
property will raise a InvalidValueError
.
To await a single value update from the watchable, one can utilize the WatchableHandle.wait_update
method. Alternatively, to wait for updates from all watched variables at once, the
ScrutinyClient.wait_new_value_for_all
method can be invoked.
import time
from scrutiny.sdk.client import ScrutinyClient
client = ScrutinyClient()
with client.connect('localhost', 8765):
w1 = client.watch('/alias/my_alias1')
w2 = client.watch('/rpv/x1234')
w3 = client.watch('/var/main.cpp/some_func/some_var')
client.wait_new_value_for_all() # Make sure all watchables have their first value available
while w1.value_bool: # Value updated by a background thread
print(f"w2 = {w2.value}") # Value updated by a background thread
time.sleep(0.1)
w3.value = 123 # Blocking write. This statement blocks until the device has confirmed that the variable is correctly written (or raise on failure).
Note
Reading and writing a watchable may raise an exception.
- ReadingWhen value is unavailable. This will happen if
The watchable has never been updated (small window of time after subscription)
The server disconnects
The device is disconnects
- WritingWhen the value cannot be written. This will happen if
The server disconnects
The device is disconnects
Writing is actively denied by the device. (Communication error or protected memory region)
Timeout: The write confirmation takes more time than the client
write_timeout
As demonstrated in the preceding example, device access is executed in a fully synchronized manner. Consequently, a script utilizing the Scrutiny Python SDK can be perceived as a thread operating on the embedded device with a slower memory access time.
Detecting a value change#
When developing a script that uses the SDK, it is common to have some back and forth between the device and the script. A good example would be the case of a test sequence, one could write a sequence that looks like this.
Write a GPIO
Wait for another GPIO to change its value
Start an EEPROM clear sequence
Wait for the sequence to finish
Each time the value is updated by the server, the WatchableHandle.update_counter
gets incremented.
Looking for this value is helpful to detect a change.
Two methods can help the user to wait for remote events. WatchableHandle.wait_update
and
WatchableHandle.wait_value
The server periodically broadcasts value updates, typically at a rapid pace. The delay in updates is primarily dependent on the saturation level of the communication link with the device. Factors such as the number of watchable subscriptions and the available bandwidth will influence the update rate. The server polls the device for each watchable in a round-robin scheme. When value updates are available, they are aggregated and flushed to all clients. In most common scenarios, a value update can be expected within a few hundred milliseconds.
- WatchableHandle.wait_update(timeout, previous_counter=None, sleep_interval=0.02)[source]#
Wait for the value to be updated by the server
- Parameters:
timeout (float) – Amount of time to wait for a value update
previous_counter (int | None) – Optional update counter to use for change detection. Can be set to
update_counter+N
to wait for N updatessleep_interval (float) – Value passed to
time.sleep
while waiting
- Raises:
TypeError – Given parameter not of the expected type
ValueError – Given parameter has an invalid value
InvalidValueError – If the watchable becomes invalid while waiting
TimeoutException – If no value update happens within the given timeout
- Return type:
None
- WatchableHandle.wait_value(value, timeout, sleep_interval=0.02)[source]#
Wait for the watchable to reach a given value. Raises an exception if it does not happen within a timeout value
- Parameters:
value (int | float | bool | str) – The value that this watchable must have to exit the wait state
timeout (float) – Maximum amount of time to wait for the given value
sleep_interval (float) – Value passed to
time.sleep
while waiting
- Raises:
TypeError – Given parameter not of the expected type
ValueError – Given parameter has an invalid value
InvalidValueError – If the watchable becomes invalid while waiting
TimeoutException – If the watchable value never changes for the given value within the given timeout
- Return type:
None
- ScrutinyClient.wait_new_value_for_all(timeout=5)[source]#
Wait for all watched elements to be updated at least once after the call to this method
- Parameters:
timeout (float) – Amount of time to wait for the update
- Raises:
TypeError – Given parameter not of the expected type
ValueError – Given parameter has an invalid value
TimeoutException – If not all watched elements gets updated in time
- Return type:
None
Batch writing#
Writing multiples values in a row is inefficient due to the latency associated with device access.
To optimize speed, one can consolidate multiple write operations into a single batched request using the
ScrutinyClient.batch_write
method.
In a batch write operation, multiple write requests are queued and dispatched to the server in a single API call. The server then executes all write operations in the correct order and confirms the completion of the entire batch.
It is permissible to perform multiple writes to the same watchable within the same batch. The server ensures that each write operation is completed and acknowledged by the device before initiating the subsequent operation.
- ScrutinyClient.batch_write(timeout=None)[source]#
Starts a batch write. Write operations will be enqueued and committed together. Every write is guaranteed to be executed in the right order
- Parameters:
timeout (float | None) – Amount of time to wait for the completion of the batch once committed. If
None
the default write timeout will be used.- Raises:
TypeError – Given parameter not of the expected type
ValueError – Given parameter has an invalid value
OperationFailure – Failed to complete the batch write
- Return type:
BatchWriteContext
Example#
w1 = client.watch('/alias/my_alias1')
w2 = client.watch('/rpv/x1234')
w3 = client.watch('/var/main.cpp/some_func/some_var')
try:
with client.batch_write(timeout=3):
w1.value = 1.234
w2.value = 0x11223344
w2.value = 0x55667788
w3.value = 2.345
w1.value = 3.456
# Exiting the with block will block until the batch completion or failure (with an exception)
print("Batch writing successfully completed")
except ScrutinySDKException as e:
print(f"Failed to complete a batch write. {e}")
Accessing the raw memory#
In certain scenarios, it may be advantageous to directly access the device memory, bypassing the server’s interpretive layer that transforms the data into a meaningful value. Such scenarios could include:
Dumping a data buffer
Uploading a firmware
Pushing a ROM image
etc.
For those cases, one can use ScrutinyClient.read_memory
and ScrutinyClient.write_memory
to access the memory.
- ScrutinyClient.read_memory(address, size, timeout=None)[source]#
Read the device memory synchronously.
- Parameters:
address (int) – The start address of the region to read
size (int) – The size of the region to read, in bytes.
timeout (float | None) – Maximum amount of time to wait to get the data back. If
None
, the default timeout value will be used
- Raises:
TypeError – Given parameter not of the expected type
ValueError – Given parameter has an invalid value
OperationFailure – Failed to complete the reading
TimeoutException – If the read operation does not complete within the given timeout value
- Return type:
bytes
- ScrutinyClient.write_memory(address, data, timeout=None)[source]#
Write the device memory synchronously. This method will exit once the write is completed otherwise will throw an exception in case of failure
- Parameters:
address (int) – The start address of the region to read
data (bytes) – The data to write
timeout (float | None) – Maximum amount of time to wait to get the write completion confirmation. If
None
, the default write timeout value will be used
- Raises:
TypeError – Given parameter not of the expected type
ValueError – Given parameter has an invalid value
OperationFailure – Failed to complete the reading
TimeoutException – If the read operation does not complete within the given timeout value
- Return type:
None
Available watchables#
It is possible to query the server for the current number of available watchable items and also download their definition
This feature is typically not required for automation scripts; however, it can be ncessary for presenting users with selectable watchable items. It is currently utilized by the Scrutiny GUI to populate the Variable List widget
- ScrutinyClient.get_watchable_count()[source]#
Request the server with the number of available watchable items, organized per type
- Raises:
ValueError – Bad parameter value
TypeError – Given parameter not of the expected type
OperationFailure – If the command completion fails
- Returns:
A dictionnary containing the number of watchables, classified by type
- Return type:
Dict[WatchableType, int]
- ScrutinyClient.download_watchable_list(types=None, max_per_response=500, name_patterns=[])[source]#
Request the server for the list of watchable items available in its datastore.
- Parameters:
types (List[WatchableType] | None) – List of types to download. All of them if
None
max_per_response (int) – Maximum number of watchable per datagram sent by the server.
name_patterns (List[str]) – List of name filters in the form of a glob string. Any watchable with a path that matches at least one name filter will be returned. All watchables are returned if
None
- Raises:
ValueError – Bad parameter value
TypeError – Given parameter not of the expected type
ConnectionError – If the connection to the server is broken
- Returns:
A handle to the request object that can be used for synchronization (
wait_for_completion
) or cancel the request (cancel
)- Return type:
Following is the object returned download_watchable_list
:
- class scrutiny.sdk.client.WatchableListDownloadRequest[source]#
Represents a pending watchable download request. It can be used to wait for completion or cancel the request.
- cancel()[source]#
- Informs the client that this request can be canceled.
Subsequent server response will be ignored and this request will be marked as completed, but failed.
- Raises:
TimeoutException – If the client fails to cancel the request. Should never happen
- Return type:
None
- get()[source]#
Returns the definition of all the watchables obtained through this request, classified by type.
- Raises:
InvalidValueError – If the data is not available yet (if the request did not completed successfully)
- Returns:
A dictionary of dictionary containing the difinition of each watchable entry that matched the filters. foo[type][display_path] = <definition>
- Return type:
Dict[WatchableType, Dict[str, WatchableConfiguration]]
- wait_for_completion(timeout=None)#
Wait for the request to complete
- Params timeout:
Maximum wait time in seconds. Waits forever if
None
- Raises:
TimeoutException – If the request does not complete in less than the specified timeout value
OperationFailure – If an error happened that prevented the request to successfully complete
- Parameters:
timeout (float | None) –
- Return type:
None
- property completed: bool#
Indicates whether the request has completed or not
- property completion_datetime: datetime | None#
The time at which the request has been completed.
None
if not completed yet
- property failure_reason: str#
When the request failed, this property contains the reason for the failure. Empty string if not completed or succeeded
- property is_success: bool#
Indicates whether the request has successfully completed or not
- class scrutiny.sdk.WatchableConfiguration[source]#
(Immutable struct) Represents a watchable available in the server datastore
- server_id: str#
The unique ID assigned to that watchable item by the server
- watchable_type: WatchableType#
The type of the item, either a Variable, an Alias or a Runtime Published Value
- datatype: EmbeddedDataType#
The data type of the value in the embedded firmware that this watchable refers to
- enum: EmbeddedEnum | None#
An optional enumeration associated with the possible values of the item