Sessions¶
DtolSession is the central acquisition object: it owns one configured
subsystem and exposes poll() / write() / read_events() /
measure_frequency() / start() / stop() / capabilities(). Open one with
open_device; use it as an async context
manager so the subsystem is committed on entry and released on exit.
See the Async quickstart for the happy path and
Safety for the write() gate model.
open_device¶
dtollib.factory ¶
open_device factory — the canonical entry point for ad-hoc sessions.
For multi-task / multi-device coordination, use :class:DtolManager
instead.
Design reference: docs/design.md §9.3.
open_device
async
¶
Open a :class:DtolSession for spec.
The session is returned already :meth:prepared <DtolSession.prepare>
and :meth:committed <DtolSession.commit>. When autostart is
true (the default), it is also :meth:started <DtolSession.start>.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spec
|
TaskSpec
|
Task specification. |
required |
backend
|
DtolBackend | None
|
Backend to use. |
None
|
timeout
|
float
|
Default per-call timeout. |
10.0
|
autostart
|
bool
|
Whether to start the subsystem before returning.
Single-value tasks honour this as a literal pre-start;
continuous callers that need to register a notification
callback before |
True
|
confirm_start
|
bool
|
Safety gate (docs/design.md §18.1). Autostarting a
task that drives |
False
|
Returns:
| Type | Description |
|---|---|
DtolSession
|
A configured :class: |
DtolSession
|
manager so :meth: |
Raises:
| Type | Description |
|---|---|
DtolConfirmationRequiredError
|
|
Source code in src/dtollib/factory.py
DtolSession¶
dtollib.tasks.session ¶
DtolSession — async lifecycle wrapper around one HDASS.
Lifecycle: prepare → commit → start → poll → stop/abort → close.
Single-value reads go through :meth:poll; single-value writes through
:meth:write. Continuous acquisition is owned by
:func:dtollib.streaming.record, which manages the buffer pool and the
notification bridge — the session does not expose a bare block-read.
Design reference: docs/design.md §9.1, §9.2; docs/implementation-plan.md §4.4.
DtolSession ¶
Async lifecycle wrapper around one configured DT-Open Layers subsystem.
Drives single-value reads (:meth:poll) and writes (:meth:write)
end-to-end. Continuous acquisition uses the same session but is driven
by :func:dtollib.streaming.record, which owns the buffer pool and
notification bridge.
Attributes:
| Name | Type | Description |
|---|---|---|
spec |
Bound :class: |
|
backend |
Bound :class: |
|
timeout |
Default per-call timeout in seconds. |
Source code in src/dtollib/tasks/session.py
queued_buffer_dones
property
¶
olDaGetQueueSize(OL_QUE_DONE) — done-queue depth (synchronous).
state
property
¶
Reported SDK :class:SubsystemState.
Cheap read; called inside the lock-free hot path of
:meth:is_running and from error messages.
__aenter__
async
¶
__aexit__
async
¶
Best-effort close. Uses abort to avoid deadlocking on a hung trigger.
Source code in src/dtollib/tasks/session.py
abort
async
¶
close
async
¶
Tear the session down.
Default graceful=False uses :meth:abort; graceful=True
uses :meth:stop. Releases HDASS, decrements HDRVR ref-count.
Idempotent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graceful
|
bool
|
When |
False
|
Source code in src/dtollib/tasks/session.py
commit
async
¶
Run the configured-state commit.
Dispatches on spec.data_flow:
SINGLE_VALUE→ builder runs the per-channel configure + commit.CONTINUOUS/FINITE→ builder runs the pre-commit continuous configuration; the recorder (record()) drives the register → queue →commit()ordering after wiring its bridge.
Idempotent.
Source code in src/dtollib/tasks/session.py
configure
async
¶
is_running ¶
measure_frequency
async
¶
Measure input frequency (Hz) across the task's channels.
Valid for counter-frequency + tachometer tasks. Drives
olDaMeasureFrequency per channel.
Returns:
| Name | Type | Description |
|---|---|---|
One |
DaqReading
|
class: |
DaqReading
|
name to its measured frequency in hertz. |
Source code in src/dtollib/tasks/session.py
poll
async
¶
One-shot scalar read across every channel of the task.
Behaviour (docs/implementation-plan.md §3.7):
- Captures
requested_at+ monotonic ns. - Branches on
OLSSC_SUP_SIMULTANEOUS_SH: oneolDaGetSingleValues/Floatscall across all channels, or per-channel loop. - Branches on
OLSSC_RETURNS_FLOATS: skip code-to-volts if true. - For TC channels, detects sentinel floats and populates
sensor_status+ NaN-fillsvalues. - Computes
t_utcas the midpoint ofrequested_at/received_at.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
timeout
|
float | None
|
Per-call timeout in seconds. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
One |
DaqReading
|
class: |
Raises:
| Type | Description |
|---|---|
DtolTaskStateError
|
Task is not in a state that admits
|
Source code in src/dtollib/tasks/session.py
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 | |
prepare
async
¶
Allocate HDASS, validate against capabilities, configure channels.
Idempotent. Stops short of olDaConfig so the continuous
path can insert notification + Ready-queue setup between
:meth:prepare and :meth:commit. For single-value mode there
is nothing to interleave; :meth:configure is the convenience
that runs prepare then commit.
Source code in src/dtollib/tasks/session.py
read_block
async
¶
Read one buffer's worth of hardware-clocked data, synchronously.
The polled alternative to :func:dtollib.streaming.record for
CONTINUOUS / FINITE tasks. On first call it primes a buffer
pool (allocate → queue → olDaStart); each call then waits for the
next completed buffer on the SDK Done queue and returns it as a
:class:DaqBlock. No notification bridge is involved — this is a
direct olDaGetBuffer poll.
:func:record / :func:~dtollib.streaming.record_polled remain the
recommended path; read_block suits simple scripts and tests that
want one buffer at a time without an async for consumer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
samples_per_channel
|
int
|
Buffer depth in samples per channel. Fixed for the life of the pool — later calls must pass the same value. |
required |
timeout
|
float | None
|
Seconds to wait for a completed buffer. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
One |
DaqBlock
|
class: |
DaqBlock
|
count ( |
Raises:
| Type | Description |
|---|---|
DtolValidationError
|
|
DtolTaskStateError
|
|
DtolTimeoutError
|
No buffer completed within |
Source code in src/dtollib/tasks/session.py
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 | |
read_events
async
¶
Read the current counter value(s) across the task's channels.
Valid for counter/timer + quadrature tasks (event counting,
edge-to-edge interval, accumulated quadrature position). Drives
olDaReadEvents per channel and packages the counts into a
:class:DaqReading so counter rows join sibling-library samples on
(device, t_mono_ns).
Returns:
| Name | Type | Description |
|---|---|---|
One |
DaqReading
|
class: |
DaqReading
|
name to its integer count. |
Source code in src/dtollib/tasks/session.py
read_inprocess
async
¶
Drain the currently-filling buffer without waiting for completion.
The low-latency partial-buffer read for CONTINUOUS / FINITE
tasks on subsystems that advertise OLSSC_SUP_INPROCESSFLUSH —
useful on slow tasks (e.g. 200 Hz TC, 1 kHz strain) where waiting for
a full buffer is unacceptable. Backed by olDmCopyFromBuffer on the
in-process HBUF; primes the pool on first use like :meth:read_block.
The SDK transfers data in device-specific segment sizes, so the
returned block.samples_per_channel is whatever was available — not
necessarily a full buffer.
Returns:
| Name | Type | Description |
|---|---|---|
A |
DaqBlock | None
|
class: |
DaqBlock | None
|
in-process buffer holds zero valid samples. |
Raises:
| Type | Description |
|---|---|
DtolCapabilityError
|
The subsystem does not support in-process flush. |
DtolTaskStateError
|
|
Source code in src/dtollib/tasks/session.py
start
async
¶
olDaStart — transitions subsystem to RUNNING.
Source code in src/dtollib/tasks/session.py
stop
async
¶
olDaStop — orderly stop. Blocks until current buffer fills.
write
async
¶
Single-value write to AO / DO channels with the §18 safety gate.
Validation is atomic and pre-SDK (docs/design.md §18): every value is checked against its channel before any write reaches the backend, so a single bad value leaves the device untouched. The wrapper never silently clamps.
Gate model (decided 2026-05-28; confirm-gate per design §18.1):
- Unknown channel name → :class:
DtolValidationError. - Value outside the device
[min_val, max_val]→ always :class:DtolValidationError(electrically impossible;confirmdoes not override). - Value outside
[safe_min, safe_max](when set), or a channel withrequires_confirm=True, withoutconfirm=True→ :class:DtolConfirmationRequiredError.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values
|
Mapping[str, float | bool]
|
Channel |
required |
confirm
|
bool
|
Operator confirmation for safety-gated writes. |
False
|
Raises:
| Type | Description |
|---|---|
DtolTaskStateError
|
|
DtolValidationError
|
Unknown channel or out-of-device-range value. |
DtolConfirmationRequiredError
|
Safety gate tripped without confirm. |