Device access control
An AT1000 drives real hardware - relays, power rails, GPIO. If two test PCs pushed conflicting states at the same time, the result would be undefined. AT1000 solves this with a simple open-first access model: exactly one controller holds the device at a time.
Why a session is needed​
Every controller must open() the device before driving hardware. The server mints an opaque session token and records it as the active token - exactly one is active at any moment. The SDK attaches that token to every request; state-changing calls from any other controller are rejected.
Read-only access (discovery, monitoring, reading state, subscribing to events) needs no token at all, so observers never interfere with the controller.
Last-open-wins takeover​
There is no queue, no negotiation, and no way to hold the device against a newcomer. A later open() - from any client, anywhere - unconditionally replaces the active token. The previous holder is revoked and finds out on its next hardware call.
Client A: open() → token a1 (A controls the device)
Client B: open() → token b2 (B controls the device; a1 revoked)
Client A: relay.close() → 409 ACCESS_REVOKED (taken over by B)
Tokens never expire by time. A token dies only by replacement (a later open()), a front-panel reclaim (see below), a project container exiting, or a server restart. A crashed or finished controller leaves only an inert token behind, which the next open() silently replaces - there is nothing to release.
Read-only monitoring​
To watch a device without ever taking control, open it read-only. A read-only client skips the session open, sends no session header, and issues only ungated (GET) calls. All monitoring endpoints and the entire management surface (device info, projects, events) stay open, with or without a token:
- NodeJS
- Python
const monitor = await AT1000.open(host, { readonly: true });
const state = await monitor.power.dut(0).read(); // GET - always allowed
monitor = AT1000.open(host, readonly=True)
state = monitor.power.dut(0).read() # GET - always allowed
Handling revocation​
When your controller has been taken over, the next hardware call raises a structured error. In JavaScript this is an ApiError whose code is either ACCESS_REVOKED (a token the server minted and later replaced) or DEVICE_NOT_OPEN (no token, or an unknown one). In Python these surface as the dedicated AccessRevokedError and DeviceNotOpenError exceptions:
- NodeJS
- Python
import { AT1000, ApiError } from '@ikalogic/at1000';
try {
await tester.relays.relay(0).close();
} catch (err) {
if (err instanceof ApiError && err.code === 'ACCESS_REVOKED') {
console.log('Device was taken over by another controller.');
// Re-open to take the device back (itself a takeover):
tester = await AT1000.open(host);
} else if (err instanceof ApiError && err.code === 'DEVICE_NOT_OPEN') {
console.log('No session - call AT1000.open() first.');
} else {
throw err;
}
}
from ikalogic_at1000 import AT1000, AccessRevokedError, DeviceNotOpenError
try:
tester.relays.relay(0).close()
except AccessRevokedError:
print("Device was taken over by another controller.")
# Re-open to take the device back (itself a takeover):
tester = AT1000.open(host)
except DeviceNotOpenError:
print("No session - call AT1000.open() first.")
Checking who holds the device​
tester.session.state() reports the current holder - never the token. It returns the holder's kind (remote for an SDK/interface controller, project for an in-container test sequence), label, and since timestamp, or just active: false when nobody holds the device:
- NodeJS
- Python
const state = await tester.session.state();
if (state.active) {
console.log(`Held by ${state.label} (${state.kind})`);
} else {
console.log('Device is free.');
}
state = tester.session.state()
if state.active:
print(f"Held by {state.label} ({state.kind})")
else:
print("Device is free.")
Reclaiming from the front panel​
While a remote session is active, the device's front-panel menu is locked so knob rotations reach the controlling script instead of the menu. To take the device back locally, hold the knob for 5 seconds: this invalidates the active token (the remote controller's next call then fails with ACCESS_REVOKED) and restores the front-panel menu. It is the operator's local equivalent of last-open-wins.