Skip to main content

Sequence API Reference

This page lists every method of a command sequence in the three SDKs: the builder, each step, the report and the errors. To learn what a sequence does and how to plan one, read Command sequences first.

Creating a sequence​

A sequence is built with tester.sequence() and sent with run(). Appending a step sends nothing. Every step method returns a new sequence with the step appended, so a sequence can be extended into branches or run several times.

Steps are grouped by namespace. The path of a method follows the step name:

NamespaceNodeJS and PythonRust
GPIOseq.gpio.read(...)seq.gpio().read(...)
PWMseq.gpio.pwm.output(...)seq.gpio().pwm().output(...)
Relaysseq.relays.close(...)seq.relays().close(...)
DUT supplyseq.power.dut.enable(...)seq.power().dut().enable(...)
USB port powerseq.power.usb.enable(...)seq.power().usb().enable(...)
Relay 0 currentseq.power.relay.read(...)seq.power().relay().read(...)
Screenseq.hmi.screen.print(...)seq.hmi().screen().print(...)
Speakerseq.hmi.audio.play(...)seq.hmi().audio().play(...)
USB devicesseq.com.usb.wait(...)seq.com().usb().wait(...)

Conventions in every SDK:

  • Units: voltages in volts, currents in amperes, frequencies in hertz. Duty cycle, progress and volume are in percent, from 0 to 100.
  • Durations: seconds in NodeJS (number) and Python (float), Duration in Rust.
  • Options: an options object in NodeJS, keyword arguments in Python, and a struct or None in Rust.

Python has the same builder in ikalogic_at1000 and in ikalogic_at1000.aio. Only run() differs: with ikalogic_at1000.aio, write report = await tester.sequence()....run().

Pin, relay, USB port and DUT supply numbers are checked against what the device has before anything is sent: when the step is appended in NodeJS and Python, and by run() in Rust. The device checks the whole sequence again before it runs the first step: see Errors.


sequence()​

Starts an empty sequence on an open device.

Signatures:

  • NodeJS: tester.sequence(): Sequence
  • Python: tester.sequence() -> Sequence
  • Rust: tester.sequence() -> Sequence
const seq = tester.sequence();

Returns: Sequence / Sequence / Sequence - An empty sequence. In Rust, len() and is_empty() tell how many steps it holds. The group methods (gpio(), relays(), power(), hmi(), com()) take the sequence and return it, with the next step added, so you call the group method again before each step.


delay(duration)​

Appends a pause. The device keeps every resource the sequence uses during the pause.

Signatures:

  • NodeJS: seq.delay(duration: number): Sequence
  • Python: seq.delay(duration: float) -> Sequence
  • Rust: seq.delay(duration: Duration) -> Sequence
const report = await tester.sequence()
.gpio.configure(0, { mode: 'digital', direction: 'output', value: false, vol: 0, voh: 3.3, vil: 0.8, vih: 2 })
.gpio.write(0, true)
.delay(0.01) // 10 ms, timed on the device
.gpio.write(0, false)
.run();

Parameters:

  • duration (number / float / Duration): Length of the pause, finite and greater than zero

In the report: elapsed only.

Exceptions:

  • Throws a validation error if duration is not finite and greater than zero. In Rust, a zero Duration makes run() return At1000Error::InvalidInput
  • SEQUENCE_TIMED_OUT if the pause would end after the sequence's time limit

as(name) / as_(name) / named(name)​

Names the step just appended, so its result can be read by name in the report. The method is as() in NodeJS, as_() in Python (as is a Python keyword) and named() in Rust. Names stay in the SDK: they are not sent to the device.

Signatures:

  • NodeJS: seq.as(name: string): Sequence
  • Python: seq.as_(name: str) -> Sequence
  • Rust: seq.named(name: impl Into<String>) -> Sequence
const report = await tester.sequence()
.gpio.read(0).as('initial')
.delay(0.01).as('pause')
.run();

console.log(report.named.initial.value, report.named.pause.elapsed);

Parameters:

  • name (string / str / impl Into<String>): Exact, case-sensitive name, unique within the sequence

Returns: Sequence / Sequence / Sequence - The same steps, with the last one named.

Exceptions:

  • Throws an error (ValueError in Python) if the name is empty, if it is already used in this sequence, if the last step already has a name, or if no step has been appended yet. In NodeJS, TypeScript also rejects a name already used, at compile time
  • In Rust, the name is not recorded and run() returns At1000Error::InvalidInput for parameter name

See Reading the results and Sequence report.


run(options?)​

Sends every step in one request and waits for the report. The only method that talks to the device.

Signatures:

  • NodeJS: seq.run(options?: { timeout?: number }): Promise<SequenceReport>
  • Python: seq.run(timeout: float = 30.0) -> SequenceReport; with ikalogic_at1000.aio, async def run(timeout: float = 30.0) -> SequenceReport
  • Rust: seq.run(&self, timeout: impl Into<Option<Duration>>) -> Result<SequenceReport, At1000Error>
const report = await tester.sequence()
.gpio.read([0, 1, 2]).as('scan')
.run({ timeout: 10 });

console.log(report.elapsed, report.results.length);

Parameters:

  • timeout (number / float / Duration, optional): Time limit of the whole sequence, 30 s when omitted (None in Rust). It must be finite and greater than zero. It starts when the device receives the request, so time spent waiting for another sequence to finish counts against it. Each wait's own timeout is cut to the time left. The SDK gives the HTTP request 2 s more than timeout, so that the device, not the network connection, reports a sequence that takes too long

Returns: Promise<SequenceReport> / SequenceReport / Result<SequenceReport, At1000Error> - One result per step, in order. See Sequence report.

Exceptions:

  • Throws an error if no step has been appended, or if timeout is not finite and greater than zero. In NodeJS this error is thrown by run() itself, not by the returned promise
  • In Rust, also returns At1000Error::InvalidInput for the first invalid step appended. Nothing is sent in that case
  • A run that does not complete returns no report. It fails with the error of the step that stopped it: see Errors and When a sequence fails

Bounds and options​

Several steps share the same option shapes.

Bounds describe an inclusive range with a minimum, a maximum or both. Both are finite numbers, and min must not be above max.

  • NodeJS: SequenceBounds, an object { min?: number; max?: number } with at least one of the two
  • Python: SequenceBounds(min=..., max=...), or a (min, max) tuple with None for no limit on that side: (None, 0.5) means "at most 0.5"
  • Rust: Bounds::AtLeast(v), Bounds::AtMost(v) or Bounds::Between { min, max }, also built from the ranges min.., ..=max and min..=max

Repeated reads (gpio.read, power.dut.read, power.usb.read, power.relay.read):

  • repeat (number / int): Total number of readings, an integer of at least 1, and 1 when omitted
  • delay (number / float): Pause in seconds between two readings, from the end of one to the start of the next. Only allowed when repeat is 2 or more. There is no pause before the first reading or after the last one
  • Rust: ReadRepeat { count: u32, delay: Option<Duration> } in the repeat field of the step's options. A count of 0 is invalid
  • expect is checked on every reading. A reading outside it stops the run with SEQUENCE_ASSERTION_FAILED

