nidaqlib.manager¶
nidaqlib.manager ¶
DaqManager — multi-task lifecycle and dispatch (design doc §15).
Direct-port of sartoriuslib's manager.py, shape-translated for DAQ:
- Port-keyed locks → per-task locks plus a per-device lock for tasks that share a card (best-effort; NI is the final authority).
- Per-operation outcomes are reported as :class:
DeviceResult[T]. - The recorder consumed :class:
ErrorPolicyin v0.1; the manager becomes the second consumer here.
Lifecycle invariants (sibling parity):
- Sessions start lazily. :meth:
addconstructs a :class:DaqSessionand records the spec; :meth:startperforms the actual NI calls. - :meth:
closeunwinds in LIFO order (last added, first closed). On failure during a group operation, all errors are collected into one :class:ExceptionGrouprather than aborting on the first. - :meth:
addis idempotent on the same name + spec — a duplicateaddbumps a refcount; the matching :meth:removedecrements. Only when the refcount reaches zero is the session torn down.
DaqManager ¶
Lifecycle, dispatch, and group operations across multiple NI tasks.
Construction does not touch the driver. Add tasks via :meth:add
(lazy — no NI calls), then call :meth:start to bring them up.
:meth:close always tears down in reverse-add order.
The manager is async-context-manager-aware: async with DaqManager()
closes every session on exit, even on raised errors.
Create a manager.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
error_policy
|
ErrorPolicy
|
Default policy for group operations
(:meth: |
RAISE
|
Source code in src/nidaqlib/manager.py
add
async
¶
Register a task with this manager. Idempotent on duplicate name.
Performs a best-effort preflight conflict check against tasks already managed (design doc §15.3). NI is the final authority — the preflight only catches obvious overlaps.
add does not allocate NI resources — it constructs a
:class:DaqSession and records it. The session's
:meth:DaqSession.configure (which creates the NI task and applies
channels / timing / logging / triggers) runs lazily on the first
:meth:start for the task. Any NI rejection of the spec (bad
physical channel, unsupported channel kind, sample rate above
device max, …) therefore surfaces at :meth:start time, not
:meth:add time. The preflight catches operator-side overlap
only; everything NI validates lives downstream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Manager-side label for this task. Must be unique. |
required |
source
|
TaskSpec | DaqSession
|
Either a :class: |
required |
backend
|
DaqBackend | None
|
Optional :class: |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
The |
DaqSession
|
class: |
DaqSession
|
the same |
|
DaqSession
|
bumps a refcount. |
Raises:
| Type | Description |
|---|---|
NIDaqTaskStateError
|
|
NIDaqResourceError
|
|
Source code in src/nidaqlib/manager.py
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 | |
close
async
¶
Tear down every managed session in LIFO order. Idempotent.
Failures are collected into an :class:ExceptionGroup; one slow /
broken close does not prevent others from running.
Source code in src/nidaqlib/manager.py
get ¶
Return the session registered under name.
Raises:
| Type | Description |
|---|---|
KeyError
|
|
poll
async
¶
Poll one or more tasks once each. Returns one :class:DaqReading per task.
Source code in src/nidaqlib/manager.py
read_block
async
¶
Read one block per task in parallel.
Source code in src/nidaqlib/manager.py
remove
async
¶
Decrement refcount; tear down on the last :meth:remove.
A no-op for unknown names — matches sibling parity.
Raises:
| Type | Description |
|---|---|
NIDaqError
|
Surfaced from session close (collected into a
group when called from :meth: |
Source code in src/nidaqlib/manager.py
start
async
¶
Start one or more managed tasks. Defaults to all in add-order.
Source code in src/nidaqlib/manager.py
start_synchronized
async
¶
Arm slaves first, then start master.
Multi-task synchronisation requires strict ordering: each slave is configured against a shared sample clock or trigger and must reach the armed-and-waiting state before the master is started — once the master arms its clock or fires its trigger, the slaves react immediately. If a slave is started after the master, samples before its first edge are lost.
Slaves are armed sequentially (not concurrently): NI's
start_task returns once the task is armed, so issuing the
starts in order guarantees every slave has reached the armed state
before the master starts. This is intentionally simpler than the
parallel fan-out used by :meth:start; the difference matters
when one slave fails to arm — the master must not start at all.
On failure during slave arming, every slave that had already armed is stopped (in reverse order) before the error is raised; the master is not started.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
master
|
str
|
Manager-add name of the master task. |
required |
slaves
|
Sequence[str]
|
Manager-add names of the slave tasks. Order is respected — slaves are armed left-to-right. |
required |
error_policy
|
ErrorPolicy | None
|
Optional override; defaults to the manager's policy. |
None
|
confirm
|
bool
|
Required when any task being started can actuate hardware immediately. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
One |
Mapping[str, DeviceResult[None]]
|
class: |
Mapping[str, DeviceResult[None]]
|
entry of |
Raises:
| Type | Description |
|---|---|
KeyError
|
|
BaseExceptionGroup
|
One or more tasks failed under
:attr: |
Source code in src/nidaqlib/manager.py
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 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 | |
stop
async
¶
Stop one or more managed tasks. Defaults to all in reverse-add.
Source code in src/nidaqlib/manager.py
DeviceResult
dataclass
¶
One per-task outcome from a manager group operation.
The name matches the ecosystem manager surface even though DAQ granularity is one NI task per slot.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
Manager-add name of the task. |
value |
T | None
|
The operation's success value, or |
error |
NIDaqError | None
|
The wrapped :class: |