Skip to main content

muse-driver-framework

Shared pure-Python runtime framework for AMX MUSE Script-as-Device programs.

The package contains the transport-independent pieces shared by the Precis Driver and Meter Validation programs:

  • asynchronous TCP connection lifecycle and reconnect handling;
  • independent TCP plus UDP lifecycle supervision for dual-transport devices;
  • a fixed-peer AsyncUdpChannel for datagram devices, without discovery or retries;
  • fixed static slot -> host:port configuration with no discovery or dynamic rebinding;
  • descriptor control registry and control type inference;
  • bounded, latest-only MeterStream frame delivery;
  • bounded, latest-only XYSeriesStream delivery for complete [x, y] frames.

XYSeriesStream accepts a variable-length array of finite pairs, including an empty array. Each frame is atomic and only the newest complete frame is kept, so a slow chart cannot create a backlog or mix points from different frames.

It has no third-party runtime dependencies and supports Python 3.9+.

Each framework import belongs to one model-specific Script-as-Device runtime. The framework does not discover devices, verify model identity, or merge devices from other driver programs. A driver connects directly to the static address configured for each enabled slot. Multiple slots in one manager must represent the same device model and protocol. The manager also rejects a factory that returns different client classes for different slots; this is a local structural guard, not a cross-brand registry or device identity check.

AsyncUdpChannel is a composable transport for a device that uses UDP beside its TCP control connection. The driver creates one channel inside its own manager/event loop with a fixed peer and local bind address. It accepts and returns complete datagrams, bounds the receive queue, and drops old telemetry when a consumer is slow. It does not provide discovery, device validation, retransmission, fragmentation, ordering, or acknowledgement; those behaviors remain protocol-specific. A reliable UDP protocol must implement its framing and retry rules in the driver instead of treating a datagram as a TCP reply. receive() waits for one datagram, so best-effort telemetry readers should wrap each receive in a short asyncio.wait_for() timeout and continue sending the next sample after a timeout. Do not let a missing UDP sample block the driver's TCP command/feedback loop. The timeout is a driver liveness policy, not a framework acknowledgement or retry mechanism. After a channel-level UDP error, a driver may call restart() to clear stale datagrams and reopen the same fixed peer; this does not affect the manager's TCP connection unless the driver explicitly chooses to coordinate them.

AsyncTcpUdpDeviceClient supplies the common coordination for this pattern: TCP keeps its normal command/reconnect lifecycle while a driver-owned udp_receive_loop() is supervised independently. A UDP parser failure is reported and the UDP channel is reopened without tearing down TCP. The driver still owns HiQnet or vendor-specific subscriptions, framing, ordering, and complete-frame validation.

For UDP command replies, do not use the default latest-only telemetry queue. The driver must keep an ordered reply queue, apply its protocol sequence rules, and report queue overflow or out-of-order replies explicitly. For fragmented telemetry, the driver must reassemble and validate a complete frame before offering it to MeterStream or XYSeriesStream; the channel only drops whole datagrams and never acts as a fragment reassembler.

Runtime use in MUSE

Each Script-as-Device program declares the exact framework version in its own requirements.txt:

muse-driver-framework==1.1.4

The program imports the package, never a copied local driver_framework.py:

from muse_driver_framework import AsyncTcpUdpDeviceClient, AsyncTcpDeviceManager

MUSE must have internet access when it installs the requirements file. Keep the version exact so a framework release cannot silently change an existing driver.

Local verification

Build and install the wheel into an isolated environment before testing a driver. The driver directories should not contain a local framework copy.

To test unpublished source changes, run python3 -m unittest discover -s tests -v here, or the parent project's local-validation/test-source.sh. The framework suite includes line and length-prefixed TCP framing, dual transport failure isolation, ordered UDP reply policy, complete Meter/XY-series frame recovery, and 50 mixed-protocol runtimes repeated through start/stop. The latter uses PYTHONPATH for source testing only; MUSE still installs the pinned public version, not these local edits. Tests and builds do not publish or deploy code.