Waits (gpio.wait_voltage, gpio.wait_level, power.*.wait, com.usb.wait):

  • timeout (number / float, optional): Longest time this wait may take, greater than zero. It is cut to the time left in the sequence's time limit. Without it, the wait may use all the time left
  • stable_for (number / float, optional): Time the condition must keep holding before the wait completes, greater than zero. A sample that fails the condition starts the window again. Omitted, the first sample that meets the condition completes the wait
  • Rust: WaitOptions { timeout: Option<Duration>, stable_for: Option<Duration> }, or None
  • A wait whose condition already holds completes at once. A wait that runs out of time stops the run with SEQUENCE_TIMED_OUT. See Waiting and checking

GPIO steps​

GPIO steps use pins 0-31. A pin keeps the configuration it had before the run until a gpio.configure or gpio.reset step changes it, and the device checks each step against the configuration the pin will have at that point. See the GPIO API Reference for pin modes and levels.

gpio.read(io, options?)​

Reads one pin, or several pins in the order given. A digital pin reads a level (true or false) from its vil and vih thresholds. An analog pin reads a voltage.

Signatures:

  • NodeJS: seq.gpio.read(io: number | readonly number[], options?: SequenceReadOptions): Sequence
  • Python: seq.gpio.read(io: int | Iterable[int], *, repeat: Optional[int] = None, delay: Optional[float] = None, expect: Optional[Union[bool, Bounds]] = None) -> Sequence
  • Rust: seq.gpio().read(pins: impl Into<SequenceIds>, options: impl Into<Option<ReadOptions>>) -> Sequence
const report = await tester.sequence()
.gpio.configure(1, { mode: 'digital', direction: 'input', vil: 0.8, vih: 2 }) // a level needs a digital pin
.gpio.read(0) // one pin, one reading
.gpio.read([0, 1, 2]).as('scan') // three pins, in the order given
.gpio.read(0, { repeat: 4, delay: 0.001 }).as('burst') // four readings, 1 ms apart
.gpio.read(1, { expect: true }).as('check') // every reading must be high
.run();

Parameters:

  • io (number | number[] / int | list[int] / impl Into<SequenceIds>): One pin, or distinct pins read in the order given. Rust takes a usize, a Vec<usize>, a &[usize] or a [usize; N]
  • options (SequenceReadOptions / keyword arguments / ReadOptions, optional):
    • repeat, delay: See Bounds and options. With several pins, delay is the pause between two passes over the whole list. There is no pause between the pins of one pass. In Rust, both are in ReadOptions::repeat
    • expect (boolean | { min, max } / bool | Bounds / Option<SequenceExpect>): Checked on every reading. A boolean is a level and needs digital pins. Voltage bounds need analog pins and both min and max, each in [-25, 25] V. Rust's SequenceExpect is Level(bool) or Range { min, max }, built from a bool or a min..=max range

In the report: io, as given. One pin read once gives its reading in value. A pin list, or repeat above 1, gives samples: one { io, value, elapsed } entry per pin and reading, pass after pass.

Exceptions:

  • Throws a validation error if a pin is outside 0-31, the list is empty or lists a pin twice, repeat is below 1, delay comes with a repeat below 2, or voltage bounds are outside [-25, 25] V or have min above max
  • 400 INVALID_VALUE if expect does not match the pin's mode: a boolean on an analog pin, or bounds on a digital pin
  • SEQUENCE_ASSERTION_FAILED if a reading does not satisfy expect: Command 2 (`gpio.read`): pin 1 read false, which does not satisfy `expect`

gpio.write(io, value)​

Writes one pin, or several pins in the order given. Each value is read through its own pin's mode, so one step may set a level on one pin and a voltage on another.

Signatures:

  • NodeJS: seq.gpio.write(io: number | readonly number[], value: SequenceValue | readonly SequenceValue[]): Sequence
  • Python: seq.gpio.write(io: int | Iterable[int], value: Union[bool, float, Sequence[Union[bool, float]]]) -> Sequence
  • Rust: seq.gpio().write(pins: impl Into<SequenceIds>, value: impl Into<SequenceValues>) -> Sequence
const report = await tester.sequence()
.gpio.configure(0, { mode: 'digital', direction: 'output', value: false, vol: 0, voh: 3.3, vil: 0.8, vih: 2 })
.gpio.configure(1, { mode: 'digital', direction: 'output', value: false, vol: 0, voh: 3.3, vil: 0.8, vih: 2 })
.gpio.configure(2, { mode: 'digital', direction: 'output', value: false, vol: 0, voh: 3.3, vil: 0.8, vih: 2 })
.gpio.configure(3, { mode: 'analog', direction: 'output', value: 0 })
.gpio.write(0, true) // one pin
.gpio.write([0, 1, 2], false) // the same value on every pin
.gpio.write([0, 1, 3], [true, false, 2.5]) // one value per pin
.run();

Parameters:

  • io (number | number[] / int | list[int] / impl Into<SequenceIds>): One pin, or distinct pins written in the order given
  • value (boolean | number | (boolean | number)[] / bool | float | list / impl Into<SequenceValues>): One value for every listed pin, or a list with exactly one value per pin. On a digital pin, a number is a level: 0 is low, anything else is high. On an analog pin, a number is a voltage in [0, 24] V. Rust takes a bool, an f64 or a GpioValue, or an array or Vec of them; mixing levels and voltages takes GpioValue

In the report: io, as given. One pin gives the value applied in value. Several pins give one { io, value, elapsed } entry per pin in samples, in the order they were driven.

Exceptions:

  • Throws a validation error if a pin is outside 0-31, the list is empty or lists a pin twice, a value is not finite, or a list of values is not as long as the pin list
  • 400 INVALID_CONFIGURATION if a pin is an input at that point of the sequence: cannot write to pin 0, configured as input
  • 400 INVALID_VALUE for a boolean on an analog pin; 400 OUT_OF_RANGE_VALUE for a voltage outside [0, 24] V
  • 409 CONFIGURATION_CONFLICT if a bus or PWM uses the pin

gpio.wait_voltage(io, options)​

Waits until an analog pin reads inside voltage bounds. In Rust, the bounds are a separate argument.

Signatures:

  • NodeJS: seq.gpio.wait_voltage(io: number, options: SequenceWaitVoltageOptions): Sequence
  • Python: seq.gpio.wait_voltage(io: int, *, min: Optional[float] = None, max: Optional[float] = None, timeout: Optional[float] = None, stable_for: Optional[float] = None) -> Sequence
  • Rust: seq.gpio().wait_voltage(io: usize, band: impl Into<Bounds>, options: impl Into<Option<WaitOptions>>) -> Sequence
const report = await tester.sequence()
.gpio.wait_voltage(3, { min: 2.0, max: 3.0, timeout: 0.5 }).as('band')
.run();

Parameters:

  • io (number / int / usize): One analog pin
  • min, max (number / float): Lowest and highest accepted voltage, each in [-25, 25] V, at least one. min alone waits for the pin to read at or above it, max alone at or below it
  • band (Rust only, impl Into<Bounds>): Replaces min and max: 2.0.., ..=3.0, 2.0..=3.0 or a Bounds
  • timeout, stable_for: See Bounds and options

In the report: io and, in value, the last voltage read (Rust: voltage).

Exceptions:

  • Throws a validation error if neither min nor max is given, min is above max, an edge is outside [-25, 25] V, or timeout or stable_for is not greater than zero
  • 400 INVALID_CONFIGURATION if the pin is not analog at that point: pin 3 must be in `analog` mode. Use gpio.wait_level on a digital pin
  • SEQUENCE_TIMED_OUT if the pin is not inside the bounds in time: pin 3 still reads 1.2000 V
