Tools for developing and testing laboratory instrument interfaces that speak CLSI LIS01-A2 (formerly ASTM E1381, the low-level protocol with ENQ/ACK, frames and checksums) and CLSI LIS02-A2 (formerly ASTM E1394, the record format H/P/O/R/C/Q/L).
lisbench is a workbench for the people who build and integrate these interfaces. It is not middleware for clinical operation and must not be used to route patient results in production.
Status: v0.8, protocol core, command line tool, simulator, monitor, bridges, database mapper and instrument simulators for Arduino/ESP32; first instrument profile (Cepheid GeneXpert, from the public specification); conformance test suite; web UI. The Raspberry Pi package is next; see docs/spec/features.md. A spare-time project.
- LIS01-A2 framing: split records into frames of at most 240 text bytes, frame numbers modulo 8, checksum, ETB/ETX; decoding with exact error reasons.
- LIS01-A2 link state machine without I/O: establishment, contention (instrument has priority), busy NAK, retransmission (six attempts), timeouts (15 s / 30 s), receiver interrupt (EOT instead of ACK), disconnect, cancel. Every submitted message gets exactly one result, on every path.
- LIS02-A2 records: delimiters from the header, fields/repeats/components,
escapes
&F& &S& &R& &E& &Xhh&, all field names of H, P, O, R, C, Q, L, S in one table. - Character sets: Latin-1 or UTF-8 on the wire; non-ASCII as
&Xhh&(7-bit), raw 8-bit, or transliterated to ASCII with a report of dropped characters. - Validation of record order, hierarchy and sequence numbers; a builder that refuses records that do not fit instead of dropping them.
- Reassembly of records from a raw one-direction capture.
- Simulator for an instrument or a LIS over TCP (client or server) or a serial port: scenarios in YAML (send, expect, wait), automatic answers to queries, and fault injection on the wire (refuse, corrupt, drop or delay ACK, NAK, ENQ, EOT or frames). See examples/.
- Monitor between instrument and LIS (TCP or serial, or a passive tap on two
receive lines): decodes both directions, flags bad and repeated frames,
NAKs, slow replies and transfers without L record, evaluates watch
expressions on field values (
R.value > 100,O.universal_test_id.4 == TSH) and triggers on them (R.value > 400 => exec ./alarm.sh,=> mark,=> stop), saves the raw traffic and replays it offline. - Conformance test suite (
lisbench conform) for a LIS or an instrument: 14 test cases on LIS01-A2 (wrong checksum and frame number must be refused, repeated frames, six attempts, timeouts, busy wait, contention, receiver interrupt, frame format), each with a verdict pass / fail / inconclusive; an instrument profile serves as PICS. Terms after ISO/IEC 9646. - Sessions: LIS and instrument simulators as saved session files, several
at once, with an order book (specimens, tests from a shared catalogue,
item states, order-result matching, unsolicited results by policy) and
host-query or unsolicited instruments;
lisbench runor the web UI. - Web UI (
lisbench serve): monitor, captures, conformance test and simulator in the browser, live; one binary with the pages built in, made for a Raspberry Pi next to the instrument. - Bridges without decoding: serial ↔ TCP, TCP client ↔ server (role swap), and recording of one endpoint to a capture or raw file.
- Mapper from an SQLite database to messages, described in YAML: nested queries with their own scopes, bound parameters, escaping of every value, records that do not fit the hierarchy are errors, write-back after sending, output to TCP, serial or files. See examples/mapper/.
- Instruments on microcontrollers: an Arduino library and sketches for an Uno (USB serial) and an ESP32 (Wi-Fi) that send results to a LIS. See devsim/.
Requires CMake ≥ 3.25 and a C++23 compiler (GCC ≥ 13, Clang ≥ 19 or MSVC 2022). Dependencies (asio, yaml-cpp, SQLite, CLI11, nlohmann/json, Catch2) are fetched at configure time.
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir buildEvery release carries Debian packages built on Debian 13 (the base of
current Raspberry Pi OS): arm64 for a Pi 3B+ or newer with the 64-bit
system, armhf for the 32-bit system (ARMv7, so not for a Pi Zero or Pi 1),
and amd64 for a PC.
sudo apt install ./lisbench_0.7.0_arm64.deb
lisbench --version
sudo nano /etc/default/lisbench # address of the web UI, data directory
sudo systemctl enable --now lisbench # web UI on port 8080The package installs /usr/bin/lisbench, the instrument profiles under
/usr/share/lisbench/devices (so --profile cepheid-genexpert works
without a path), examples under /usr/share/doc/lisbench/examples, and a
systemd service that runs lisbench serve as user lisbench (member of
dialout for serial ports, data in /var/lib/lisbench). The service is not
enabled on install: it makes a web UI without authentication reachable on
the network, which should be a deliberate step. To build a package
yourself: cmake --build build && (cd build && cpack -G DEB).
lisbench frame FILE [--raw] records (one per line) -> frames ENQ ... EOT
lisbench deframe FILE raw capture -> records, checks every frame
lisbench decode FILE [--json] records or capture -> fields by name
lisbench validate FILE order, hierarchy, sequence numbers
lisbench sim SCENARIO.yaml run a simulator scenario
lisbench sim --endpoint URI ... the same from options (--send, --receive,
--answer-queries, --fault, --trace, --fast-timing)
lisbench monitor --a URI --b URI pass traffic through and show it decoded
(--watch, --trigger, --save, --decode, --verbose, --tap)
lisbench monitor --replay FILE the same for a saved capture
lisbench serve [--http ADDR:PORT] [--captures DIR] web UI (default 127.0.0.1:8080)
lisbench run DIR/sessions/NAME.yaml ... run session files without the UI
lisbench conform --iut ROLE --endpoint URI conformance test suite (--only, --profile, --json)
lisbench bridge --a URI --b URI join two endpoints, bytes unchanged (--serve, --save)
lisbench record --from URI --to F write what an endpoint sends to a file (--raw)
lisbench map MAPPING.yaml --db DB messages from a database (--each, --arg, --to)Endpoints: tcp://host:port, tcp-listen://:port,
serial:/dev/ttyUSB0?baud=9600&data=8&parity=none&stop=1 (serial:COM3?... on Windows).
The low-level protocol is LIS01-A2 unless the URI says otherwise:
tcp://host:port?low-level=none (records with CR, the message ends with L) or
?low-level=mllp (HL7 MLLP block); serial ports take &low-level=....
decode and validate take --charset latin1|utf8, --non-ascii ascii-hex|raw|transliterate and --strict. Example:
$ lisbench frame tests/corpus/valid_host_query.astm
<ENQ>
<STX>1H|\^&|||lisbench-sim|||||LIS||P|LIS2-A2|20261025120500<CR><ETX>1F<CR><LF>
<STX>2Q|1|^S-1002||ALL||||||||O<CR><ETX>5D<CR><LF>
<STX>3L|1|N<CR><ETX>06<CR><LF>
<EOT>Unit tests, a property test over all record lengths 0–2000, a lossy-wire
simulation (drops and bit errors, 200 seeds) for the link, an exhaustive
character test U+0000–U+00FF in every mode, randomised round-trip tests,
simulator-against-simulator tests over TCP and over a pseudo-terminal null
modem with injected faults, and libFuzzer targets in fuzz/. CI builds with GCC 13/14, Clang 19 and MSVC,
runs ASan/UBSan, clang-tidy and the fuzzers. Details in
docs/spec/tests.md.
python-astm (Python library), OpenELIS analyzer bridge (delivery into OpenELIS), ser2net (serial over TCP), Mirth Connect / Open Integration Engine (interface engines).
MIT, see LICENSE. The CLSI standards are not part of this repository; they are available from CLSI.