DK Studio/Documentation
ProductChangelog

Device topology and physical connection

The studio never touches hardware. Everything physical happens on the bench, through the agent process. This page introduces what the agent reaches β€” serial ports and the shell β€” how a bench is put together, and the naming conventions that keep a rack of benches intelligible.

The device under test

There are three layers, and only the innermost one is physical:

LayerWhere it runsWhat it does
StudioYour serverSends commands, shows results. No hardware access.
AgentThe bench that owns the deviceOpens ports, runs scripts, reports results.
DeviceThe benchThe DUT, the serial peer, the networked target.

The agent is the only process that opens a port or spawns a process on the bench. A device in DK Studio is therefore addressed the way the agent reaches it: a serial port path on the bench, or a host reachable over the network from the bench.

USB and serial

A USB-serial adapter plugged into the bench appears as a serial port. The agent finds it with the get_serial_ports command, whose result carries a JSON array in output.stdout:

[
  {
    "path": "/dev/tty.usbmodem14201",
    "manufacturer": "FTDI",
    "serialNumber": "FT9ABCDE",
    "vendorId": "0403",
    "productId": "6001"
  }
]

Each entry describes one adapter, and the fields are the adapter's identity: the path you open it by, its manufacturer, its serial number, and the USB vendor and product ids.

The serial bindings are optional

The agent loads serialport lazily. On a host without the native bindings β€” or without the USB permissions to see them β€” get_serial_ports succeeds but returns an empty array, and open_serial fails. If the list is empty, install the agent's dependencies on that bench and check that the user running the agent may read the port.

The serial commands are:

CommandArgsBehaviour
get_serial_portsnoneLists the serial ports the agent can see.
open_serialpath (required), baudRate (default 115200)Opens the port.
serial_writepath and data (both required)Writes bytes; refused if the port is not open or not writable.
close_serialpath (required)Closes the port.
{ "agentId": "6d1f…", "command": "open_serial",
  "args": { "path": "/dev/tty.usbmodem14201", "baudRate": 115200 } }
The socket path carries only `path` and `baudRate`

open_serial over the command API passes only path and baudRate to the port. Framing options β€” dataBits, stopBits and parity β€” are accepted by the agent's own local endpoint, POST /api/serial on port 3001, and not by the socket path. Over the socket, 8 data bits, 1 stop bit and no parity are the only settings you get.

The studio records serial metadata

Bytes arriving on an open port are handled on the agent and printed to its own console and local serial panel. The studio records the serial metadata β€” path, manufacturer, serial number, vendor id and product id β€” and receives it in device_event messages when a port connects or disconnects.

Networked targets

A target that speaks over IP β€” an ECU, a gateway, a DUT behind a router β€” is reached with the shell command. The script runs under /bin/bash on the bench:

ArgumentDefaultMeaning
scriptrequiredThe script text.
cwd/tmpThe working directory. The script is written to <cwd>/.agent_script_<timestamp>.sh, mode 0755, and run.

The timeout is not an argument: it travels beside args, as the request's own timeout, and defaults to 300000 milliseconds.

{ "agentId": "6d1f…", "command": "shell",
  "args": { "script": "ping -c 3 192.168.1.42 && curl -s http://192.168.1.42/health",
            "cwd": "/tmp" },
  "timeout": 15000 }

The result carries output.stdout, output.stderr, output.exitCode and output.durationMs. A timeout exits with -2 and sets stderr to Command timed out after <ms>ms; a script that cannot be spawned exits with -1.

Because the script runs on the bench, the bench needs the routes, credentials and DNS that the target requires. The studio is not on that network and does not need to be.

Test benches

A bench is a machine, an agent process, and the things plugged into it. A minimal bench needs:

ComponentRequirement
HostLinux or macOS with Node.js 24+, pnpm 11, and bash at /bin/bash.
AgentOne process, installed with ./install.sh so it runs as a service and survives a reboot.
LicenceOne licence per bench, issued from Admin β†’ Licences.
CredentialsThe licence code, entered once in the agent's registration form; the credentials the studio issues in return are stored in data/agent.json.
HardwareSerial adapters and DUTs, reachable as ports on the bench.

Issue one licence per bench. A licence is claimed once; presenting the same code again is refused, so a bench that is reinstalled keeps its identity only if it re-registers with the credentials it already holds β€” and a shared licence across a fleet makes revocation meaningless.

Keep `data/agent.json` out of backups you share

It holds the agent's API key and agent secret in plain text. It is also the file that makes a reinstalled bench come back with the same identity, so exclude it from image snapshots that get copied between machines, and restrict its permissions.

Naming conventions

Nothing in the product enforces a naming scheme, so pick one and apply it consistently β€” the agent list is only as readable as the names you give it.

ElementConventionExample
Agent name<site>-<room>-<bench>-<role>sh-b12-bench03-dut
Serial port pathThe stable device path, not /dev/ttyUSB0/dev/serial/by-id/usb-FTDI_FT9ABCDE-if00-port0
Device referenceThe port path or target host as seen from the bench192.168.1.42
LicenceOne per bench, named by the bench, not by the person holding itβ€”

Two rules that pay off:

  • Use the /dev/serial/by-id/ path, not /dev/ttyUSB0. The ttyUSB* numbering changes when adapters are replugged or reordered; the by-id path is derived from the adapter's serial number and does not.
  • Make the agent name the physical location. When a bench goes offline, the name is what tells you where to go.