note

The pin is read from polled ADC samples, so a change shorter than the time between two samples can be missed.


gpio.wait_level(io, level, options?)​

Waits until a digital pin reads a level. The level comes from the pin's vil and vih thresholds.

Signatures:

  • NodeJS: seq.gpio.wait_level(io: number, level: 'low' | 'high' | boolean, options?: SequenceWaitOptions): Sequence
  • Python: seq.gpio.wait_level(io: int, level: Union[bool, SequenceLevel, Literal["low", "high"]], *, timeout: Optional[float] = None, stable_for: Optional[float] = None) -> Sequence
  • Rust: seq.gpio().wait_level(io: usize, level: impl Into<SequenceLevel>, options: impl Into<Option<WaitOptions>>) -> Sequence
const report = await tester.sequence()
.gpio.configure(0, { mode: 'digital', direction: 'input', vil: 0.8, vih: 2 })
.gpio.wait_level(0, 'high', { stable_for: 0.02, timeout: 0.5 }).as('ready')
.run();

Parameters:

  • io (number / int / usize): One digital pin
  • level ('low' | 'high' | boolean / "low" | "high" | bool | SequenceLevel / impl Into<SequenceLevel>): Level to wait for; true is high. Rust takes a bool or SequenceLevel::Low / SequenceLevel::High
  • options (SequenceWaitOptions / keyword arguments / WaitOptions, optional): timeout and stable_for, see Bounds and options

In the report: io and, in value, the last level read (Rust: level).

Exceptions:

  • Throws a validation error if the pin is outside 0-31, or timeout or stable_for is not greater than zero
  • 400 INVALID_CONFIGURATION if the pin is not digital at that point: pin 0 must be in `digital` mode. Use gpio.wait_voltage on an analog pin
  • SEQUENCE_TIMED_OUT if the level is not reached in time: pin 0 still reads false

gpio.hold() / gpio.release()​

gpio.hold() holds the outputs: later writes are stored and the pins keep their levels. gpio.release() applies every stored write at once. They work like tester.gpio.hold() and tester.gpio.release(): see Output synchronization.

Signatures:

  • NodeJS: seq.gpio.hold(): Sequence, seq.gpio.release(): Sequence
  • Python: seq.gpio.hold() -> Sequence, seq.gpio.release() -> Sequence
  • Rust: seq.gpio().hold() -> Sequence, seq.gpio().release() -> Sequence
const report = await tester.sequence()
.gpio.configure(0, { mode: 'digital', direction: 'output', value: false, vol: 0, voh: 3.3, vil: 0.8, vih: 2 })
.gpio.configure(1, { mode: 'digital', direction: 'output', value: false, vol: 0, voh: 3.3, vil: 0.8, vih: 2 })
.gpio.configure(2, { mode: 'digital', direction: 'output', value: false, vol: 0, voh: 3.3, vil: 0.8, vih: 2 })
.gpio.hold() // hold the outputs
.gpio.write([0, 1, 2], [true, true, false])
.gpio.release() // apply the three writes together
.run();

Parameters: None

In the report: elapsed only.

caution

If the run stops between gpio.hold() and gpio.release(), the outputs stay held. Call tester.gpio.release() or tester.reset() after a failure.


gpio.configure(io, config)​

Changes a pin's configuration, like the pin's configure methods. The fields given replace the current ones; the others keep their values. The device checks the later steps against the configuration this step sets.

Signatures:

  • NodeJS: seq.gpio.configure(io: number, config: SequenceConfigureOptions): Sequence
  • Python: seq.gpio.configure(io: int, *, mode: Optional[Literal["analog", "digital"]] = None, direction: Optional[Literal["input", "output", "open_drain"]] = None, value: Optional[Union[bool, float]] = None, vil: Optional[float] = None, vih: Optional[float] = None, vol: Optional[float] = None, voh: Optional[float] = None) -> Sequence
  • Rust: seq.gpio().configure(io: usize, config: impl Into<GpioConfigPatch>) -> Sequence
const report = await tester.sequence()
.gpio.configure(0, {
mode: 'digital',
direction: 'output',
value: false,
vol: 0,
voh: 5,
vil: 1,
vih: 4,
})
.gpio.configure(3, { mode: 'analog', direction: 'output', value: 2.5 })
.run();

Parameters:

  • io (number / int / usize): One pin

  • config (SequenceConfigureOptions / keyword arguments / GpioConfigPatch): At least one of:

    • mode: 'digital' or 'analog'
    • direction: 'input', 'output' or 'open_drain'
    • value: A level, or a voltage in [0, 24] V for an analog output
    • vil, vih: Input thresholds, in [-25, 25] V
    • vol, voh: Output levels of a digital output, in [0, 24] V, with voh above vol

    Rust also accepts a complete GpioConfiguration

In the report: io and, in config, the configuration the device applied (a GpioConfiguration).

Exceptions:

  • Throws a validation error if the pin is outside 0-31, no field is given, or a field is outside its range
  • 400 validation errors of the device for an invalid combination, as for the pin's configure methods
  • 409 CONFIGURATION_CONFLICT if a bus or PWM uses the pin: Command 1 (`gpio.configure`): IO 18 is used by PWM

gpio.reset(io?)​

Resets one pin to an analog input with value 0. Without io, resets every pin like tester.gpio.reset(): it also releases the output hold and disables PWM on IO 17, 18, 21 and 22.

Signatures:

  • NodeJS: seq.gpio.reset(io?: number): Sequence
  • Python: seq.gpio.reset(io: Optional[int] = None) -> Sequence
  • Rust: seq.gpio().reset(io: impl Into<Option<usize>>) -> Sequence
const report = await tester.sequence()
.gpio.reset(3) // pin 3 only
.gpio.reset() // every pin, the output hold and PWM
.run();

Parameters:

  • io (number / int / usize, optional): The pin to reset. Omit it (None in Rust) to reset every pin

In the report: io when one pin was reset; nothing else.

Exceptions:

  • Throws a validation error if the pin is outside 0-31
  • 409 CONFIGURATION_CONFLICT if a bus or PWM uses the pin: Command 1 (`gpio.reset`): IO 16 is used by I2C_0

PWM steps​

PWM pins have fixed roles: IO 18 and 22 generate a square wave, IO 17 and 21 measure one. A PWM pin must be an analog input, and no bus may use its channel. IO 17 and 18 share one channel and IO 21 and 22 share the other, so only one pin of each pair can use PWM at a time. To measure an AT1000 PWM output on its own PWM input (a loopback), wire IO 18 to IO 21, or IO 22 to IO 17. These steps follow the rules of the PwmIO class; PWM measurement and generation explains them.

gpio.pwm.output(io, options)​

Starts a square wave of about 50 % duty on IO 18 or 22. The device generates the closest frequency it can.

Signatures:

  • NodeJS: seq.gpio.pwm.output(io: number, options: PwmOutputOptions): Sequence
  • Python: seq.gpio.pwm.output(io: int, *, frequency: float, vcc: Optional[float] = None, duration: Optional[float] = None) -> Sequence
  • Rust: seq.gpio().pwm().output(io: usize, config: PwmOutputConfig) -> Sequence
