fujilib.sync¶
Blocking twins of the async API (design §7.3): Fuji.open, SyncAnalyzer
and its SyncRemoteCalibration, discovery, recording and sinks, and the
SyncPortal that runs the event loop in a background thread.
fujilib.sync.analyzer ¶
The blocking analyzer facade: :class:SyncAnalyzer and :meth:Fuji.open (design §7.3).
Each method is a one-line blocking call of the :class:~fujilib.devices.analyzer.Analyzer
method of the same name, with the same parameters and defaults (a parity
test holds them together)::
from fujilib.sync import Fuji
with Fuji.open("COM8", channel_map={"CH3": "o2"}) as anz:
print(anz.poll().channel("CH3"))
A remote manual calibration is a blocking context manager too
(:class:SyncRemoteCalibration), entered and left on the portal's loop.
Fuji ¶
The sync entry point: with Fuji.open(...) as anz:.
open
staticmethod
¶
open(
port,
*,
profile=ZP_PROFILE,
protocol=None,
address=1,
serial_settings=None,
timeout=DEFAULTS.request_timeout_s,
identify=True,
channel_map=None,
options=Capability.NONE,
write_warn_per_minute=DEFAULTS.write_warn_per_minute,
settle_after_reopen_s=DEFAULTS.settle_after_reopen_s,
portal=None,
)
Open an analyzer for the with block; the arguments are :func:open_device's.
Without portal the analyzer gets a portal of its own, closed with
it. A :class:~fujilib.transport.base.Transport passed as port
must belong to portal's loop.
Source code in src/fujilib/sync/analyzer.py
SyncAnalyzer ¶
A blocking view of an :class:~fujilib.devices.analyzer.Analyzer, bound to a portal.
Wrap analyzer, whose loop is portal's; :meth:Fuji.open does this.
Source code in src/fujilib/sync/analyzer.py
apply_settings ¶
apply_settings(
document,
*,
confirm=False,
any_analyzer=False,
max_tier=SafetyTier.DANGEROUS,
timeout=None,
)
Blocking :meth:Analyzer.apply_settings.
Source code in src/fujilib/sync/analyzer.py
calibration_status ¶
Blocking :meth:Analyzer.calibration_status.
channel_status ¶
Blocking :meth:Analyzer.channel_status.
close ¶
diff_settings ¶
Blocking :meth:Analyzer.diff_settings.
Source code in src/fujilib/sync/analyzer.py
identify ¶
Blocking :meth:Analyzer.identify.
Source code in src/fujilib/sync/analyzer.py
manual_calibration ¶
manual_calibration(
plan,
*,
gas,
confirm=False,
rule=None,
adc=False,
interval=0.5,
key_timeout=2.0,
run_timeout=30.0,
cleanup_timeout=30.0,
)
Blocking :meth:Analyzer.manual_calibration; use it in a with block.
Source code in src/fujilib/sync/analyzer.py
plan_auto_calibration ¶
Blocking :meth:Analyzer.plan_auto_calibration.
plan_auto_zero_calibration ¶
Blocking :meth:Analyzer.plan_auto_zero_calibration.
plan_manual_calibration ¶
Blocking :meth:Analyzer.plan_manual_calibration.
Source code in src/fujilib/sync/analyzer.py
poll ¶
read_adc ¶
read_calibration_log ¶
Blocking :meth:Analyzer.read_calibration_log.
Source code in src/fujilib/sync/analyzer.py
read_channel ¶
Blocking :meth:Analyzer.read_channel.
read_clock ¶
read_error_log ¶
read_metadata ¶
read_parameter ¶
Blocking :meth:Analyzer.read_parameter.
Source code in src/fujilib/sync/analyzer.py
read_parameters ¶
Blocking :meth:Analyzer.read_parameters.
Source code in src/fujilib/sync/analyzer.py
read_ranges ¶
read_settings ¶
Blocking :meth:Analyzer.read_settings.
Source code in src/fujilib/sync/analyzer.py
reopen ¶
reprobe ¶
return_to_measurement ¶
Blocking :meth:Analyzer.return_to_measurement.
Source code in src/fujilib/sync/analyzer.py
set_calibration_gas ¶
Blocking :meth:Analyzer.set_calibration_gas.
Source code in src/fujilib/sync/analyzer.py
set_hold_mode ¶
Blocking :meth:Analyzer.set_hold_mode.
Source code in src/fujilib/sync/analyzer.py
set_hold_value ¶
Blocking :meth:Analyzer.set_hold_value.
Source code in src/fujilib/sync/analyzer.py
set_output_hold ¶
Blocking :meth:Analyzer.set_output_hold.
Source code in src/fujilib/sync/analyzer.py
set_range ¶
Blocking :meth:Analyzer.set_range.
Source code in src/fujilib/sync/analyzer.py
set_range_method ¶
Blocking :meth:Analyzer.set_range_method.
Source code in src/fujilib/sync/analyzer.py
set_response_time ¶
Blocking :meth:Analyzer.set_response_time.
Source code in src/fujilib/sync/analyzer.py
snapshot ¶
start_auto_calibration ¶
Blocking :meth:Analyzer.start_auto_calibration.
Source code in src/fujilib/sync/analyzer.py
start_auto_zero_calibration ¶
Blocking :meth:Analyzer.start_auto_zero_calibration.
Source code in src/fujilib/sync/analyzer.py
start_blowback ¶
Blocking :meth:Analyzer.start_blowback.
status ¶
wait_for_calibration ¶
Blocking :meth:Analyzer.wait_for_calibration.
Source code in src/fujilib/sync/analyzer.py
wait_for_manual_calibration ¶
Blocking :meth:Analyzer.wait_for_manual_calibration.
Source code in src/fujilib/sync/analyzer.py
write_parameter ¶
Blocking :meth:Analyzer.write_parameter.
Source code in src/fujilib/sync/analyzer.py
SyncRemoteCalibration ¶
A blocking view of a :class:~fujilib.devices.keys.RemoteCalibration.
Its with block enters and leaves the run on the portal's loop, so the
cleanup runs there however the block is left. Every call is interruptible
(:meth:SyncPortal.call_interruptible): Ctrl-C cancels it on the loop and
waits for it to finish before the block is left, so a key it was about to
send is not sent afterwards, and Ctrl-C while entering still cleans up.
progress callbacks are called on the loop's thread.
Wrap run, whose analyzer's loop is portal's.
Source code in src/fujilib/sync/analyzer.py
fujilib.sync.discovery ¶
Blocking discovery (design §7.5).
find_devices ¶
find_devices(
*,
ports=None,
addresses=(1,),
profiles=DEVICE_PROFILES,
per_probe_timeout_s=0.3,
identify=True,
max_concurrency=8,
portal=None,
)
Blocking :func:fujilib.devices.discovery.find_devices.
Runs on portal, or on a portal of its own when portal is None.
Source code in src/fujilib/sync/discovery.py
fujilib.sync.recording ¶
Blocking recording (design §7.3, §7.6).
:func:record runs the async recorder on a portal's loop and yields a
:class:SyncRecording, iterated with a plain for loop::
from fujilib.sync import Fuji, PollSourceAdapter, SyncCsvSink, pipe, record
with (
Fuji.open("COM8", channel_map={"CH3": "o2"}) as anz,
record(PollSourceAdapter("zpa", anz), rate_hz=1.0, duration=60) as rec,
SyncCsvSink("run.csv", portal=anz.portal) as sink,
):
pipe(rec, sink)
Everything behaves as in :mod:fujilib.streaming.recorder; leaving the
with block stops the recording, and raises the error that ended it, if
one did.
PollSourceAdapter ¶
A :class:~fujilib.sync.analyzer.SyncAnalyzer as a poll source (unified API §E).
The blocking twin of :class:fujilib.streaming.poll_source.PollSourceAdapter;
it brings the analyzer's portal to :func:record.
Publish device's polls under name.
Source code in src/fujilib/sync/recording.py
SyncRecording ¶
A running recording, iterated with a plain for loop (unified API §I).
Wrap recording, which runs on portal's loop; :func:record does this.
Source code in src/fujilib/sync/recording.py
pipe ¶
Blocking :func:fujilib.sinks.base.pipe, on the recording's portal.
sink is a blocking sink or an async one, open either way. Ctrl-C
stops it: the pipe writes what it holds, then KeyboardInterrupt is
raised here.
Source code in src/fujilib/sync/recording.py
record ¶
record(
source,
*,
rate_hz,
duration=None,
names=None,
overflow=OverflowPolicy.BLOCK,
buffer_size=64,
reconnect=None,
portal=None,
)
Blocking :func:fujilib.streaming.recorder.record.
source is a blocking :class:PollSourceAdapter, whose portal is used
unless portal is given, or an async poll source with the portal
its analyzers run on.
Raises:
| Type | Description |
|---|---|
FujiValidationError
|
an async source without a |
FujiConnectionError
|
on leaving the block, when a connection failure ended the recording. |
Source code in src/fujilib/sync/recording.py
fujilib.sync.sinks ¶
Blocking sinks (design §7.3, §7.6).
Each is the async sink of the same name behind a :class:SyncSinkAdapter,
which opens, writes and closes it on a portal's loop: the portal given,
or one of its own for the with block. Pass the analyzer's portal
(anz.portal) to share one loop.
SyncCsvSink ¶
Bases: SyncSinkAdapter
Blocking :class:~fujilib.sinks.csv.CsvSink.
A sink for path; its columns are locked now if channels are given.
Source code in src/fujilib/sync/sinks.py
SyncInMemorySink ¶
Bases: SyncSinkAdapter
Blocking :class:~fujilib.sinks.memory.InMemorySink.
An empty sink; its columns are locked now if channels are given.
Source code in src/fujilib/sync/sinks.py
SyncParquetSink ¶
SyncParquetSink(
path,
*,
channels=None,
compression="zstd",
row_group_size=DEFAULT_ROW_GROUP_SIZE,
metadata=None,
portal=None,
)
Bases: SyncSinkAdapter
Blocking :class:~fujilib.sinks.parquet.ParquetSink.
A sink for path; the arguments are :class:~fujilib.sinks.parquet.ParquetSink's.
Source code in src/fujilib/sync/sinks.py
SyncSinkAdapter ¶
A blocking view of an async :class:~fujilib.sinks.base.SampleSink.
Wrap sink; without portal it gets a portal of its own when opened.
Source code in src/fujilib/sync/sinks.py
close ¶
Blocking close; a portal of the sink's own stops with it. Again is a no-op.
Raises:
| Type | Description |
|---|---|
FujiSinkError
|
the shared portal stopped before the sink was closed, so the sink could not finish (a Parquet file would be unreadable). |
Source code in src/fujilib/sync/sinks.py
open ¶
Blocking open; if it fails, a portal of the sink's own stops again.
Source code in src/fujilib/sync/sinks.py
fujilib.sync.portal ¶
The blocking portal behind the sync facade (design §7.3).
:class:SyncPortal runs an AnyIO event loop in a background thread
(:func:anyio.from_thread.start_blocking_portal) and calls coroutines on it
from ordinary code. A portal is used once: its with block starts the loop
and ends it.
A single exception raised inside a task group reaches AnyIO wrapped in an
exception group; :meth:SyncPortal.call unwraps a group of one, so callers
catch the :class:~fujilib.errors.FujiError subclass itself.
SyncPortal ¶
An event loop in a background thread, for blocking calls into the async core.
Example::
with SyncPortal() as portal:
analyzer = portal.call(open_device, "COM8")
Prepare a portal on AnyIO backend ("asyncio" or "trio").
Source code in src/fujilib/sync/portal.py
__enter__ ¶
Start the loop.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
the portal was used before. |
Source code in src/fujilib/sync/portal.py
__exit__ ¶
Stop the loop and join its thread.
Source code in src/fujilib/sync/portal.py
call ¶
Run func(*args, **kwargs) on the portal's loop and wait for the result.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
the portal is not running. |
Source code in src/fujilib/sync/portal.py
call_interruptible ¶
As :meth:call, but Ctrl-C cancels func on the loop before it is raised here.
:meth:call leaves the coroutine running on the loop when Ctrl-C
interrupts the wait for it. Here Ctrl-C cancels it there and waits for
it to finish, whatever it does on its way out, before
KeyboardInterrupt is raised. So nothing it was about to do happens
afterwards.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
the portal is not running. |
Source code in src/fujilib/sync/portal.py
run_in_loop ¶
Run the plain function func in the loop's thread and return its result.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
the portal is not running. |
start_task_soon ¶
Start func() on the portal's loop; its future cancels the task when cancelled.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
the portal is not running. |
Source code in src/fujilib/sync/portal.py
wrap_async_context_manager ¶
Present an async context manager, entered and exited on the portal's loop, as a sync one.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
the portal is not running. |