Lucipy Synchronous Client

API refererence

An Synchronous Hybrid Controller Python Client for REDAC/LUCIDAC

This is a minimal python client, making simple things simple. That means things like device managament is capable of the python REPL without headache, following the KISS principle.

This client implementation does not feature strong typing, dataclasses, asynchronous functions. Instead, it implements a blocking API and tries to mimic the way how the Model-1 Hybrid Controller interface worked (the one in https://github.com/anabrid/pyanalog/).

This is a single file implementation focussing on portability and minimal dependencies. If you have pyserial installed, it will be used, otherwise this also runs fine without.

class lucipy.synchc.dotdict[source]

dot.notation access to dictionary attributes

lucipy.synchc.has_data(fh)[source]

Peeks a file handle (checks for data without reading/consuming)

class lucipy.synchc.tcpsocket(host, port, auto_reconnect=True)[source]

A socket with readline support

send(sth)[source]

Expects sth to be a string

read(*args, **kwargs)[source]

Returns a complete line as string. See instead also: self.s.recv(123)

class lucipy.synchc.serialsocket(device)[source]

Uses pyserial to connect to directly attached device

class lucipy.synchc.jsonlines(actual_socket)[source]

Middleware that speaks dictionaries at front and JSON at back

exception lucipy.synchc.HybridControllerError[source]
lucipy.synchc.endpoint2socket(endpoint_url: Endpoint | str) tcpsocket | serialsocket[source]

Provides the appropriate socket for a given endpoint

class lucipy.synchc.Run(hc)[source]

Represents a running Run

data() Iterator[list[float]][source]

“Slurp” all data aquisition from the run. Basically a “busy wait” or “synchronous wait” until the run finishes. data() returns once the run is stopped and yields data otherwise. Therefore usage can be just like list(hc.run().data())

class lucipy.synchc.LUCIDAC(endpoint_url=None, auto_reconnect=True, register_methods=True)[source]

This kind of class is known as HybridController in other codes. It serves as primary entry point for the lucipy code.

The constructor has a variety of variants how to be called:

Parameters:
  • endpoint_url

    If no endpoint (either as string or Endpoint class instance) is provided, will lookup the environment variable LUCIDAC_ENDPOINT.

    If neither an endpoint nor the environment variable is set, autodetection is applied and the first connection is chosen. Note that if no LUCIDAC is attached via USB serial, the zeroconf detection will require a few hundred milliseconds, depending on your network.

  • auto_reconnect – Whether reconnect in case of los connection

  • register_methods – Register typical message types exposed by the server as methods for this class. This allows to call hc.foo(bar) instead of hc.query("foo", bar).

req_id

Eth Mac address of Microcontroller, required for the circuit entity hierarchy

hc_mac

Storage for stateful preparation of runs.

run_config

Storage for stateful preparation of runs.

register_methods(commands, memoizable=[], overwrite=False)[source]

register method shorthands. Typically this method is only used by __init__.

send(msg_type, msg={})[source]

Sets up an envelope and sends it, but does not wait for reply

query(msg_type, msg={})[source]

Sends a query and waits for the answer, returns that answer

slurp()[source]

Read all remaining stuff in the socket. Useful for broken run cleanup

get_mac()[source]

Get system ethernet mac address. Is cached.

get_entities()[source]

Gets entities and determines system Mac address

static determine_idal_ic_time_from_k0s(mIntConfig)[source]

Given a MIntBlock configuration, which looks like [{k:1000,ic:...},{k:10000,ic:...}...], determines the ideal ic time, in nanoseconds

set_config(config)[source]

config being something like dict("/U": ..., "/C": ...), i.e. the entities for a single cluster. There is only one cluster in LUCIDAC.

Warning

This also determines the ideal IC time if that has not been set before (either manually or in a previous run).

set_circuit(circuit)[source]

set_config was renamed to set_circuit in later firmware versions

set_by_path(path, config)[source]

path is a string like ["/C", "17", "factor"]

set_op_time(*, ns=0, us=0, ms=0)[source]

Sets OP-Time with clear units. Returns computed value, which is just the sum of all arguments.

Consider the limitations reported in start_run().

Note that this function signature is somewhat comparable to python builtin datetime.timedelta, however Python’s timedelta has only microseconds resolution which is not as highly-resolved as in LUCIDAC.

Parameters:
  • ns – nanoseconds

  • us – microseconds

  • ms – milliseconds

set_daq(*, num_channels=0, sample_op=True, sample_op_end=True, sample_rate=500000)[source]
Parameters:
  • num_channels – Data aquisition specific - number of channels to sample. Between 0 (no data aquisition) and 8 (all channels)

  • sample_op – Sample a first point exactly when optime starts

  • sample_op_end – Sample a last point exactly when optime ends

  • sample_rate – Number of samples requested over the overall optime (TODO: check if this descrption is correct)

set_run(*, halt_on_external_trigger=False, halt_on_overload=True, ic_time=None, op_time=None)[source]
Parameters:
  • halt_on_external_trigger – Whether halt the run if external input triggers

  • halt_on_overload – Whether halt the run if overload occurs during computation

  • ic_time – Request time to load initial conditions, in nanoseconds. This time depends on the k0 factors. However, it is rarely neccessary to tune this parameter, once useful values have been used. If not set, a value derived from the (first time in object lifetime) configuration set will be used.

  • op_time – Request time for simulation, in nanoseconds. This is the most important option for this method. Note that by a current limitation in the hardware, only op_times < 1sec are supported.

start_run() Run[source]

Uses the set_run and set_daq as before. Returns a Run object which allows to read all data.

manual_mode(to: str)[source]

manual mode control

master_for(*minions)[source]

Create a master-minion setup with at least one controlled LUCIDAC (minion). minions: type LUCIDAC

class lucipy.synchc.LUCIGroup(master: LUCIDAC, *minions: LUCIDAC)[source]

Group of LUCIDACs in a master/minion setup. Usage is like

>>> gru    = LUCIDAC("tcp://foo")       
>>> kevin  = LUCIDAC("tcp://bar")       
>>> bob    = LUCIDAC("tcp://baz")       
>>> group  = LUCIGroup(gru, kevin, bob) 
>>> group.set_circuit(...)              
>>> group.start_run() ...