const report = await tester.sequence()
.gpio.pwm.output(18, { frequency: 10000, vcc: 3.3, duration: 1 }).as('clock')
.run();

console.log(report.named.clock.config.frequency);

Parameters:

  • io (number / int / usize): IO 18 or 22
  • options (PwmOutputOptions / keyword arguments / PwmOutputConfig):
    • frequency (number / float / f64): Requested frequency, from about 91.56 Hz (48 MHz / 524289) to 7.5 MHz
    • vcc (number / float / Option<f64>, optional): Logic level, 1.8-3.3 V. Unchanged when omitted
    • duration (number / float / Option<Duration>, optional): Run time, above 0 and up to 86400 s (24 h). Omitted, the output runs until it is disabled

In the report: io and, in config, the PWM configuration applied, with the frequency actually generated (a PwmConfig).

Exceptions:

  • Throws a validation error if the pin is not IO 18 or 22, or frequency, vcc or duration is out of range
  • 409 CONFIGURATION_CONFLICT if the pin is not an analog input or a bus uses its channel: Command 2 (`gpio.pwm.output`): Cannot enable PWM on IO 22 while IO 22 is configured as a GPIO, or if PWM is enabled on the other pin of its pair (IO 17 for IO 18, IO 21 for IO 22)
caution

An output started without duration keeps running after the sequence ends, and after a failed run. Stop it with gpio.pwm.disable, gpio.reset() or tester.reset().


gpio.pwm.input(io, options?)​

Enables PWM measurement on IO 17 or 21.

Signatures:

  • NodeJS: seq.gpio.pwm.input(io: number, options?: PwmInputOptions): Sequence
  • Python: seq.gpio.pwm.input(io: int, *, vcc: Optional[float] = None) -> Sequence
  • Rust: seq.gpio().pwm().input(io: usize, config: impl Into<Option<PwmInputConfig>>) -> Sequence
const report = await tester.sequence()
.gpio.pwm.input(17, { vcc: 3.3 })
.run();

Parameters:

  • io (number / int / usize): IO 17 or 21
  • vcc (number / float / Option<f64>, optional): Logic level, 1.8-3.3 V. Unchanged when omitted. In Rust, pass None for no options

In the report: io and the PWM configuration applied in config.

Exceptions:

  • Throws a validation error if the pin is not IO 17 or 21, or vcc is out of range
  • 409 CONFIGURATION_CONFLICT if the pin is not an analog input or a bus uses its channel, or if PWM is enabled on the other pin of its pair (IO 18 for IO 17, IO 22 for IO 21): Command 1 (`gpio.pwm.input`): Cannot enable PWM on IO 17 while PWM on IO 18 is enabled

gpio.pwm.disable(io)​

Disables PWM on IO 17, 18, 21 or 22. A running output stops.

Signatures:

  • NodeJS: seq.gpio.pwm.disable(io: number): Sequence
  • Python: seq.gpio.pwm.disable(io: int) -> Sequence
  • Rust: seq.gpio().pwm().disable(io: usize) -> Sequence
const report = await tester.sequence()
.gpio.pwm.disable(18)
.run();

Parameters:

  • io (number / int / usize): IO 17, 18, 21 or 22

In the report: io and the PWM configuration applied in config.

Exceptions:

  • Throws a validation error if the pin is not IO 17, 18, 21 or 22

gpio.pwm.measure(io, options?)​

Captures the signal on IO 17 or 21, like PwmIO.measure(). Enable measurement first with gpio.pwm.input, in the sequence or before it. In the example, IO 18 is wired to IO 21.

Signatures:

  • NodeJS: seq.gpio.pwm.measure(io: number, options?: SequencePwmMeasureOptions): Sequence
  • Python: seq.gpio.pwm.measure(io: int, *, timeout: Optional[float] = None, expect: Optional[Union[SequencePwmExpectation, Mapping[str, Any]]] = None) -> Sequence
  • Rust: seq.gpio().pwm().measure(io: usize, options: impl Into<Option<PwmMeasureOptions>>) -> Sequence
const report = await tester.sequence()
.gpio.pwm.output(18, { frequency: 10000, vcc: 3.3 })
.gpio.pwm.input(21, { vcc: 3.3 })
.gpio.pwm.measure(21, { timeout: 1, expect: { frequency: { min: 9900, max: 10100 } } })
.as('clock')
.gpio.pwm.disable(18)
.gpio.pwm.disable(21)
.run();

console.log(report.named.clock.measurement);

Parameters:

  • io (number / int / usize): IO 17 or 21
  • options (SequencePwmMeasureOptions / keyword arguments / PwmMeasureOptions, optional):
    • timeout (number / float / Option<Duration>): Capture time limit, 0.1-60 s, 5 s when omitted. It is cut to the time left in the sequence's time limit
    • expect ({ frequency?, duty_cycle? } / SequencePwmExpectation or a mapping / Option<PwmExpect>): Bounds on the frequency in Hz and the duty cycle in percent, at least one

In the report: io and, in measurement, the capture (a PwmMeasurement). Its status is ok with the statistics, or timeout when no full cycle was captured.

Exceptions:

  • Throws a validation error if the pin is not IO 17 or 21, timeout is outside 0.1-60 s, or expect has no bounds
  • SEQUENCE_ASSERTION_FAILED with expect, if no full cycle was captured or a value is outside its bounds: IO 17 measured 9500.000 Hz, which does not satisfy `expect.frequency`. Without expect, a timeout status is reported normally
  • SEQUENCE_TIMED_OUT if the time left in the sequence is too short for the capture and no full cycle was captured, or less than 0.1 s is left

Relay steps​

relays.* steps switch the relays, 0-7. To measure the current through relay 0, use power.relay.read and power.relay.wait. See Relays.

relays.open(relay?) / relays.close(relay?)​

Opens or closes one relay, several relays in the order given, or every relay when relay is omitted.

Signatures:

  • NodeJS: seq.relays.open(relay?: number | readonly number[]): Sequence, seq.relays.close(relay?: number | readonly number[]): Sequence
  • Python: seq.relays.open(relay: Optional[Union[int, Iterable[int]]] = None) -> Sequence, seq.relays.close(relay: Optional[Union[int, Iterable[int]]] = None) -> Sequence
  • Rust: seq.relays().open(relays: impl Into<RelaySelection>) -> Sequence, seq.relays().close(relays: impl Into<RelaySelection>) -> Sequence
const report = await tester.sequence()
.relays.close([0, 1]).as('routed')
.delay(0.1)
.relays.open() // every relay
.run();

Parameters:

  • relay (number | number[] / int | list[int] / impl Into<RelaySelection>, optional): One relay, or distinct relays switched in the order given. Omit it for every relay. Rust takes a relay number, a list, or RelaySelection::All

In the report: relay, as given; absent when every relay was switched.

Exceptions:

  • Throws a validation error if a relay is outside 0-7, or the list is empty or lists a relay twice

relays.reset()​

Opens every relay.

Signatures:

  • NodeJS: seq.relays.reset(): Sequence
  • Python: seq.relays.reset() -> Sequence
  • Rust: seq.relays().reset() -> Sequence

Parameters: None

In the report: elapsed only.


Power steps​

power.dut.* drives the DUT supply (number 0), power.usb.* the power of USB ports 0 and 1, and power.relay.* reads the current monitor of relay 0. Reads in a sequence always take a fresh measurement. See the Power API Reference.

