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:
| Namespace | NodeJS and Python | Rust |
|---|---|---|
| GPIO | seq.gpio.read(...) | seq.gpio().read(...) |
| PWM | seq.gpio.pwm.output(...) | seq.gpio().pwm().output(...) |
| Relays | seq.relays.close(...) | seq.relays().close(...) |
| DUT supply | seq.power.dut.enable(...) | seq.power().dut().enable(...) |
| USB port power | seq.power.usb.enable(...) | seq.power().usb().enable(...) |
| Relay 0 current | seq.power.relay.read(...) | seq.power().relay().read(...) |
| Screen | seq.hmi.screen.print(...) | seq.hmi().screen().print(...) |
| Speaker | seq.hmi.audio.play(...) | seq.hmi().audio().play(...) |
| USB devices | seq.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),Durationin Rust. - Options: an options object in NodeJS, keyword arguments in Python, and a struct or
Nonein 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
- NodeJS
- Python
- Rust
const seq = tester.sequence();
seq = tester.sequence()
let seq = tester.sequence();
println!("{} steps, empty: {}", seq.len(), seq.is_empty());
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
- NodeJS
- Python
- Rust
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();
report = (
tester.sequence()
.gpio.configure(0, mode="digital", direction="output", value=False, vol=0.0, voh=3.3, vil=0.8, vih=2.0)
.gpio.write(0, True)
.delay(0.01) # 10 ms, timed on the device
.gpio.write(0, False)
.run()
)
use std::time::Duration;
use ikalogic_at1000::GpioConfiguration;
let output = GpioConfiguration::DigitalOutput { value: false, vol: 0.0, voh: 3.3, vil: 0.8, vih: 2.0 };
let report = tester.sequence()
.gpio().configure(0, output)
.gpio().write(0, true)
.delay(Duration::from_millis(10)) // 10 ms, timed on the device
.gpio().write(0, false)
.run(None)?;
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
durationis not finite and greater than zero. In Rust, a zeroDurationmakesrun()returnAt1000Error::InvalidInput SEQUENCE_TIMED_OUTif 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
- NodeJS
- Python
- Rust
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);
report = (
tester.sequence()
.gpio.read(0).as_("initial")
.delay(0.01).as_("pause")
.run()
)
initial = report.get("initial")
pause = report.get("pause")
print(initial, pause.elapsed if pause else None)
use std::time::Duration;
let report = tester.sequence()
.gpio().read(0, None).named("initial")
.delay(Duration::from_millis(10)).named("pause")
.run(None)?;
let initial = report.get("initial").and_then(|r| r.step.value());
let pause = report.get("pause").map(|r| r.step.elapsed());
println!("{initial:?} {pause:?}");
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 (
ValueErrorin 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()returnsAt1000Error::InvalidInputfor parametername
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; withikalogic_at1000.aio,async def run(timeout: float = 30.0) -> SequenceReport - Rust:
seq.run(&self, timeout: impl Into<Option<Duration>>) -> Result<SequenceReport, At1000Error>
- NodeJS
- Python
- Rust
const report = await tester.sequence()
.gpio.read([0, 1, 2]).as('scan')
.run({ timeout: 10 });
console.log(report.elapsed, report.results.length);
report = tester.sequence().gpio.read([0, 1, 2]).as_("scan").run(timeout=10.0)
print(report.elapsed, len(report.results))
use std::time::Duration;
let report = tester.sequence()
.gpio().read([0, 1, 2], None).named("scan")
.run(Duration::from_secs(10))?;
println!("{} steps in {:?}", report.results().len(), report.elapsed());
Parameters:
timeout(number/float/Duration, optional): Time limit of the whole sequence, 30 s when omitted (Nonein 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 owntimeoutis cut to the time left. The SDK gives the HTTP request 2 s more thantimeout, 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
timeoutis not finite and greater than zero. In NodeJS this error is thrown byrun()itself, not by the returned promise - In Rust, also returns
At1000Error::InvalidInputfor 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 withNonefor no limit on that side:(None, 0.5)means "at most 0.5" - Rust:
Bounds::AtLeast(v),Bounds::AtMost(v)orBounds::Between { min, max }, also built from the rangesmin..,..=maxandmin..=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 omitteddelay(number/float): Pause in seconds between two readings, from the end of one to the start of the next. Only allowed whenrepeatis 2 or more. There is no pause before the first reading or after the last one- Rust:
ReadRepeat { count: u32, delay: Option<Duration> }in therepeatfield of the step's options. Acountof 0 is invalid expectis checked on every reading. A reading outside it stops the run withSEQUENCE_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 leftstable_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> }, orNone - 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
- NodeJS
- Python
- Rust
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();
report = (
tester.sequence()
.gpio.configure(1, mode="digital", direction="input", vil=0.8, vih=2.0) # 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()
)
use std::time::Duration;
use ikalogic_at1000::{GpioConfiguration, ReadOptions, ReadRepeat};
let burst = ReadRepeat { count: 4, delay: Some(Duration::from_millis(1)) };
let report = tester.sequence()
.gpio().configure(1, GpioConfiguration::DigitalInput { vil: 0.8, vih: 2.0 }) // a level needs a digital pin
.gpio().read(0, None) // one pin, one reading
.gpio().read([0, 1, 2], None).named("scan") // three pins, in the order given
.gpio().read(0, ReadOptions { repeat: Some(burst), ..Default::default() }).named("burst")
.gpio().read(1, ReadOptions { expect: Some(true.into()), ..Default::default() }).named("check")
.run(None)?;
Parameters:
io(number | number[]/int | list[int]/impl Into<SequenceIds>): One pin, or distinct pins read in the order given. Rust takes ausize, aVec<usize>, a&[usize]or a[usize; N]options(SequenceReadOptions/ keyword arguments /ReadOptions, optional):repeat,delay: See Bounds and options. With several pins,delayis the pause between two passes over the whole list. There is no pause between the pins of one pass. In Rust, both are inReadOptions::repeatexpect(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 bothminandmax, each in [-25, 25] V. Rust'sSequenceExpectisLevel(bool)orRange { min, max }, built from aboolor amin..=maxrange
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,
repeatis below 1,delaycomes with arepeatbelow 2, or voltage bounds are outside [-25, 25] V or haveminabovemax 400 INVALID_VALUEifexpectdoes not match the pin's mode: a boolean on an analog pin, or bounds on a digital pinSEQUENCE_ASSERTION_FAILEDif a reading does not satisfyexpect: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
- NodeJS
- Python
- Rust
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();
output = dict(mode="digital", direction="output", value=False, vol=0.0, voh=3.3, vil=0.8, vih=2.0)
report = (
tester.sequence()
.gpio.configure(0, **output)
.gpio.configure(1, **output)
.gpio.configure(2, **output)
.gpio.configure(3, mode="analog", direction="output", value=0.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()
)
use ikalogic_at1000::{GpioConfiguration, GpioValue};
let output = GpioConfiguration::DigitalOutput { value: false, vol: 0.0, voh: 3.3, vil: 0.8, vih: 2.0 };
let report = tester.sequence()
.gpio().configure(0, output.clone())
.gpio().configure(1, output.clone())
.gpio().configure(2, output)
.gpio().configure(3, GpioConfiguration::AnalogOutput { value: 0.0 })
.gpio().write(0, true) // one pin
.gpio().write([0, 1, 2], false) // the same value on every pin
.gpio().write(
[0, 1, 3],
[GpioValue::Digital(true), GpioValue::Digital(false), GpioValue::Analog(2.5)],
) // one value per pin
.run(None)?;
Parameters:
io(number | number[]/int | list[int]/impl Into<SequenceIds>): One pin, or distinct pins written in the order givenvalue(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 abool, anf64or aGpioValue, or an array orVecof them; mixing levels and voltages takesGpioValue
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_CONFIGURATIONif a pin is an input at that point of the sequence:cannot write to pin 0, configured as input400 INVALID_VALUEfor a boolean on an analog pin;400 OUT_OF_RANGE_VALUEfor a voltage outside [0, 24] V409 CONFIGURATION_CONFLICTif 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
- NodeJS
- Python
- Rust
const report = await tester.sequence()
.gpio.wait_voltage(3, { min: 2.0, max: 3.0, timeout: 0.5 }).as('band')
.run();
report = (
tester.sequence()
.gpio.wait_voltage(3, min=2.0, max=3.0, timeout=0.5).as_("band")
.run()
)
use std::time::Duration;
use ikalogic_at1000::WaitOptions;
let wait = WaitOptions { timeout: Some(Duration::from_millis(500)), ..Default::default() };
let report = tester.sequence()
.gpio().wait_voltage(3, 2.0..=3.0, wait).named("band")
.run(None)?;
Parameters:
io(number/int/usize): One analog pinmin,max(number/float): Lowest and highest accepted voltage, each in [-25, 25] V, at least one.minalone waits for the pin to read at or above it,maxalone at or below itband(Rust only,impl Into<Bounds>): Replacesminandmax:2.0..,..=3.0,2.0..=3.0or aBoundstimeout,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
minnormaxis given,minis abovemax, an edge is outside [-25, 25] V, ortimeoutorstable_foris not greater than zero 400 INVALID_CONFIGURATIONif the pin is not analog at that point:pin 3 must be in `analog` mode. Usegpio.wait_levelon a digital pinSEQUENCE_TIMED_OUTif the pin is not inside the bounds in time:pin 3 still reads 1.2000 V
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
- NodeJS
- Python
- Rust
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();
report = (
tester.sequence()
.gpio.configure(0, mode="digital", direction="input", vil=0.8, vih=2.0)
.gpio.wait_level(0, "high", stable_for=0.02, timeout=0.5).as_("ready")
.run()
)
use std::time::Duration;
use ikalogic_at1000::{GpioConfiguration, WaitOptions};
let report = tester.sequence()
.gpio().configure(0, GpioConfiguration::DigitalInput { vil: 0.8, vih: 2.0 })
.gpio().wait_level(0, true, WaitOptions {
stable_for: Some(Duration::from_millis(20)),
timeout: Some(Duration::from_millis(500)),
}).named("ready")
.run(None)?;
Parameters:
io(number/int/usize): One digital pinlevel('low' | 'high' | boolean/"low" | "high" | bool | SequenceLevel/impl Into<SequenceLevel>): Level to wait for;trueis high. Rust takes aboolorSequenceLevel::Low/SequenceLevel::Highoptions(SequenceWaitOptions/ keyword arguments /WaitOptions, optional):timeoutandstable_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
timeoutorstable_foris not greater than zero 400 INVALID_CONFIGURATIONif the pin is not digital at that point:pin 0 must be in `digital` mode. Usegpio.wait_voltageon an analog pinSEQUENCE_TIMED_OUTif 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
- NodeJS
- Python
- Rust
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();
output = dict(mode="digital", direction="output", value=False, vol=0.0, voh=3.3, vil=0.8, vih=2.0)
report = (
tester.sequence()
.gpio.configure(0, **output)
.gpio.configure(1, **output)
.gpio.configure(2, **output)
.gpio.hold() # hold the outputs
.gpio.write([0, 1, 2], [True, True, False])
.gpio.release() # apply the three writes together
.run()
)
use ikalogic_at1000::GpioConfiguration;
let output = GpioConfiguration::DigitalOutput { value: false, vol: 0.0, voh: 3.3, vil: 0.8, vih: 2.0 };
let report = tester.sequence()
.gpio().configure(0, output.clone())
.gpio().configure(1, output.clone())
.gpio().configure(2, output)
.gpio().hold() // hold the outputs
.gpio().write([0, 1, 2], [true, true, false])
.gpio().release() // apply the three writes together
.run(None)?;
Parameters: None
In the report: elapsed only.
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
- NodeJS
- Python
- Rust
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();
report = (
tester.sequence()
.gpio.configure(
0, mode="digital", direction="output", value=False, vol=0.0, voh=5.0, vil=1.0, vih=4.0
)
.gpio.configure(3, mode="analog", direction="output", value=2.5)
.run()
)
use ikalogic_at1000::{GpioConfigPatch, GpioDirection, GpioMode};
let output = GpioConfigPatch {
mode: Some(GpioMode::Digital),
direction: Some(GpioDirection::Output),
value: Some(false.into()),
vol: Some(0.0),
voh: Some(5.0),
vil: Some(1.0),
vih: Some(4.0),
};
let report = tester.sequence()
.gpio().configure(0, output)
.run(None)?;
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 outputvil,vih: Input thresholds, in [-25, 25] Vvol,voh: Output levels of a digital output, in [0, 24] V, withvohabovevol
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
400validation errors of the device for an invalid combination, as for the pin's configure methods409 CONFIGURATION_CONFLICTif 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
- NodeJS
- Python
- Rust
const report = await tester.sequence()
.gpio.reset(3) // pin 3 only
.gpio.reset() // every pin, the output hold and PWM
.run();
report = (
tester.sequence()
.gpio.reset(3) # pin 3 only
.gpio.reset() # every pin, the output hold and PWM
.run()
)
let report = tester.sequence()
.gpio().reset(3) // pin 3 only
.gpio().reset(None) // every pin, the output hold and PWM
.run(None)?;
Parameters:
io(number/int/usize, optional): The pin to reset. Omit it (Nonein 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_CONFLICTif 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
- NodeJS
- Python
- Rust
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);
report = (
tester.sequence()
.gpio.pwm.output(18, frequency=10_000, vcc=3.3, duration=1.0).as_("clock")
.run()
)
use std::time::Duration;
use ikalogic_at1000::PwmOutputConfig;
let report = tester.sequence()
.gpio().pwm().output(18, PwmOutputConfig {
frequency: 10_000.0,
vcc: Some(3.3),
duration: Some(Duration::from_secs(1)),
}).named("clock")
.run(None)?;
Parameters:
io(number/int/usize): IO 18 or 22options(PwmOutputOptions/ keyword arguments /PwmOutputConfig):frequency(number/float/f64): Requested frequency, from about 91.56 Hz (48 MHz / 524289) to 7.5 MHzvcc(number/float/Option<f64>, optional): Logic level, 1.8-3.3 V. Unchanged when omittedduration(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,vccordurationis out of range 409 CONFIGURATION_CONFLICTif 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)
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
- NodeJS
- Python
- Rust
const report = await tester.sequence()
.gpio.pwm.input(17, { vcc: 3.3 })
.run();
report = tester.sequence().gpio.pwm.input(17, vcc=3.3).run()
use ikalogic_at1000::PwmInputConfig;
let report = tester.sequence()
.gpio().pwm().input(17, PwmInputConfig { vcc: Some(3.3) })
.run(None)?;
Parameters:
io(number/int/usize): IO 17 or 21vcc(number/float/Option<f64>, optional): Logic level, 1.8-3.3 V. Unchanged when omitted. In Rust, passNonefor 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
vccis out of range 409 CONFIGURATION_CONFLICTif 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
- NodeJS
- Python
- Rust
const report = await tester.sequence()
.gpio.pwm.disable(18)
.run();
report = tester.sequence().gpio.pwm.disable(18).run()
let report = tester.sequence()
.gpio().pwm().disable(18)
.run(None)?;
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
- NodeJS
- Python
- Rust
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);
report = (
tester.sequence()
.gpio.pwm.output(18, frequency=10_000, vcc=3.3)
.gpio.pwm.input(21, vcc=3.3)
.gpio.pwm.measure(21, timeout=1.0, expect={"frequency": (9_900, 10_100)})
.as_("clock")
.gpio.pwm.disable(18)
.gpio.pwm.disable(21)
.run()
)
use std::time::Duration;
use ikalogic_at1000::{PwmExpect, PwmInputConfig, PwmMeasureOptions, PwmOutputConfig};
let measure = PwmMeasureOptions {
timeout: Some(Duration::from_secs(1)),
expect: Some(PwmExpect {
frequency: Some((9_900.0..=10_100.0).into()),
..Default::default()
}),
};
let report = tester.sequence()
.gpio().pwm().output(18, PwmOutputConfig { frequency: 10_000.0, vcc: Some(3.3), duration: None })
.gpio().pwm().input(21, PwmInputConfig { vcc: Some(3.3) })
.gpio().pwm().measure(21, measure).named("clock")
.gpio().pwm().disable(18)
.gpio().pwm().disable(21)
.run(None)?;
Parameters:
io(number/int/usize): IO 17 or 21options(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 limitexpect({ frequency?, duty_cycle? }/SequencePwmExpectationor 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,
timeoutis outside 0.1-60 s, orexpecthas no bounds SEQUENCE_ASSERTION_FAILEDwithexpect, 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`. Withoutexpect, atimeoutstatus is reported normallySEQUENCE_TIMED_OUTif 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
- NodeJS
- Python
- Rust
const report = await tester.sequence()
.relays.close([0, 1]).as('routed')
.delay(0.1)
.relays.open() // every relay
.run();
report = (
tester.sequence()
.relays.close([0, 1]).as_("routed")
.delay(0.1)
.relays.open() # every relay
.run()
)
use std::time::Duration;
use ikalogic_at1000::RelaySelection;
let report = tester.sequence()
.relays().close([0, 1]).named("routed")
.delay(Duration::from_millis(100))
.relays().open(RelaySelection::All) // every relay
.run(None)?;
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, orRelaySelection::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
- NodeJS
- Python
- Rust
const report = await tester.sequence()
.power.dut.enable(0, 5)
.power.dut.wait(0, { voltage: { min: 4.8, max: 5.2 }, timeout: 2 })
.run();
report = (
tester.sequence()
.power.dut.enable(0, 5.0)
.power.dut.wait(0, voltage=(4.8, 5.2), timeout=2.0)
.run()
)
use std::time::Duration;
use ikalogic_at1000::{PowerBounds, 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 report = tester.sequence()
.power().dut().enable(0, 5.0)
.power().dut().wait(0, supply, wait)
.run(None)?;
Parameters:
dut(number/int/usize): DUT supply number, 0target_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
dutis not 0, ortarget_voltageis outside 1.6-13 V and is not 24 V 400 OUT_OF_RANGE_VALUEfrom 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
dutis 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
- NodeJS
- Python
- Rust
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);
}
report = (
tester.sequence()
.power.dut.enable(0, 5.0)
.delay(0.2)
.power.dut.read(0, repeat=5, delay=0.01, expect={"current": (None, 0.5)})
.as_("current")
.run()
)
use std::time::Duration;
use ikalogic_at1000::{PowerBounds, PowerReadOptions, ReadRepeat};
let read = PowerReadOptions {
repeat: Some(ReadRepeat { count: 5, delay: Some(Duration::from_millis(10)) }),
expect: Some(PowerBounds { current: Some((..=0.5).into()), ..Default::default() }),
};
let report = tester.sequence()
.power().dut().enable(0, 5.0)
.delay(Duration::from_millis(200))
.power().dut().read(0, read).named("current")
.run(None)?;
Parameters:
dut(number/int/usize): DUT supply number, 0options(SequencePowerReadOptions/ keyword arguments /PowerReadOptions, optional):repeat,delay: See Bounds and optionsexpect({ voltage?, current? }/SequencePowerExpectationor 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
dutis not 0,repeatis below 1,delaycomes with arepeatbelow 2, orexpecthas no bounds SEQUENCE_ASSERTION_FAILEDif a reading does not satisfyexpect
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
- NodeJS
- Python
- Rust
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);
report = (
tester.sequence()
.power.dut.enable(0, 5.0)
.power.dut.wait(0, current=(None, 0.2), timeout=2.0, stable_for=0.05).as_("settled")
.run()
)
use std::time::Duration;
use ikalogic_at1000::{PowerBounds, WaitOptions};
let settled = PowerBounds { current: Some((..=0.2).into()), ..Default::default() };
let wait = WaitOptions {
timeout: Some(Duration::from_secs(2)),
stable_for: Some(Duration::from_millis(50)),
};
let report = tester.sequence()
.power().dut().enable(0, 5.0)
.power().dut().wait(0, settled, wait).named("settled")
.run(None)?;
Parameters:
dut(number/int/usize): DUT supply number, 0voltage,current(bounds): Accepted voltage in volts and current in amperes, at least one. In Rust, both are inPowerBoundstimeout,stable_for: See Bounds and options
In the report: dut and, in value, the last { voltage, current } reading.
Exceptions:
- Throws a validation error if
dutis not 0, neithervoltagenorcurrentis given, a bound is invalid, ortimeoutorstable_foris not greater than zero SEQUENCE_TIMED_OUTif 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
- NodeJS
- Python
- Rust
const report = await tester.sequence()
.power.usb.enable(0)
.com.usb.wait(0, { connected: true, timeout: 5 })
.run();
report = (
tester.sequence()
.power.usb.enable(0)
.com.usb.wait(0, connected=True, timeout=5.0)
.run()
)
use std::time::Duration;
use ikalogic_at1000::WaitOptions;
let wait = WaitOptions { timeout: Some(Duration::from_secs(5)), ..Default::default() };
let report = tester.sequence()
.power().usb().enable(0)
.com().usb().wait(0, true, wait)
.run(None)?;
Parameters:
usb(number/int/usize): USB port, 0 or 1
In the report: usb.
Exceptions:
- Throws a validation error if
usbis 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
- NodeJS
- Python
- Rust
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);
report = (
tester.sequence()
.power.usb.enable(1)
.delay(0.1)
.power.usb.read(1, expect={"voltage": (4.75, None)}).as_("vbus")
.run()
)
use std::time::Duration;
use ikalogic_at1000::{PowerBounds, PowerReadOptions};
let read = PowerReadOptions {
expect: Some(PowerBounds { voltage: Some((4.75..).into()), ..Default::default() }),
..Default::default()
};
let report = tester.sequence()
.power().usb().enable(1)
.delay(Duration::from_millis(100))
.power().usb().read(1, read).named("vbus")
.run(None)?;
Parameters:
usb(number/int/usize): USB port, 0 or 1options: Same aspower.dut.read
In the report: usb, and value or samples as for power.dut.read.
Exceptions:
- Throws a validation error if
usbis not 0 or 1, or for the option errors ofpower.dut.read SEQUENCE_ASSERTION_FAILEDif a reading does not satisfyexpect
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 1voltage,current,timeout,stable_for: Same aspower.dut.wait
In the report: usb and, in value, the last { voltage, current } reading.
Exceptions:
- Throws a validation error if
usbis not 0 or 1, or for the option errors ofpower.dut.wait SEQUENCE_TIMED_OUTif 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
- NodeJS
- Python
- Rust
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);
report = (
tester.sequence()
.relays.close(0)
.delay(0.05)
.power.relay.read(0, repeat=5, delay=0.01, expect={"current": (None, 0.1)})
.as_("load")
.run()
)
use std::time::Duration;
use ikalogic_at1000::{ReadRepeat, RelayPowerReadOptions};
let read = RelayPowerReadOptions {
repeat: Some(ReadRepeat { count: 5, delay: Some(Duration::from_millis(10)) }),
expect: Some((..=0.1).into()),
};
let report = tester.sequence()
.relays().close(0)
.delay(Duration::from_millis(50))
.power().relay().read(0, read).named("load")
.run(None)?;
Parameters:
relay(number/int/usize): Must be 0. Only relay 0 has a current monitoroptions(SequenceRelayReadOptions/ keyword arguments /RelayPowerReadOptions, optional):repeat,delay: See Bounds and optionsexpect({ current }/SequenceCurrentExpectationor 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
relayis not 0, or for therepeatanddelayerrors ofpower.dut.read SEQUENCE_ASSERTION_FAILEDif a reading does not satisfyexpect
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
- NodeJS
- Python
- Rust
const report = await tester.sequence()
.relays.close(0)
.power.relay.wait(0, { current: { max: 0.01 }, timeout: 3 }).as('asleep')
.run();
report = (
tester.sequence()
.relays.close(0)
.power.relay.wait(0, current=(None, 0.01), timeout=3.0).as_("asleep")
.run()
)
use std::time::Duration;
use ikalogic_at1000::WaitOptions;
let wait = WaitOptions { timeout: Some(Duration::from_secs(3)), ..Default::default() };
let report = tester.sequence()
.relays().close(0)
.power().relay().wait(0, ..=0.01, wait).named("asleep")
.run(None)?;
Parameters:
relay(number/int/usize): Must be 0current(bounds, required): Accepted current in amperestimeout,stable_for: See Bounds and options
In the report: relay (0) and, in value, the last { current } reading.
Exceptions:
- Throws a validation error if
relayis not 0,currentis invalid, ortimeoutorstable_foris not greater than zero SEQUENCE_TIMED_OUTif 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
- NodeJS
- Python
- Rust
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();
report = (
tester.sequence()
.hmi.screen.print("Testing")
.hmi.screen.progress(30)
.hmi.screen.colors(text="#FFFFFF", background="#006400")
.hmi.audio.play("success", 50)
.run()
)
use ikalogic_at1000::{Color, ScreenColorsPatch, Sound};
let colors = ScreenColorsPatch {
text: Some(Color::rgb(0xFF, 0xFF, 0xFF)),
background: Some(Color::rgb(0x00, 0x64, 0x00)),
..Default::default()
};
let report = tester.sequence()
.hmi().screen().print("Testing")
.hmi().screen().progress(30.0)
.hmi().screen().colors(colors)
.hmi().audio().play(Sound::Success, 50.0)
.run(None)?;
Parameters:
text(string/str/impl Into<String>): At most 1024 characters (Unicode code points)
Exceptions:
- Throws a validation error if
textis longer than 1024 characters;400 INVALID_VALUEfrom 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>):#RRGGBBcolors, at least one.progressis the color of the progress bar; to set how full the bar is, usehmi.screen.progress(). Rust builds aColorwithColor::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 theprogresscolor ofhmi.screen.colors
Exceptions:
- Throws a validation error if
percentis 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::Notification2orSound::ImperialMarchvolume(number/float/f64, required): Volume, 0-100
Exceptions:
- Throws a validation error if
volumeis outside 0-100 400 INVALID_VALUEif 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
- NodeJS
- Python
- Rust
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));
report = (
tester.sequence()
.power.usb.enable(0)
.com.usb.wait(0, connected=True, vendor_id=0x0403, timeout=5.0).as_("usb")
.run()
)
use std::time::Duration;
use ikalogic_at1000::{UsbCondition, WaitOptions};
let ftdi = UsbCondition { connected: true, vendor_id: Some(0x0403), product_id: None };
let wait = WaitOptions { timeout: Some(Duration::from_secs(5)), ..Default::default() };
let report = tester.sequence()
.power().usb().enable(0)
.com().usb().wait(0, ftdi, wait).named("usb")
.run(None)?;
Parameters:
usb(number/int/usize): USB port, 0 or 1connected(boolean/bool, required):truewaits for at least one matching device,falsefor nonevendor_id,product_id(number/int/Option<u16>, optional): 16-bit identifiers a matching device must carry. Omitted, any device matchesuntil(Rust only,impl Into<UsbCondition>):UsbCondition { connected, vendor_id, product_id }, or aboolforconnectedwithout filterstimeout,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
usbis not 0 or 1,vendor_idorproduct_idis outside 0-65535, ortimeoutorstable_foris not greater than zero SEQUENCE_TIMED_OUTif the condition is not met in time:USB 0 has no matching device, orUSB 0 still has 1 matching devices
Sequence report​
run() returns a SequenceReport only when every step completed. See Reading the results.
SequenceReport​
| Member | NodeJS | Python | Rust |
|---|---|---|---|
| Run time | elapsed: number | elapsed: float | elapsed() -> Duration |
| All results, in step order | results: SequenceStep[] | results: list[SequenceStep] | results() -> &[SequenceStep] |
| Named results | named (object by name) | named: dict[str, SequenceNamedResult] | none, use get() |
| One named result | get(name): SequenceNamedResult | undefined | get(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:
| Field | Steps |
|---|---|
io | every gpio.* step that takes a pin, as given (a number or a list) |
relay | relays.open, relays.close (absent for every relay), power.relay.* |
dut, usb | power.dut.*, power.usb.*, com.usb.wait |
value | one reading or applied value: gpio.read and gpio.write of one pin, every wait, power.*.read without repeat |
samples | several readings: gpio.read of a list or with repeat, gpio.write of several pins, power.*.read with repeat |
config | gpio.configure (pin configuration), gpio.pwm.output, gpio.pwm.input, gpio.pwm.disable (PWM configuration) |
measurement | gpio.pwm.measure |
devices | com.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': onevalue. Agpio.readof one pin given as a number, read once; agpio.writeof one pin; every wait (gpio.wait_voltage,gpio.wait_level,power.dut.wait,power.usb.wait,power.relay.wait); apower.*.readwithout repeat'collection':samples. Agpio.readof a pin list (even a list of one pin) or withrepeatabove 1; agpio.writeof several pins; apower.*.readwithrepeatabove 1'control': every other step, includingcom.usb.waitandgpio.pwm.measure. It has novalueorsamples, but carries the step's own data when it has some:configforgpio.configureand the PWM steps,measurementforgpio.pwm.measure,devicesforcom.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.
- NodeJS
- Python
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);
from ikalogic_at1000 import SequenceCollectionResult, SequenceScalarResult
report = (
tester.sequence()
.power.dut.enable(0, 5.0)
.power.dut.wait(0, voltage=(4.8, 5.2), timeout=2.0).as_("supply")
.gpio.read([0, 1], repeat=3, delay=0.01).as_("scan")
.run()
)
supply = report.named["supply"]
if isinstance(supply, SequenceScalarResult):
print(supply.value)
scan = report.get("scan")
if isinstance(scan, SequenceCollectionResult):
for sample in scan.samples:
print(sample)
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:
GpioReadandGpioWrite:io: SequenceIdsandoutcome: SequenceOutcome, which isValue { value: GpioValue }orSamples { samples: Vec<SequenceSample> }GpioWaitVoltage:ioandvoltage: f64.GpioWaitLevel:ioandlevel: boolGpioConfigure:ioandconfig: GpioConfiguration.GpioReset:io: Option<usize>GpioPwmOutput,GpioPwmInput,GpioPwmDisable:ioandconfig: PwmConfig.GpioPwmMeasure:ioandmeasurement: PwmMeasurementRelaysOpen,RelaysClose:relay: Option<SequenceIds>,Nonefor every relayPowerDutEnable,PowerDutDisable,PowerDutReset:dut.PowerUsbEnable,PowerUsbDisable,PowerUsbReset:usbPowerDutRead,PowerUsbRead:outcome: SequenceOutcome<PowerReading, PowerSample>.PowerDutWait,PowerUsbWait:value: PowerReading.PowerReadinghasvoltageandcurrentPowerRelayRead:relayandoutcome: SequenceOutcome<RelayPowerState, RelayPowerSample>.PowerRelayWait:relayandvalue: RelayPowerState, which hascurrentComUsbWait:usbanddevices: Vec<UsbDevice>
Shortcuts on SequenceStep:
elapsed() -> Duration: every stepvalue() -> Option<GpioValue>: agpio.readorgpio.writeof one pin, oncesamples() -> Cow<'_, [SequenceSample]>: the pin values of agpio.read,gpio.write,gpio.wait_voltageorgpio.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 stepsvoltage() -> Option<f64>: the voltage that completed agpio.wait_voltagelevel() -> Option<bool>: the level that completed agpio.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​
| Code | HTTP | When | Steps already run |
|---|---|---|---|
SEQUENCE_TIMED_OUT | 409 | A 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 finish | Keep their effect. If the time ran out before the first step, nothing runs |
SEQUENCE_ASSERTION_FAILED | 409 | A reading did not satisfy expect (gpio.read, power.*.read, gpio.pwm.measure) | Keep their effect |
ACCESS_REVOKED | 409 | Another 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_VALUE | 400 | A 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 long | Nothing runs |
OUT_OF_RANGE_VALUE | 400 | A number is outside its range: a pin, relay, port or supply number, a voltage, a duration, more than 65536 steps | Nothing runs |
INVALID_CONFIGURATION | 400 | A 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 pin | Nothing runs |
JSON_DESERIALIZATION | 400 | The request is not valid. The SDKs build valid requests, so this points to an SDK and firmware mismatch | Nothing runs |
CONFIGURATION_CONFLICT | 409 | A step uses a pin that a bus or PWM uses; an HMI step while an operator prompt is open | Nothing runs when the device finds it before the run |
DEVICE_NOT_OPEN | 409 | No session is open | Nothing runs |
INTERNAL_ERROR | 500 | A resource the sequence needs did not start, for example The HMI is not available | Nothing 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
ApiErrorwithcode,detail(the message),suggestionandpath - Python: device errors are an
ApiErrorwith the same fields.ACCESS_REVOKEDraisesAccessRevokedErrorandDEVICE_NOT_OPENraisesDeviceNotOpenError, both subclasses ofApiError - Rust:
ACCESS_REVOKEDisAt1000Error::Revoked(ApiError),DEVICE_NOT_OPENisAt1000Error::NotOpen(ApiError), and every other device error isAt1000Error::Api(ApiError).ApiError::codeis anErrorCode:ErrorCode::SequenceTimedOut,ErrorCode::SequenceAssertionFailed,ErrorCode::InvalidValue,ErrorCode::OutOfRangeValue,ErrorCode::InvalidConfiguration,ErrorCode::JsonDeserialization,ErrorCode::ConfigurationConflict
- NodeJS
- Python
- Rust
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;
}
}
from ikalogic_at1000 import ApiError
try:
(
tester.sequence()
.gpio.configure(1, mode="digital", direction="input", vil=0.8, vih=2.0)
.gpio.wait_level(1, "high", timeout=0.5)
.run()
)
except ApiError as err:
if err.code != "SEQUENCE_TIMED_OUT":
raise
print(err.detail) # Command 1 (`gpio.wait_level`): pin 1 still reads false
tester.reset()
use std::time::Duration;
use ikalogic_at1000::{At1000Error, ErrorCode, GpioConfiguration, WaitOptions};
let wait = WaitOptions { timeout: Some(Duration::from_millis(500)), ..Default::default() };
let seq = tester.sequence()
.gpio().configure(1, GpioConfiguration::DigitalInput { vil: 0.8, vih: 2.0 })
.gpio().wait_level(1, true, wait);
match seq.run(None) {
Ok(report) => println!("ready after {:?}", report.elapsed()),
Err(At1000Error::Api(err)) if err.code == ErrorCode::SequenceTimedOut => {
println!("{}", err.message); // Command 1 (`gpio.wait_level`): pin 1 still reads false
tester.reset()?;
}
Err(err) => return Err(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
Errorat once. A pin, relay, port or supply number the device does not have gives anErrorwhose message reads like the device'sRESOURCE_NOT_FOUNDerror. Adding more than 65536 steps also throws - Python: the step method raises
ValueError(including pydantic'sValidationError). A pin, relay, port or supply the device does not have raises anApiErrorwith codeRESOURCE_NOT_FOUND - Rust: an invalid step is not appended.
run()returnsAt1000Error::InvalidInputfor 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.
tester.gpio.sequence() is deprecated. It still works, but new code should use tester.sequence(): see Moving from gpio.sequence().