instro handles data from instruments. They define what happens to measurements and commands, whether that means sending data to Nominal Core, streaming to Nominal Connect, writing to files, or implementing custom behavior.
Every instrument can have multiple publishers attached, and data flows automatically: Instrument -> Publishers -> Destinations.
An attached publisher is an exclusive resource owned by that instrument. Closing the instrument closes its publishers. Do not attach the same ordinary publisher instance to more than one instrument. Use SharedPublisher when multiple instruments need to publish to one underlying destination.
The Power of One Line
Here’s how simple it is to go from collecting data locally to streaming it to Nominal Core: Without a publisher:NominalCorePublisher to the publishers list, every measurement and command is now recorded in your Nominal Core dataset. The InstroDAQ instance owns that publisher and closes it when daq.close() runs.
Important note about publishersData is published as a direct result of an instrument method being called.For example, when
InstroPSU.get_voltage() runs, it queries the instrument for the voltage and causes all attached publishers to publish the measurement response automatically.This makes it easy to ensure measurements and state changes are consistently recorded or streamed without explicit publishing logic in your workflow.Treat each publisher instance as owned by one instrument. To share one file, stream, client, or other sink across instruments, wrap it in SharedPublisher and attach a clone to each additional instrument.Publisher Protocol
All publishers implement a simple protocol with two required methods:close() during teardown. Implement close() to release resources owned by the publisher, such as files, clients, sockets, or background workers. A publisher should not assume it can be reused after close() has run.
Data Types
Measurement: Contains data read from the instrument (e.g., analog input readings, voltage measurements). Includes channel data, timestamps, and optional tags.Command: Represents a command sent to the instrument (e.g., setting voltage, enabling output). Includes channel data, a single timestamp, and optional tags.
Built-in Publishers
instro provides built-in publishers for sending data to Nominal’s platform and for writing to local files.
NominalCorePublisher
Sends measurement and command data to Nominal Core datasets for long-term storage, analysis, and collaboration. Constructor Parameters:The
NominalCorePublisher includes built-in batching via the Nominal Core Python SDK, which optimizes upload performance automatically.NominalConnectPublisher
Streams real-time data to Nominal Connect for live visualization and monitoring during tests that use the Nominal Connect Desktop Application. Nominal Connect apps can also installinstro for you; see Using Nominal Connect.
Constructor Parameters:
FilePublisher
Writes measurements and commands to a local file in Avro, CSV, or JSON Lines format. Useful for data capture in offline or air-gapped environments, for post-run upload to Nominal Core, and for inspecting raw data while debugging. When to use:- You’re running tests on a system without access to Nominal Core or Connect
- You want a durable local copy of raw data for post-processing or later upload
- You want to inspect measurements in a human-readable format while debugging
custom_file_name is not provided, the output file is named measurements-YYYY-MM-DD-HH-MM-SS.<format> using the time FilePublisher was constructed.
Format tradeoffs:
avro(default): Compact binary format with snappy compression. Uses the same schema as Nominal Core ingest, so captured files can be uploaded after the fact without transformation. Recommended for production captures.csv: One row per(timestamp, channel, value, tags)tuple. Good for quick inspection in spreadsheet tools.jsonl: Newline-delimited JSON, one record per publish. Recommended when you want human-readable output: each record is appended and flushed in constant time, and every flushed line is a complete record, so a crash mid-run leaves a readable file.json(deprecated): Appends each publish to a single JSON array. The entire file is re-read and rewritten on every publish call, so the cost of a capture grows quadratically (O(n^2)) with the number of records. Constructing aFilePublisherwithformat="json"emits aDeprecationWarning; usejsonlinstead, since records carry identical fields.
FilePublisher can be attached alongside a NominalCorePublisher to keep a local copy of data while streaming to the cloud:
If you only need a local file as a fallback for when network connectivity to Nominal Core is lost, use the
file_fallback parameter on NominalCorePublisher instead of attaching a separate FilePublisher.Attaching Publishers
Publisher attachment transfers ownership to the instrument. Use each publisher instance with one instrument unless the instance is aSharedPublisher handle.
During Creation (via publishers parameter)
After Creation
Core Publisher Shorthand
A shorthand method to add publishing to Nominal Core is to directly pass in adataset_rid kwarg. This requires a default profile to have been configured on the system.
Custom Publisher
To create a custom publisher, create a class that implements the publisher protocol: apublish method and a close method. The example below prints to the console every time a Measurement is published corresponding to a predefined name.
close(), and do not reuse a custom publisher instance after its owning instrument has closed.
Publisher execution model
Custom publishers face the classic buffered versus unbuffered I/O tradeoff. By default, publishing is unbuffered:publish runs synchronously, on the same thread as the instrument method that produced the data. That is deliberate. The in-memory channel buffer behind get_channel is itself a publisher, and get_channel’s blocking semantics rely on samples being in the buffer by the time each background-daemon iteration completes. If publishing were deferred to a background thread by default, a get_channel call could miss a sample the daemon just collected.
Keep custom publish implementations fast. When the sink is slow (network, disk), opt into buffered or background publishing by wrapping the publisher in a BasicBufferedPublisher or QueuedPublisher.
Publisher Wrappers
Publisher wrappers modify the behavior of other publishers by adding buffering, asynchronous processing, or explicit shared ownership. They are composable: wrap any publisher to add new capabilities.SharedPublisher
SharedPublisher coordinates shared ownership of one underlying publisher across multiple instruments. The default model is still exclusive ownership: one publisher instance belongs to one instrument. Use SharedPublisher only when multiple instruments must publish to the same file, stream, client, or other sink.
Each instrument receives its own SharedPublisher handle. Closing one instrument closes only that handle. The underlying publisher closes after the last shared handle closes.
When to use:
- Multiple instruments should write to the same local capture file
- Multiple instruments should publish to the same custom sink
- The underlying publisher owns a resource that should stay open until all instruments finish
BasicBufferedPublisher
BasicBufferedPublisher collects data in memory and publishes in batches, reducing the overhead of frequent publish calls. It is the concrete implementation of the abstract BufferedPublisher base, so instantiate BasicBufferedPublisher.
When to use:
- You’re making many small publish calls and want to reduce overhead
- You want to batch data before sending to a remote service
- Network or I/O latency is impacting performance
The buffer is automatically flushed when it reaches capacity or when
close() is called, ensuring no data is lost.QueuedPublisher
Offloads publishing to a background thread, making publish calls non-blocking and preventing slow publishers from impacting instrument operations. When to use:- Publishing is slow (network latency, disk I/O) and blocking your test loop
- You want instrument operations to proceed immediately without waiting for publish
- You need guaranteed throughput for time-critical measurements
Using
QueuedPublisher can dramatically improve test performance when publishing is a bottleneck. The background thread handles all I/O while your test continues uninterrupted.