power.reset()​

Turns the DUT supply off and cuts both USB ports, like tester.power.reset().

Signatures:

  • NodeJS: seq.power.reset(): Sequence
  • Python: seq.power.reset() -> Sequence
  • Rust: seq.power().reset() -> Sequence

Parameters: None

In the report: elapsed only.


power.dut.enable(dut, target_voltage)​

Turns the DUT supply on at a target voltage.

Signatures:

  • NodeJS: seq.power.dut.enable(dut: number, target_voltage: number): Sequence
  • Python: seq.power.dut.enable(dut: int, target_voltage: float) -> Sequence
  • Rust: seq.power().dut().enable(dut: usize, target_voltage: f64) -> Sequence
const report = await tester.sequence()
.power.dut.enable(0, 5)
.power.dut.wait(0, { voltage: { min: 4.8, max: 5.2 }, timeout: 2 })
.run();

Parameters:

  • dut (number / int / usize): DUT supply number, 0
  • target_voltage (number / float / f64): 1.6-13 V, or 24 V. Voltages between 13 and 24 V are not available

In the report: dut.

Exceptions:

  • Throws a validation error if dut is not 0, or target_voltage is outside 1.6-13 V and is not 24 V
  • 400 OUT_OF_RANGE_VALUE from the device for the same cases

power.dut.disable(dut) / power.dut.reset(dut)​

disable turns the DUT supply off. reset turns it off and restores its default target voltage.

Signatures:

  • NodeJS: seq.power.dut.disable(dut: number): Sequence, seq.power.dut.reset(dut: number): Sequence
  • Python: seq.power.dut.disable(dut: int) -> Sequence, seq.power.dut.reset(dut: int) -> Sequence
  • Rust: seq.power().dut().disable(dut: usize) -> Sequence, seq.power().dut().reset(dut: usize) -> Sequence

Parameters:

  • dut (number / int / usize): DUT supply number, 0

In the report: dut.

Exceptions:

  • Throws a validation error if dut is not 0

power.dut.read(dut, options?)​

Measures the DUT supply's voltage and current.

Signatures:

  • NodeJS: seq.power.dut.read(dut: number, options?: SequencePowerReadOptions): Sequence
  • Python: seq.power.dut.read(dut: int, *, repeat: Optional[int] = None, delay: Optional[float] = None, expect: Optional[Union[SequencePowerExpectation, Mapping[str, Any]]] = None) -> Sequence
  • Rust: seq.power().dut().read(dut: usize, options: impl Into<Option<PowerReadOptions>>) -> Sequence
const report = await tester.sequence()
.power.dut.enable(0, 5)
.delay(0.2)
.power.dut.read(0, { repeat: 5, delay: 0.01, expect: { current: { max: 0.5 } } })
.as('current')
.run();

for (const sample of report.named.current.samples) {
console.log(sample.voltage, sample.current, sample.elapsed);
}

Parameters:

  • dut (number / int / usize): DUT supply number, 0
  • options (SequencePowerReadOptions / keyword arguments / PowerReadOptions, optional):
    • repeat, delay: See Bounds and options
    • expect ({ voltage?, current? } / SequencePowerExpectation or a mapping / Option<PowerBounds>): Bounds on the voltage in volts and the current in amperes, at least one. Checked on every reading

In the report: dut. One reading gives { voltage, current } in value. A repeat above 1 gives one { voltage, current, elapsed } entry per reading in samples.

Exceptions:

  • Throws a validation error if dut is not 0, repeat is below 1, delay comes with a repeat below 2, or expect has no bounds
  • SEQUENCE_ASSERTION_FAILED if a reading does not satisfy expect

power.dut.wait(dut, options)​

Waits until the DUT supply's voltage, current or both are inside bounds. When both are given, both must hold on the same reading. The device reads the supply every 10 ms. In Rust, the bounds are a separate argument.

Signatures:

  • NodeJS: seq.power.dut.wait(dut: number, options: SequencePowerWaitOptions): Sequence
  • Python: seq.power.dut.wait(dut: int, *, voltage: Optional[Bounds] = None, current: Optional[Bounds] = None, timeout: Optional[float] = None, stable_for: Optional[float] = None) -> Sequence
  • Rust: seq.power().dut().wait(dut: usize, bounds: PowerBounds, options: impl Into<Option<WaitOptions>>) -> Sequence
const report = await tester.sequence()
.power.dut.enable(0, 5)
.power.dut.wait(0, { current: { max: 0.2 }, timeout: 2, stable_for: 0.05 }).as('settled')
.run();

console.log(report.named.settled.value.current);

Parameters:

  • dut (number / int / usize): DUT supply number, 0
  • voltage, current (bounds): Accepted voltage in volts and current in amperes, at least one. In Rust, both are in PowerBounds
  • timeout, stable_for: See Bounds and options

In the report: dut and, in value, the last { voltage, current } reading.

Exceptions:

  • Throws a validation error if dut is not 0, neither voltage nor current is given, a bound is invalid, or timeout or stable_for is not greater than zero
  • SEQUENCE_TIMED_OUT if the bounds are not met in time: DUT 0 still reads 0.0010 V, 0.000000 A

power.usb.enable(usb) / power.usb.disable(usb) / power.usb.reset(usb)​

Powers a USB port, cuts its power, or resets its supply.

Signatures:

  • NodeJS: seq.power.usb.enable(usb: number): Sequence, seq.power.usb.disable(usb: number): Sequence, seq.power.usb.reset(usb: number): Sequence
  • Python: seq.power.usb.enable(usb: int) -> Sequence, seq.power.usb.disable(usb: int) -> Sequence, seq.power.usb.reset(usb: int) -> Sequence
  • Rust: seq.power().usb().enable(usb: usize) -> Sequence, seq.power().usb().disable(usb: usize) -> Sequence, seq.power().usb().reset(usb: usize) -> Sequence
const report = await tester.sequence()
.power.usb.enable(0)
.com.usb.wait(0, { connected: true, timeout: 5 })
.run();

Parameters:

  • usb (number / int / usize): USB port, 0 or 1

In the report: usb.

Exceptions:

  • Throws a validation error if usb is not 0 or 1

power.usb.read(usb, options?)​

Measures the voltage and current of a USB port (0 or 1), once or several times. It takes the same repeat, delay and expect options as power.dut.read, and reports its readings the same way.

Signatures:

  • NodeJS: seq.power.usb.read(usb: number, options?: SequencePowerReadOptions): Sequence
  • Python: seq.power.usb.read(usb: int, *, repeat: Optional[int] = None, delay: Optional[float] = None, expect: Optional[Union[SequencePowerExpectation, Mapping[str, Any]]] = None) -> Sequence
  • Rust: seq.power().usb().read(usb: usize, options: impl Into<Option<PowerReadOptions>>) -> Sequence
const report = await tester.sequence()
.power.usb.enable(1)
.delay(0.1)
.power.usb.read(1, { expect: { voltage: { min: 4.75 } } }).as('vbus')
.run();

console.log(report.named.vbus.value.voltage);

Parameters:

  • usb (number / int / usize): USB port, 0 or 1
  • options: Same as power.dut.read

In the report: usb, and value or samples as for power.dut.read.

Exceptions:

  • Throws a validation error if usb is not 0 or 1, or for the option errors of power.dut.read
  • SEQUENCE_ASSERTION_FAILED if a reading does not satisfy expect

