Remote Access to Devices
Please note that this functionality should only be used on a trusted network.
Besides the device specific protocols, the device can be made accessible
from the outside via JSON-RPC over ZMQ. This can be achieved by passing
the -r
option with a host:port
string to the simulation:
$ lewis chopper -r 127.0.0.1:10000 -p "epics: {prefix: 'SIM:'}"
Now the device can be controlled via the lewis-control.py
-script
in a different terminal window. The service can be queried to show the
available objects by not supplying an object name:
$ lewis-control -r 127.0.0.1:10000
The -r
(or --rpc-host
) option defaults to the value shown here,
so it will be omitted in the following examples. To get information on
the API of an object, supplying an object name without a property or
method will list the object’s API:
$ lewis-control device
This will output a list of properties and methods which is available for remote access. This may not comprise the full interface of the object depending on the server side configuration. Obtaining the value of a property is done like this:
$ lewis-control device state
The same syntax is used to call methods without parameters:
$ lewis-control device initialize
To set a property to a new value, the value has to be supplied on the command line:
$ lewis-control device target_speed 100
$ lewis-control device start
It is possible to set multiple device parameters at once, but this goes through the simulation itself, so that it is generic to all devices:
$ lewis-control simulation set_device_parameters "{'target_speed': 1, 'target_phase': 20}"
Another case of device-related access to the simulation is switching the setup. To obtain a list of available setups, the following command is available:
$ lewis-control simulation setups
It is then possible to switch the setup to one from the list, assuming it is called new_setup
:
$ lewis-control simulation switch_setup new_setup
The setup switching process is logged.
Accessing the Device Communication Interface
Just as device model and communication interface are separate concepts in Lewis, these interfaces can be controlled separately as well.
To query the available communication protocols, the following command is available:
$ lewis-control interface protocols
This will list all communication protocols that are currently exposing device behavior. The following methods are available for interacting with the communication interfaces:
$ lewis-control interface disconnect
$ lewis-control interface connect
$ lewis-control interface is_connected
$ lewis-control interface documentation
Without any arguments, these methods will affect all of the device’s interfaces, but specifying
any number of valid protocol names will limit the method to those protocols. Assuming a device
has two interfaces, one for the stream
protocol and one for epics
, the following sequence
would disconnect both, but then only reconnect the stream
-adapter:
$ lewis-control interface disconnect
$ lewis-control interface connect stream
$ lewis-control interface is_connected stream
True
$ lewis-control interface is_connected
{'stream': True, 'epics': False}
Disconnecting is essentially the equivalent of “cutting the cable”, no new connections will be accepted and existing ones will be terminated.
To find out how to interact with any device via its usual communication channels a way would be:
$ lewis-control interface protocol
['stream', 'epics']
$ lewis-control interface documentation epics
[ ... long description of protocol ... ]
Value Interpretation and Syntax
lewis_control
interprets values as built-in Python literals or containers using
ast.literal_eval
. This means any
syntax for literal evaluation supported by Python works here as well. The following are all valid
values which are interpreted as you might expect:
$ lewis-control device float_value 12.0
$ lewis-control device float_value .5
$ lewis-control device float_value 1.23e10
$ lewis-control device int_value 123
$ lewis-control device int_value 0xDEADBEEF
$ lewis-control device int_value 010 # Value of 8 in base 8 (octal)
$ lewis-control device str_value hello_world
$ lewis-control device method_call_with_two_string_args hello world
$ lewis-control device str_value "hello world"
$ lewis-control device unicode_value "u'hello_world'"
$ lewis-control device list_value "[1,2,3]"
$ lewis-control device list_value "['a', 'b', 'c']"
$ lewis-control device dict_value "{'a': 1, 'b': 2}"
WARNING: Any value that cannot be successfully evaluated is silently converted into a string literal instead! The following attempts turn into strings because the letters are not quoted:
$ lewis-control device str_value_looks_like_dict "{a: 1, b: 2}"
$ lewis-control device str_value_looks_like_list "[a, b, c]"
This is done for convenience, to avoid having to double quote and/or escape quote trivial string values to match Python syntax while also taking shell quotation and escapes into account. But it can lead to unexpected results at times.
Control Client Python API
For use cases that require more flexibility and control, it is advised to write a Python script
using the API provided in lewis.core.control_client
instead of using the command line utility.
This makes it possible to use the remote objects in a fairly transparent fashion.
Here is a brief example using the chopper
device:
from time import sleep
from lewis.core.control_client import ControlClient
client = ControlClient(host='127.0.0.1', port='10000')
chopper = client.get_object('device')
chopper.target_speed = 100
chopper.initialize()
while chopper.state != 'stopped':
sleep(0.1)
chopper.start()
All calls, reads and assignments are synchronous and blocking in terms of the methods and attributes they access on the server. However, much like with real devices, the behaviour of the simulated device is asynchronous from its interface. Consequently, depending on the specific device, some effects of calling a method may take place long after the method is called (and returns).
This is why, in the above example, a loop is used to wait for chopper.state
to change in
response to the chopper.initialize()
call.