Skip to main content

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_PORT is the vendor JSON-RPC port on the eNSPIRE controller, normally 9090. Only the directly connected control computer needs to reach it.
  • DEVICE_TCP_PORT (or --tcp-port) is the ZeroMQ port that this server binds, normally 5555. 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_HOST or DEVICE_PORT is missing: create .env in the repository root or provide --host and --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_T and ENSPIRE_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_off waits 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_rate will 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

plesty_attodry2100xl_enspire_server-0.1.0.tar.gz (171.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

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

Hashes for plesty_attodry2100xl_enspire_server-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5569a15560de5f29b92d1d2b384f8cbac78aad6e1db6ea82fe026be955207807
MD5 57ce6ee882711831e9b2144729da1eb8
BLAKE2b-256 7eccc77e8457d535b81b2cc9a2351745971cf46c5096f11fb9179232721eb078

See more details on using hashes here.

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

Hashes for plesty_attodry2100xl_enspire_server-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b371af2129c66f5211ca50efaf86795b70ecfa9b280331295e902e77af685350
MD5 6a12b2e98c71dd81eabeafc3fcee8c1a
BLAKE2b-256 a658028ea5e6773ec9f68bc829d43446baf4865b08c36e3aaf2bb39a0fa405b8

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page