power.usb.wait(usb, options)​

Waits until the voltage, the current or both of a USB port (0 or 1) are inside bounds. It takes the same bounds and timeout / stable_for options as power.dut.wait.

Signatures:

  • NodeJS: seq.power.usb.wait(usb: number, options: SequencePowerWaitOptions): Sequence
  • Python: seq.power.usb.wait(usb: int, *, voltage: Optional[Bounds] = None, current: Optional[Bounds] = None, timeout: Optional[float] = None, stable_for: Optional[float] = None) -> Sequence
  • Rust: seq.power().usb().wait(usb: usize, bounds: PowerBounds, options: impl Into<Option<WaitOptions>>) -> Sequence

Parameters:

  • usb (number / int / usize): USB port, 0 or 1
  • voltage, current, timeout, stable_for: Same as power.dut.wait

In the report: usb and, in value, the last { voltage, current } reading.

Exceptions:

  • Throws a validation error if usb is not 0 or 1, or for the option errors of power.dut.wait
  • SEQUENCE_TIMED_OUT if the bounds are not met in time

power.relay.read(relay, options?)​

Measures the current through relay 0, in amperes. This step only measures: it does not open or close the relay. Use relays.close to switch it.

Signatures:

  • NodeJS: seq.power.relay.read(relay: number, options?: SequenceRelayReadOptions): Sequence
  • Python: seq.power.relay.read(relay: int, *, repeat: Optional[int] = None, delay: Optional[float] = None, expect: Optional[Union[SequenceCurrentExpectation, Mapping[str, Any]]] = None) -> Sequence
  • Rust: seq.power().relay().read(relay: usize, options: impl Into<Option<RelayPowerReadOptions>>) -> Sequence
const report = await tester.sequence()
.relays.close(0)
.delay(0.05)
.power.relay.read(0, { repeat: 5, delay: 0.01, expect: { current: { max: 0.1 } } })
.as('load')
.run();

for (const sample of report.named.load.samples) console.log(sample.current);

Parameters:

  • relay (number / int / usize): Must be 0. Only relay 0 has a current monitor
  • options (SequenceRelayReadOptions / keyword arguments / RelayPowerReadOptions, optional):
    • repeat, delay: See Bounds and options
    • expect ({ current } / SequenceCurrentExpectation or a mapping / Option<Bounds>): Bounds on the current in amperes, checked on every reading. In Rust, the bounds themselves

In the report: relay (0). One reading gives { current } in value. A repeat above 1 gives one { current, elapsed } entry per reading in samples. There is no voltage.

Exceptions:

  • Throws a validation error if relay is not 0, or for the repeat and delay errors of power.dut.read
  • SEQUENCE_ASSERTION_FAILED if a reading does not satisfy expect

power.relay.wait(relay, options)​

Waits until the current through relay 0 is inside bounds. The device reads it every 10 ms. In Rust, the bounds are a separate argument.

Signatures:

  • NodeJS: seq.power.relay.wait(relay: number, options: SequenceRelayWaitOptions): Sequence
  • Python: seq.power.relay.wait(relay: int, *, current: Bounds, timeout: Optional[float] = None, stable_for: Optional[float] = None) -> Sequence
  • Rust: seq.power().relay().wait(relay: usize, current: impl Into<Bounds>, options: impl Into<Option<WaitOptions>>) -> Sequence
const report = await tester.sequence()
.relays.close(0)
.power.relay.wait(0, { current: { max: 0.01 }, timeout: 3 }).as('asleep')
.run();

Parameters:

  • relay (number / int / usize): Must be 0
  • current (bounds, required): Accepted current in amperes
  • timeout, stable_for: See Bounds and options

In the report: relay (0) and, in value, the last { current } reading.

Exceptions:

  • Throws a validation error if relay is not 0, current is invalid, or timeout or stable_for is not greater than zero
  • SEQUENCE_TIMED_OUT if the current is not inside the bounds in time

HMI steps​

HMI steps write to the screen and play sounds. They report elapsed only. They fail with 409 CONFIGURATION_CONFLICT while an operator prompt owns the screen and knob. See HMI.

hmi.reset()​

Resets the screen and the speaker.

Signatures:

  • NodeJS: seq.hmi.reset(): Sequence
  • Python: seq.hmi.reset() -> Sequence
  • Rust: seq.hmi().reset() -> Sequence

Parameters: None


hmi.screen.print(text)​

Shows text on the screen.

Signatures:

  • NodeJS: seq.hmi.screen.print(text: string): Sequence
  • Python: seq.hmi.screen.print(text: str) -> Sequence
  • Rust: seq.hmi().screen().print(text: impl Into<String>) -> Sequence
const report = await tester.sequence()
.hmi.screen.print('Testing')
.hmi.screen.progress(30)
.hmi.screen.colors({ text: '#FFFFFF', background: '#006400' })
.hmi.audio.play('success', 50)
.run();

Parameters:

  • text (string / str / impl Into<String>): At most 1024 characters (Unicode code points)

Exceptions:

  • Throws a validation error if text is longer than 1024 characters; 400 INVALID_VALUE from the device for the same case

hmi.screen.clear_text() / hmi.screen.clear()​

clear_text() clears the text only. clear() clears the text and the progress bar.

Signatures:

  • NodeJS: seq.hmi.screen.clear_text(): Sequence, seq.hmi.screen.clear(): Sequence
  • Python: seq.hmi.screen.clear_text() -> Sequence, seq.hmi.screen.clear() -> Sequence
  • Rust: seq.hmi().screen().clear_text() -> Sequence, seq.hmi().screen().clear() -> Sequence

Parameters: None


hmi.screen.colors(colors)​

Sets the text, progress bar or background color. Colors not given keep their value.

Signatures:

  • NodeJS: seq.hmi.screen.colors(colors: Color): Sequence
  • Python: seq.hmi.screen.colors(*, text: Optional[str] = None, progress: Optional[str] = None, background: Optional[str] = None) -> Sequence
  • Rust: seq.hmi().screen().colors(colors: ScreenColorsPatch) -> Sequence

Parameters:

  • text, progress, background (string / str / Option<Color>): #RRGGBB colors, at least one. progress is the color of the progress bar; to set how full the bar is, use hmi.screen.progress(). Rust builds a Color with Color::rgb(r, g, b) or by parsing "#RRGGBB"

Exceptions:

  • Throws an error if no color is given, or a validation error if a color is not #RRGGBB

hmi.screen.progress(percent)​

Sets the progress bar. 0 hides it.

Signatures:

  • NodeJS: seq.hmi.screen.progress(percent: number): Sequence
  • Python: seq.hmi.screen.progress(percent: float) -> Sequence
  • Rust: seq.hmi().screen().progress(percent: f64) -> Sequence

Parameters:

  • percent (number / float / f64): Progress, 0-100. This is a number, unlike the progress color of hmi.screen.colors

Exceptions:

  • Throws a validation error if percent is outside 0-100

hmi.audio.play(sound_id, volume)​

Starts a sound. The sequence does not wait for the sound to end: the next step runs at once. To let a sound finish, add a delay after it.

Signatures:

  • NodeJS: seq.hmi.audio.play(sound_id: string, volume: number): Sequence
  • Python: seq.hmi.audio.play(sound_id: str, volume: float) -> Sequence
  • Rust: seq.hmi().audio().play(sound: Sound, volume: f64) -> Sequence

