LUCIDAC emulation
The LUCIDAC emulation provides python code to mimic the behaviour of a network-enabled LUCIDAC.
That is, any kind of LUCIDAC client can connect to the TCP/IP service provided by the class
Emulation and the software does best to try to emulate a “mockup”, “virtualized”
or “digital twin” version of the real hardware.
Focus is put on the run simulation which of course makes use of the Circuit simulation code. Therefore, in this usage of words, simulation is part of the extended concept of emulation which also takes into account the JSONL network protocol API.
How to start the emulator
An easy way to start the server is for instance by making up a script,
#!/usr/bin/env python
from lucipy import Emulation
Emulation().serve_forever()
This can be easily adopted, for instance with this advanced version
#!/usr/bin/env python
import sys
from lucipy import Emulation
Emulation(bind_addr="0.0.0.0", bind_port=int(sys.argv[1])).serve_forever()
This version can be called via ./start-server.py 1234 in order to listen on all interfaces
on port 1234
General Features
Network transparent in the same way as the actual LUCIDAC is. Therefore any kind of client should be able to connect.
Multiprocessing “non-blocking” forking version is readily available, however in this case currently each client sees his own emulated and independent LUCIDAC. By default the server is “blocking” the same way as early LUCIDAC firmware versions used to do.
Known limitations
Currently emulates only REV0 LUCIDAC hardware with one MInt and one MMul block.
Very limited support for ACL IN/OUT and ADC IN/OUT. Basically only query based plotting (data aquisition) is supported.
Only a subset of queries is supported. Call
helpin order to get a listing. In particular no calls with respect to administration/device configuration, login/logout, etc are supported for obvious reasons.The emulator only speaks the JSONL protocol. If you want to do something more advanced such as Websockets and the GUI, you can proxy this service by Lucigo (see there also for alternatives).
API Reference
- class lucipy.simulator.Emulation(bind_ip='127.0.0.1', bind_port=5732, emulated_mac='70-79-74-68-6f-6e')[source]
A super simple LUCIDAC emulator. This class allows to start up a TCP/IP server which speaks part of the JSONL protocol and emulates the same way a LUCIDAC teensy would behave. It thus is a shim layer ontop of the Simulation class which gets a configuration in and returns numpy data out. The Emulation instead will make sure it behaves as close as possible to a real LUCIDAC over TCP/IP.
In good RPC fashion, methods are exposed via a tiny registry and marked
@expose.The emulation is very superficial. The focus is on getting the configuration in and some run data which allows for easy developing new clients, debugging, etc. without a real LUCIDAC involved.
Please refer to the documentation for a high level introduction.
Note
Since the overall code does not use asyncio as a philosophy, also this code is written as a very traditional forking server. In our low-volume practice, there should be no noticable performance penalty.
- default_emulated_mac = '70-79-74-68-6f-6e'
The string ‘python’ encoded as Mac address 70-79-74-68-6f-6e just for fun
- get_entities()[source]
Just returns the standard LUCIDAC REV0 entities with the custom MAC address.
- start_run(start_run_msg)[source]
Emulate an actual run with the LUCIDAC Run queue and FlexIO data aquisition.
This function does it all in one rush “in sync” , no need for a dedicated queue.
Should react on a message such as the following:
example_start_run_message = { 'id': '417ebb51-40b4-4afe-81ce-277bb9d162eb', 'session': None, 'config': { 'halt_on_external_trigger': False, # will ignore 'halt_on_overload': True, # 'ic_time': 123456, # will ignore 'op_time': 234567 # most important, determines simulation time }, 'daq_config': { 'num_channels': 0, # should obey 'sample_op': True, # will ignore 'sample_op_end': True, # will ignore 'sample_rate': 500000 # will ignore }}
Considering the DAQ, it will just return the nodal integration points for the time being. One could easily interpolate at the actual times, thought!
- exposed_methods()[source]
Returns a dictionary of exposed methods with string key names and callables as values
- handle_request(line, writer=None)[source]
Handles incoming JSONL encoded envelope and respons with a string encoded JSONL envelope
- Parameters:
line – String encoded JSONL input envelope
writer – Callback accepting a single binary string argument. If provided, is used for firing out-of-band messages during the reply from a handler. Given the linear program flow, it shall be guaranteed that the callback is not called after return.
- Returns:
String encoded JSONL envelope output
- serve_forever(forking=False)[source]
Hand over control to the socket server event queue.
Note
If you choose a forking server, the server can handle multiple clients a time and is not “blocking” (the same way as the early real firmware embedded servers were). However, the “parallel server” in a forking (=multiprocessing) model also means that each client gets its own virtualized LUCIDAC due to the multiprocess nature (no shared address space and thus no shared LUCIDAC memory model) of the server.
- Parameters:
forking – Choose a forking server model