Skip to content

v0.1.0

Choose a tag to compare

@GraysonBellamy GraysonBellamy released this 25 Apr 19:03
· 31 commits to main since this release
v0.1.0
78bd03a

alicatlib v0.1.0

First public release of alicatlib — an async-first Python library for Alicat
mass flow meters and controllers. Tested against real hardware (M-series,
GP07R100, V10 firmware) and a captured-replies fake transport.

Highlights

  • Async-first, sync-equivalent. Full anyio-based core (Device,
    FlowMeter, FlowController, PressureMeter, PressureController) with a
    parity-tested sync facade (Alicat.open, SyncDevice, SyncFlowController,
    …). CI fails if a new async method lands without its sync wrapper.
  • Bootstrap identification. open_device(...) runs VE??M*??D*
    with post-frame DCU (units) and FPF (full-scale) sweeps, so setpoint
    bounds and per-field units are known before the first user call. GP07R100
    and pre-VE firmware are supported via fallback paths.
  • Streaming with backpressure. Device.stream(...) yields a bounded,
    overflow-policy-aware iterator (DROP_OLDEST default, BLOCK,
    DROP_NEWEST). A protocol-level is_streaming latch fast-fails any
    request/response command issued on the same bus while a stream is live.
  • Acquisition + sinks. record() schedules absolute-target polling that
    never accumulates drift; pipe() drives any SampleSink. In-tree sinks:
    InMemorySink, CsvSink, JsonlSink, SqliteSink (WAL),
    ParquetSink (alicatlib[parquet]), PostgresSink (alicatlib[postgres],
    binary COPY with executemany fallback).
  • Multi-device orchestration. AlicatManager canonicalises port identity
    (POSIX realpath, Windows \\.\COM… strip), ref-counts shared buses, and
    dispatches across ports concurrently while serialising same-port work.
    Per-call error policy via ErrorPolicy.{RAISE,RETURN} + DeviceResult[T].
  • Typed safety rails. Setpoint requests are validated against
    DeviceInfo.full_scale before I/O. Destructive commands (HC, ADDR,
    NCB, totalizer resets) require confirm=True. Capability gates raise
    AlicatMissingHardwareError rather than letting the wire fail.
  • Registry from primer data. Build-time codegen produces 98 statistics,
    150 gases, and 119 units across 8 categories from a primer-verified
    codes.json; CI guards generated-file idempotency.
  • Observability. Structured DEBUG wire trace on alicatlib.protocol
    (tx / rx with {direction, raw, len}), pre-I/O INFO events on
    setpoint / LSS / LV write paths, and one capability summary per
    identification.

Install

pip install alicatlib
# Optional sinks:
pip install 'alicatlib[parquet]'
pip install 'alicatlib[postgres]'

Requires Python 3.13+. Serial transport is provided by
anyserial.

Quick start

import anyio
from alicatlib import open_device

async def main():
    async with await open_device("/dev/ttyUSB0", unit_id="A") as dev:
        frame = await dev.poll()
        print(frame.as_dict())

anyio.run(main)

Sync equivalent:

from alicatlib import Alicat

with Alicat.open("/dev/ttyUSB0", unit_id="A") as dev:
    print(dev.poll().as_dict())

Compatibility

  • Python: 3.13, 3.14
  • Platforms: Linux, macOS, Windows
  • Firmware: V5 / V8 / V10 families, plus pre-VE fallback and GP07R100

Docs

Full changelog: CHANGELOG.md

Status

Beta. Public API is stable; hardware coverage breadth is the active work.