Parameters:

  • sound_id (string / str): 'success', 'warning', 'failure', 'notification_1', 'notification_2' or 'imperial_march'
  • sound (Rust, Sound): Sound::Success, Sound::Warning, Sound::Failure, Sound::Notification1, Sound::Notification2 or Sound::ImperialMarch
  • volume (number / float / f64, required): Volume, 0-100

Exceptions:

  • Throws a validation error if volume is outside 0-100
  • 400 INVALID_VALUE if the device has no sound with that name: Unknown sound_id: beep. The device checks this before running any step

USB steps​

com.usb.wait(usb, options)​

Waits until a USB device is connected to a port (connected: true), or until none is (connected: false). vendor_id and product_id narrow which devices count. The device checks the port every 50 ms. A port must be powered with power.usb.enable for a device to appear. In Rust, the condition is a separate argument.

Signatures:

  • NodeJS: seq.com.usb.wait(usb: number, options: SequenceUsbWaitOptions): Sequence
  • Python: seq.com.usb.wait(usb: int, *, connected: bool, vendor_id: Optional[int] = None, product_id: Optional[int] = None, timeout: Optional[float] = None, stable_for: Optional[float] = None) -> Sequence
  • Rust: seq.com().usb().wait(usb: usize, until: impl Into<UsbCondition>, options: impl Into<Option<WaitOptions>>) -> Sequence
const report = await tester.sequence()
.power.usb.enable(0)
.com.usb.wait(0, { connected: true, vendor_id: 0x0403, timeout: 5 }).as('usb')
.run();

console.log(report.named.usb.devices.map((d) => d.product));

Parameters:

  • usb (number / int / usize): USB port, 0 or 1
  • connected (boolean / bool, required): true waits for at least one matching device, false for none
  • vendor_id, product_id (number / int / Option<u16>, optional): 16-bit identifiers a matching device must carry. Omitted, any device matches
  • until (Rust only, impl Into<UsbCondition>): UsbCondition { connected, vendor_id, product_id }, or a bool for connected without filters
  • timeout, stable_for: See Bounds and options

In the report: usb and, in devices, the matching devices when the wait completed (a list of UsbDevice).

Exceptions:

  • Throws a validation error if usb is not 0 or 1, vendor_id or product_id is outside 0-65535, or timeout or stable_for is not greater than zero
  • SEQUENCE_TIMED_OUT if the condition is not met in time: USB 0 has no matching device, or USB 0 still has 1 matching devices

Sequence report​

run() returns a SequenceReport only when every step completed. See Reading the results.

SequenceReport​

MemberNodeJSPythonRust
Run timeelapsed: numberelapsed: floatelapsed() -> Duration
All results, in step orderresults: SequenceStep[]results: list[SequenceStep]results() -> &[SequenceStep]
Named resultsnamed (object by name)named: dict[str, SequenceNamedResult]none, use get()
One named resultget(name): SequenceNamedResult | undefinedget(name) -> Optional[SequenceNamedResult]get(name: &str) -> Option<SequenceNamedResult<'_>>

elapsed is measured on the device, from the start of the first step:

  • on a step, it is the time from the start of the first step to the end of that step;
  • on the report, it is the duration of the whole sequence.

Time spent waiting for another sequence to finish, before the first step, is not counted.

Each entry of results carries the step's op (for example gpio.read), its elapsed, and the fields listed under each step on this page:

FieldSteps
ioevery gpio.* step that takes a pin, as given (a number or a list)
relayrelays.open, relays.close (absent for every relay), power.relay.*
dut, usbpower.dut.*, power.usb.*, com.usb.wait
valueone reading or applied value: gpio.read and gpio.write of one pin, every wait, power.*.read without repeat
samplesseveral readings: gpio.read of a list or with repeat, gpio.write of several pins, power.*.read with repeat
configgpio.configure (pin configuration), gpio.pwm.output, gpio.pwm.input, gpio.pwm.disable (PWM configuration)
measurementgpio.pwm.measure
devicescom.usb.wait

Other steps (delay, gpio.hold, gpio.release, relays.reset, power.reset, every hmi.* step) report elapsed only.

Named results (NodeJS and Python)​

