Plesty attoDRY2100XL eNSPIRE server
A Plesty device server for the attoDRY2100XL eNSPIRE controller. It translates a small, explicitly typed Plesty API into the controller's JSON-RPC 2.0 protocol. Laboratory clients can read cryostat telemetry and use guarded temperature and magnet controls over ZeroMQ; arbitrary JSON-RPC forwarding is intentionally not available.
Architecture
The server runs on the computer that is directly connected to eNSPIRE:
laboratory client
| Plesty over ZeroMQ, normally TCP 5555
v
this device server on the control computer
| eNSPIRE JSON-RPC 2.0, normally TCP 9090
v
eNSPIRE controller
The two TCP ports serve different protocols and must not be interchanged:
DEVICE_PORTis the vendor JSON-RPC port on the eNSPIRE controller, normally9090. Only the directly connected control computer needs to reach it.DEVICE_TCP_PORT(or--tcp-port) is the ZeroMQ port that this server binds, normally5555. Approved laboratory clients connect to this port.
The server keeps one persistent controller connection and uses one dedicated worker thread by default. This serializes controller calls and preserves the connection's thread affinity.
Installation
Python 3.12 or newer and uv are required.
git clone https://gitlab.com/plesty/hub/devices/attocube/plesty-attodry2100xl-enspire-server.git
cd plesty-attodry2100xl-enspire-server
uv sync
Copy .env.example to .env, then edit .env on the control computer. The
real .env is ignored by Git and must never be committed or pasted into issue
reports. Process-environment values override .env; command-line connection
arguments override both.
Copy-Item .env.example .env
At minimum, set:
DEVICE_HOST=<controller host>
DEVICE_PORT=9090
DEVICE_TCP_PORT=5555
DEVICE_HOST and DEVICE_PORT select the eNSPIRE endpoint. DEVICE_TCP_PORT
selects the Plesty listener. --host, --port, and --tcp-port are temporary
command-line overrides. --id changes the Plesty device instance ID.
The remaining settings are:
| Variable | Purpose |
|---|---|
ENSPIRE_RUN_HARDWARE_TESTS |
Explicit opt-in for the harmless real-controller pytest; leave 0 for normal test runs. |
ENSPIRE_MAGNET_{X,Y,Z}_CHANNEL |
Vendor channel number for an installed magnet axis. |
ENSPIRE_MAGNET_{X,Y,Z}_MIN_T / MAX_T |
Installation-approved axis bounds in tesla. |
ENSPIRE_MAGNET_COUPLED_VECTOR_MAX_T |
Maximum target-vector magnitude whenever X or Y is nonzero. |
ENSPIRE_MAGNET_TRANSVERSE_ZERO_TOLERANCE_T |
Measured X/Y tolerance for entering or starting Z-only operation. |
ENSPIRE_MAGNET_ZERO_TOLERANCE_T |
Measured absolute-field tolerance used by magnet_off. |
ENSPIRE_MAGNET_ZERO_TIMEOUT_S |
Maximum time magnet_off waits for measured zero. |
ENSPIRE_MAGNET_POLL_INTERVAL_S |
Interval between measured-field checks during magnet_off. |
DEVICE_ADDRESS / PLESTY_REPORT_ARCHIVE |
Optional Plesty field-test settings; not needed to run the server. |
Magnet writes are disabled unless at least one axis has a complete channel,
minimum, and maximum configuration and both global magnet settings are set.
Leave an unavailable axis completely blank. Partial configuration is a startup
error. Installation limits belong in the ignored .env, not in tracked files.
The Z-axis minimum and maximum define the available Z-only range.
Running the server
On the directly connected control computer, run:
uv run python -m plesty.attodry2100xl_enspire_server --tcp-port 5555
The process binds ZeroMQ to all network interfaces. Stop it with Ctrl+C; the
shutdown path releases the ZeroMQ listener and closes the controller socket.
Run --help to see all command-line overrides. Keep the default dedicated-thread
mode. --pool-threading exists for diagnostics and is not recommended for normal
instrument operation.
For unattended operation, use the laboratory's approved Windows service wrapper
or task supervisor such as for example Windows Task Scheduler, WinSW, NSSM, or
another managed service system. Configure its working directory as this
repository, run the same uv run python -m ... command under a dedicated
account that can read the local .env, and send a normal termination before
forcing the process to stop. Do not configure two instances to use the same
controller or ZeroMQ port. Avoid an unbounded automatic restart loop: after an
unexpected exit, an operator must check the controller's current command,
targets, and measured state before the server is returned to service. After a
restart, perform read-only health queries before allowing state-changing calls.
Dedicated account explanation: Use a dedicated Windows user account created specifically for this service—not a personal or administrator account. It should have only the permissions it needs: reading the repository and .env, executing the application, accessing the controller network, and listening on the configured ZeroMQ port.
Client examples
Install plesty-lib on a laboratory client and connect to the control computer's
ZeroMQ port. Parameter keys are grouped with a dot:
from plesty.lib.service import build_client
with build_client("tcp://<control-computer>:5555", timeout=10_000) as client:
sane = client.query("system.is_cryostat_sane")
sample_k = client.query("temperature.sample")
pressure_mbar = client.query("pressure.cryo_in")
field_t = client.query("magnet.channel_0_field")
api = client.describe()
describe() returns the discoverable parameter and operation schema. The public
parameter groups are identity, system, temperature, pressure, and
magnet. Important writable parameters are:
# These calls change hardware state. Use only with operator approval.
client.write("temperature.sample_setpoint", 4.0)
client.write("temperature.sample_ramp_rate", 1.0)
client.set_temperature_ramp_control("sample", True)
client.set_temperature_control("sample", True)
Sample and VTI setpoints accept 0 through 310 K. Ramp rates accept 0 (limit
disabled) or 0.1 through 100 K/min. Writes change stored settings; the named
operations start or stop control. Calls return after controller acknowledgement,
not after thermal equilibrium. A ramp from below 10 K to 310 K can take at least
three hours, so monitor the measured value, setpoint, and control-status
parameters asynchronously.
The magnet API is also explicit:
# Installation limits from the server's .env are enforced before any write.
client.set_magnetic_field(z_t=0.0, y_t=0.0, x_t=0.0)
client.set_field_control(True)
# This blocks until every configured measured field is within zero tolerance.
client.magnet_off(timeout=7_300)
set_magnetic_field validates every axis and the full vector before changing
targets. It does not start field control, although changing a target while field
control is already active can start motion immediately. Starting field control
checks for quench, driven mode, and a safe stored target. Full-scale ramps can
take tens of minutes. The magnet_off client timeout is in seconds and must be
longer than ENSPIRE_MAGNET_ZERO_TIMEOUT_S; the build_client default timeout is
in milliseconds and is too short for this blocking operation.
Fields above the coupled limit require exact-zero X/Y targets and measured X/Y values within
ENSPIRE_MAGNET_TRANSVERSE_ZERO_TOLERANCE_T. Envelope changes are deliberately
non-blocking and must be staged. To enter high-Z operation, first set X/Y to zero
while Z remains in the coupled envelope, wait for transverse telemetry to settle,
then request high Z. To leave it, lower Z with X/Y still zero, wait until measured
field is inside the coupled envelope, then request a transverse target. Premature
crossings fail before any setpoint write.
The other named operation, cancel_current_command(expected_command), cancels
only when the controller's current command exactly matches the supplied value.
This guard prevents a stale client from cancelling a different command.
Security assumptions
The current Plesty ZeroMQ transport does not authenticate clients or encrypt
traffic, and this server exposes hardware-changing operations. Bind it only on a
trusted, access-controlled laboratory network. Use the host firewall to allow
DEVICE_TCP_PORT only from approved client addresses; never expose it to the
public internet or an untrusted wireless network. Keep the eNSPIRE network itself
isolated so only the control computer can reach DEVICE_PORT.
Do not put credentials, controller addresses, serial numbers, installation field limits, or measured laboratory values in commits or shared logs. The server does not expose controller network configuration, firmware updates, calibration, pumps, valves, shutdown, quench reset, service termination, or an unrestricted raw JSON-RPC method.
Validation
Normal validation uses a local fake TCP controller and an in-memory eNSPIRE simulator, and does not touch hardware:
uv run pytest
uv run plesty check
The module is held to the quantum standard, so plesty check runs the full gate
set locally, including the eight-gate device mock pipeline.
host="mock" (or --host mock) replaces the TCP client with the simulator in
mock_controller.py, so the whole stack — read specs, decoders, device-error
validation, state changes — runs with no cryostat and nothing patched out. The
simulated controller is idealised: a control loop settles on its target at once.
It therefore says nothing about ramp behaviour, controller result layouts, or
transport pathologies on the real instrument.
The public parameter surface is declared as data in
plesty/attodry2100xl_enspire_server/schema_param.json and shipped in the wheel;
the vendor method behind each key stays in device.READ_SPECS / WRITE_SPECS,
and a test asserts the two never drift apart.
On Windows consoles that cannot encode the compliance command's Unicode status symbols, run the equivalent check in UTF-8 mode:
uv run python -X utf8 -c "from plesty.sdk.cli import app; app()" check
The real-controller pytest remains skipped unless
ENSPIRE_RUN_HARDWARE_TESTS=1 is explicitly present. That opt-in test is
read-only; do not use live state-changing calls as a generic release check.
Troubleshooting
- Startup says
DEVICE_HOSTorDEVICE_PORTis missing: create.envin the repository root or provide--hostand--port. Check spelling and the process account's working directory. - Startup reports invalid magnet safety configuration: define channel,
minimum, and maximum together for every installed axis plus
ENSPIRE_MAGNET_COUPLED_VECTOR_MAX_TandENSPIRE_MAGNET_TRANSVERSE_ZERO_TOLERANCE_T, or blank all magnet settings to disable writes. - Controller connection fails: verify the eNSPIRE Web GUI from the control computer, then verify that its JSON-RPC port is reachable. Do not substitute the ZeroMQ port for the vendor port.
- A remote client cannot connect: confirm the server is running, the client
uses the control computer rather than the eNSPIRE address, the ZeroMQ port
matches
DEVICE_TCP_PORT, and the host firewall permits that client. - A request times out: ordinary reads should finish quickly. For long ramps,
setters return immediately and clients should poll telemetry. Only
magnet_offwaits for a physical target; give it a timeout longer than the configured zero timeout and continue monitoring if it reports a timeout. - Field control is rejected: inspect quench state, driven mode, configured axes, stored targets, per-axis bounds, and vector magnitude. Do not weaken the local limits to bypass a controller condition.
- A ramp is slower than expected: read
read_ramp_rates(channel). The rate is not one number per magnet — it is a table of current zones, and the rate changes abruptly between them. The bounds are in amperes and the rates in A/s; converting to tesla needs the magnet's amps-per-tesla, which this API does not supply anywhere, so it has to come from the magnet's specification or from timing a known ramp.set_ramp_ratewill not exceed a zone's factory rate and will not move a zone boundary. - The controller drops or returns malformed JSON-RPC: the server closes the vendor socket and reports the failure, then reopens it on the next request — a stalled reply costs one call, not the session. Never blindly retry a state-changing call whose delivery is uncertain: the failed one may already have executed.
- Replies stall while a ramp starts: a control computer that does not answer
within
ENSPIRE_REQUEST_TIMEOUT_S(30 s by default) drops the socket, because a late reply would desync every later request. Raise that budget for an installation that stalls longer; it bounds one round trip, never a ramp. - The ZeroMQ port is already in use: stop the previous server cleanly or choose one approved alternate port and update clients. Never run competing instances against the same controller.
Scope
This first release supports enumerated telemetry, sample/VTI temperature settings and controls, guarded current-command cancellation, installation-bounded field targets and control, and verified magnet-off. Go-to-base and sample exchange are intentionally absent. Refer to the generated Plesty parameter and function pages for the complete machine-readable API.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file plesty_attodry2100xl_enspire_server-0.1.0.tar.gz.
File metadata
- Download URL: plesty_attodry2100xl_enspire_server-0.1.0.tar.gz
- Upload date:
- Size: 171.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5569a15560de5f29b92d1d2b384f8cbac78aad6e1db6ea82fe026be955207807
|
|
| MD5 |
57ce6ee882711831e9b2144729da1eb8
|
|
| BLAKE2b-256 |
7eccc77e8457d535b81b2cc9a2351745971cf46c5096f11fb9179232721eb078
|
File details
Details for the file plesty_attodry2100xl_enspire_server-0.1.0-py3-none-any.whl.
File metadata
- Download URL: plesty_attodry2100xl_enspire_server-0.1.0-py3-none-any.whl
- Upload date:
- Size: 38.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b371af2129c66f5211ca50efaf86795b70ecfa9b280331295e902e77af685350
|
|
| MD5 |
6a12b2e98c71dd81eabeafc3fcee8c1a
|
|
| BLAKE2b-256 |
a658028ea5e6773ec9f68bc829d43446baf4865b08c36e3aaf2bb39a0fa405b8
|