Numeric commands and configuration timers reject nonfinite values before clamping or scheduling. Realtime Meter/XY-series descriptors are read-only and cannot declare both delivery types on one parameter. Driver entry points should call manager.stop() from their normal stop/exit cleanup.

TCP devices use a bounded StreamReader limit (tcp_read_limit_bytes, default 65536). This protects line-oriented drivers from an unbounded malformed frame; binary or length-prefixed protocols must override read_message() and perform their own complete-frame validation. A read-limit error is a connection error, so the normal driver reconnect lifecycle handles it.

Connection stability options are per device and optional. Existing configs keep the historical fixed reconnect delay by default. A driver may add connect_timeout_seconds, reconnect_backoff_factor, reconnect_max_delay_seconds, and reconnect_jitter_seconds to reduce a reconnect storm without changing its static slot mapping. The manager exposes runtime_stats() for low-rate health reporting; it is not a discovery API.

The TCP manager owns one event loop per model-specific driver runtime. A UDP channel used by that driver must be created, opened, consumed, and closed on the same loop. Independent driver programs do not share channels or lifecycle state, even when their static addresses belong to the same LAN.

Performance and ownership

The control registry compiles path, protocol-channel and feedback-route indexes once at construction. Treat the descriptor and registered controls as a startup snapshot; create a new registry after changing their mapping. Lookup results do not expose mutable internal sets or path lists.

Continuous commands are also coalesced by device/key before crossing into the TCP event loop, so a burst does not create one scheduled task per intermediate value. Pending keys per device are bounded by command_queue_size; stopping discards them. FIFO commands are not coalesced, and new continuous keys retain their scheduling positions relative to FIFO requests.

Meter and XY-series caches own their accepted frames. snapshot() and take_frame() capture a complete frame under the producer lock, then copy it outside the lock; callers still receive independent mutable arrays. This reduces producer contention without removing validation or changing the wire format.

run(publish) includes copying and synchronous/asynchronous publishing time in the rate-limit period, rather than sleeping an extra full period after publishing. Slow callbacks still reduce achieved throughput. Missed periods are not replayed, and idle streams wait instead of spinning. These are limits, not a promise that every configuration or MUSE host sustains the declared rate.

The parent project's local-validation/benchmark-driver.py measures source CPU costs; local-validation/stress-driver.py tests 50 loopback TCP processors and multiple complete telemetry streams with a mock SDK. Neither tests actual MUSE SDK transport or physical devices. Unpublished source changes require a new framework release and updated pinned driver requirements before deployment.

Metadata

Release files for muse-driver-framework 1.1.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for muse-driver-framework 1.1.4
File Size Uploaded
muse_driver_framework-1.1.4.tar.gz 45.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for muse-driver-framework 1.1.4
File Interpreter ABI Platform
muse_driver_framework-1.1.4-py3-none-any.whl Python 3 none any Details

Total release size: 76.3 kB

Release files / muse_driver_framework-1.1.4.tar.gz

Download URL muse_driver_framework-1.1.4.tar.gz
Size 45.9 kB
Tags Source
SHA-256 checksum
How to use checksums
b1068ff882d7931e6b57c96632cc1a258759c587fbcafc810f614cc938693b80
BLAKE2b-256 checksum
How to use checksums
2800d9315c8967527d614f8dec740904ce99a1f68c0429077927e3a475437a2f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release files / muse_driver_framework-1.1.4-py3-none-any.whl

Download URL muse_driver_framework-1.1.4-py3-none-any.whl
Size 30.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
121d868cdaa6f4e04aaed501d30b84c1cb7d11ec025c5de5b6129aef488a6016
BLAKE2b-256 checksum
How to use checksums
b827614fe2c48d2b6492ff2b8d230b484b42be6e09ce312fe57d0c606bbe227c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.6

Release history Release notifications | RSS feed

This release

1.1.4 This release

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release 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