A named result holds name, index (the step's position, from 0), op, elapsed, the ids of the step (io, relay, dut, usb), and a kind that tells which payload it carries:

  • 'scalar': one value. A gpio.read of one pin given as a number, read once; a gpio.write of one pin; every wait (gpio.wait_voltage, gpio.wait_level, power.dut.wait, power.usb.wait, power.relay.wait); a power.*.read without repeat
  • 'collection': samples. A gpio.read of a pin list (even a list of one pin) or with repeat above 1; a gpio.write of several pins; a power.*.read with repeat above 1
  • 'control': every other step, including com.usb.wait and gpio.pwm.measure. It has no value or samples, but carries the step's own data when it has some: config for gpio.configure and the PWM steps, measurement for gpio.pwm.measure, devices for com.usb.wait

The kind comes from the step as you wrote it, so you know it before the run. In NodeJS, report.named.<name> is typed from the sequence, so report.named.supply.value.voltage type-checks. In Python, the classes are SequenceScalarResult, SequenceCollectionResult and SequenceControlResult.

const report = await tester.sequence()
.power.dut.enable(0, 5)
.power.dut.wait(0, { voltage: { min: 4.8, max: 5.2 }, timeout: 2 }).as('supply')
.gpio.read([0, 1], { repeat: 3, delay: 0.01 }).as('scan')
.run();

console.log(report.named.supply.value.voltage);
for (const sample of report.named.scan.samples) {
console.log(sample.io, sample.value, sample.elapsed);
}

const supply = report.get('supply');
if (supply?.kind === 'scalar') console.log(supply.value);

SequenceStep and named results (Rust)​

report.get(name) returns a SequenceNamedResult with name: &str, index: usize and step: &SequenceStep. report.results() holds the same SequenceStep values in step order.

SequenceStep is an enum with one variant per step. It is #[non_exhaustive], so a match on it needs a _ arm. The variants, grouped like the steps:

  • Timing: Delay
  • GPIO: GpioRead, GpioWrite, GpioWaitVoltage, GpioWaitLevel, GpioHold, GpioRelease, GpioConfigure, GpioReset
  • PWM: GpioPwmOutput, GpioPwmInput, GpioPwmDisable, GpioPwmMeasure
  • Relays: RelaysOpen, RelaysClose, RelaysReset
  • Power: PowerReset, PowerDutEnable, PowerDutDisable, PowerDutReset, PowerDutRead, PowerDutWait, PowerUsbEnable, PowerUsbDisable, PowerUsbReset, PowerUsbRead, PowerUsbWait, PowerRelayRead, PowerRelayWait
  • Screen and sound: HmiReset, HmiScreenPrint, HmiScreenClearText, HmiScreenColors, HmiScreenProgress, HmiScreenClear, HmiAudioPlay
  • USB devices: ComUsbWait

Each variant holds elapsed: Duration and the step's fields:

  • GpioRead and GpioWrite: io: SequenceIds and outcome: SequenceOutcome, which is Value { value: GpioValue } or Samples { samples: Vec<SequenceSample> }
  • GpioWaitVoltage: io and voltage: f64. GpioWaitLevel: io and level: bool
  • GpioConfigure: io and config: GpioConfiguration. GpioReset: io: Option<usize>
  • GpioPwmOutput, GpioPwmInput, GpioPwmDisable: io and config: PwmConfig. GpioPwmMeasure: io and measurement: PwmMeasurement
  • RelaysOpen, RelaysClose: relay: Option<SequenceIds>, None for every relay
  • PowerDutEnable, PowerDutDisable, PowerDutReset: dut. PowerUsbEnable, PowerUsbDisable, PowerUsbReset: usb
  • PowerDutRead, PowerUsbRead: outcome: SequenceOutcome<PowerReading, PowerSample>. PowerDutWait, PowerUsbWait: value: PowerReading. PowerReading has voltage and current
  • PowerRelayRead: relay and outcome: SequenceOutcome<RelayPowerState, RelayPowerSample>. PowerRelayWait: relay and value: RelayPowerState, which has current
  • ComUsbWait: usb and devices: Vec<UsbDevice>

Shortcuts on SequenceStep:

  • elapsed() -> Duration: every step
  • value() -> Option<GpioValue>: a gpio.read or gpio.write of one pin, once
  • samples() -> Cow<'_, [SequenceSample]>: the pin values of a gpio.read, gpio.write, gpio.wait_voltage or gpio.wait_level, one entry per pin and reading. A step on one pin, done once, gives a list of one entry, so every GPIO step can be read with the same loop. Empty for other steps
  • voltage() -> Option<f64>: the voltage that completed a gpio.wait_voltage
  • level() -> Option<bool>: the level that completed a gpio.wait_level

For power, PWM and USB results, match on the variant:

use std::time::Duration;
use ikalogic_at1000::{PowerBounds, ReadOptions, ReadRepeat, SequenceStep, WaitOptions};

let supply = PowerBounds { voltage: Some((4.8..=5.2).into()), ..Default::default() };
let wait = WaitOptions { timeout: Some(Duration::from_secs(2)), ..Default::default() };
let scan = ReadOptions {
repeat: Some(ReadRepeat { count: 3, delay: Some(Duration::from_millis(10)) }),
..Default::default()
};
let report = tester.sequence()
.power().dut().enable(0, 5.0)
.power().dut().wait(0, supply, wait).named("supply")
.gpio().read([0, 1], scan).named("scan")
.run(None)?;

if let Some(SequenceStep::PowerDutWait { value, .. }) = report.get("supply").map(|r| r.step) {
println!("{} V, {} A", value.voltage, value.current);
}
if let Some(scan) = report.get("scan") {
for sample in scan.step.samples().iter() {
println!("IO {}: {:?} at {:?}", sample.io, sample.value, sample.elapsed);
}
}

Errors​

A sequence fails as a whole: there is no partial report. What happens to the device after a failure is described in When a sequence fails.

Error messages​

Errors about one step start with its position, from 0, and its step name:

Command 3 (`gpio.wait_level`): pin 1 still reads false

Each error also has a suggestion that tells how to fix it: error.suggestion in NodeJS and Python, the suggestion field of ApiError in Rust.

Error codes​

CodeHTTPWhenSteps already run
SEQUENCE_TIMED_OUT409A wait did not succeed within its timeout or the time left; the sequence went past its time limit; or the sequence used its whole time limit waiting for another sequence to finishKeep their effect. If the time ran out before the first step, nothing runs
SEQUENCE_ASSERTION_FAILED409A reading did not satisfy expect (gpio.read, power.*.read, gpio.pwm.measure)Keep their effect
ACCESS_REVOKED409Another session opened the device during the run. The device checks this before each step: Command 1 (`relays.close`): Access was taken over by remote other.Keep their effect
INVALID_VALUE400A value is not accepted: a list with a duplicate, a delay without repeat of 2 or more, an expect that does not match the pin's mode, an unknown sound, text too longNothing runs
OUT_OF_RANGE_VALUE400A number is outside its range: a pin, relay, port or supply number, a voltage, a duration, more than 65536 stepsNothing runs
INVALID_CONFIGURATION400A step does not fit the pin's configuration at that point: a write to an input, gpio.wait_voltage on a digital pin, gpio.wait_level on an analog pinNothing runs
JSON_DESERIALIZATION400The request is not valid. The SDKs build valid requests, so this points to an SDK and firmware mismatchNothing runs
CONFIGURATION_CONFLICT409A step uses a pin that a bus or PWM uses; an HMI step while an operator prompt is openNothing runs when the device finds it before the run
DEVICE_NOT_OPEN409No session is openNothing runs
INTERNAL_ERROR500A resource the sequence needs did not start, for example The HMI is not availableNothing runs

Validation errors (400) mean the device found the problem before running any step, so the device is unchanged.

How each SDK reports errors​

  • NodeJS: device errors are an ApiError with code, detail (the message), suggestion and path
  • Python: device errors are an ApiError with the same fields. ACCESS_REVOKED raises AccessRevokedError and DEVICE_NOT_OPEN raises DeviceNotOpenError, both subclasses of ApiError
  • Rust: ACCESS_REVOKED is At1000Error::Revoked(ApiError), DEVICE_NOT_OPEN is At1000Error::NotOpen(ApiError), and every other device error is At1000Error::Api(ApiError). ApiError::code is an ErrorCode: ErrorCode::SequenceTimedOut, ErrorCode::SequenceAssertionFailed, ErrorCode::InvalidValue, ErrorCode::OutOfRangeValue, ErrorCode::InvalidConfiguration, ErrorCode::JsonDeserialization, ErrorCode::ConfigurationConflict
import { ApiError } from '@ikalogic/at1000';

try {
await tester.sequence()
.gpio.configure(1, { mode: 'digital', direction: 'input', vil: 0.8, vih: 2 })
.gpio.wait_level(1, 'high', { timeout: 0.5 })
.run();
} catch (err) {
if (err instanceof ApiError && err.code === 'SEQUENCE_TIMED_OUT') {
console.log(err.detail); // Command 1 (`gpio.wait_level`): pin 1 still reads false
await tester.reset();
} else {
throw err;
}
}

Checks before sending​

Each SDK checks the steps when they are appended, so most mistakes are caught before anything is sent: numbers out of range, pins, relays or ports the device does not have, lists that are empty or list an id twice, a delay without repeat of 2 or more, bounds with neither min nor max or with min above max, PWM pins used in the wrong role, and options with no field set.

  • NodeJS: the step method throws an Error at once. A pin, relay, port or supply number the device does not have gives an Error whose message reads like the device's RESOURCE_NOT_FOUND error. Adding more than 65536 steps also throws
  • Python: the step method raises ValueError (including pydantic's ValidationError). A pin, relay, port or supply the device does not have raises an ApiError with code RESOURCE_NOT_FOUND
  • Rust: an invalid step is not appended. run() returns At1000Error::InvalidInput for the first one, without sending anything. Its message names the step: step 2 (`gpio.read`): …

The SDKs do not know the pin configuration or the sounds on the device. Those checks are done by the device, before it runs any step. In Rust, Sound is an enum, so a sound name cannot be wrong.

If the device answers with a report that does not hold one matching result per step, run() fails: NodeJS throws Invalid sequence response, Python raises ValueError, and Rust returns At1000Error::InvalidResponse.

note

tester.gpio.sequence() is deprecated. It still works, but new code should use tester.sequence(): see Moving from gpio.sequence().