DK Studio/Documentation
ProductChangelog

Environments, devices and vehicles

The lab is made of physical things: a bench, a device under test, a car on a rig, a manufacturer. DK Studio keeps those things as inventory records so that a team can find the rig it needs and say what is on it. This page introduces the asset model, what an environment is, how a device is described, and how naming and tagging let you find a rig in a large lab.

The asset model

Six tables describe the world a test bench lives in. The setup wizard creates them in the initialize step, identically on SQLite, PostgreSQL and MySQL.

TableKey columnsPoints at
projectsid, name, description, statusβ€”
productsid, name, description, project_idprojects
environmentsid, name, type, config, project_idprojects
devicesid, kind_id, device_id, serial, display_name, statusdevice_kinds
vehiclesid, name, make, model, year, vin, statusβ€”
oemsid, name, code, descriptionβ€”

A project is the top of the tree. Products and environments hang off it through project_id; devices, vehicles and OEMs are the free-standing inventory of the lab that those projects draw on.

ScreenWhat it lists
/projectsThe programmes the lab is working on, with their status.
/productsThe products under test inside each project.
/environmentsThe rigs, labs and campaigns.
/devicesThe devices under test.
/vehiclesThe vehicles on the rigs.
/oemsThe manufacturers.

Environments

An environment is a named, typed context for work: a lab, a bench, a vehicle, or a test campaign.

ColumnTypeMeaning
idtextPrimary key.
nametext, not nullThe human handle. Not unique.
typetext, not nullWhat kind of environment: a lab, a bench, a vehicle, a campaign.
configtextThe configuration of the rig, as JSON.
project_idtextForeign key to projects.

config is where the rig is described: the benches it contains, the device addresses on them, the serial ports, and the vehicles it carries. It is the one place where the composition of an environment is written down, so it is also the place to look when you want to know what is physically in front of you.

An environment and an agent are two views of the same bench

The environment record describes the rig β€” its name, its type and its configuration. The agent record describes the machine that is actually connected to the studio and can be commanded. Name them after each other β€” environment bench-01, agent bench-01 β€” and the two views line up.

Devices under test

A device is the piece of hardware under test. It is observed, not registered: the studio learns a device exists when a bench reports it, and keeps one registry row for that device from then on.

The registry is split from the catalogue of what a device can be.

The kinds

device_kinds is the catalogue of device classes. Each row describes a kind of hardware β€” the model that every phone of that kind shares, the transport the bench talks to it over, and the capabilities it offers.

ColumnTypeMeaning
idtextPrimary key. ios, android, harmonyos, and later sbm, sam, robot_arm.
nametext, not nullThe human name of the kind.
categorytext, not nullmobile, module or robot.
vendortextWho makes it.
modeltextWhich model this kind is.
transporttextHow the bench reaches it: usbmux, adb, hdc, serial, can, tcp.
capabilitiestextJSON array of what the kind can do.
spectextJSON, the fields specific to this kind.

Adding a new kind of device is adding a row here.

The registry

devices holds one row per physical unit.

ColumnTypeMeaning
idtextPrimary key. Studio-assigned, stable, and the key that history rows point at.
kind_idtextWhich kind it is.
device_idtextThe address the bench uses: a UDID, a serial, a target id.
serialtextThe nameplate serial. May be empty.
display_nametextThe human handle.
statustextin_service, repair or retired.
firmwaretextThe firmware it runs.
notestextFree text.
first_seen_attextWhen the studio first saw it.
last_seen_attextWhen the studio last heard about it.
retired_attextWhen it left service.

The pair (kind_id, device_id) is unique. That is what makes the registry a registry: when a bench reports a device the studio has seen before, it claims the existing row rather than creating a second one. Moving a phone from one bench to another does not change its identity β€” only where it sits.

status is the device's own lifecycle, and it says nothing about whether the device is plugged in right now. A retired device keeps its row, because reports and history still refer to it.

The per-unit details

device_details_phone holds the columns that belong to one machine rather than to the kind.

ColumnTypeMeaning
device_idtextThe device it describes.
product_typetextThe product type string the platform reports.
product_versiontextThe product version.
os_versiontextThe OS version it is running.
connection_typetextHow it is connected.

The model belongs to the kind, but the OS version belongs to this unit β€” a machine gets upgraded. Each kind has its own details table of this shape.

Where it is

device_sessions records where a device has been, one row per stretch of time it was on a bench.

ColumnTypeMeaning
idtextPrimary key.
device_idtextThe device.
agent_idtextThe bench it is on.
slottextThe position on the bench.
roletextdut, companion or monitor.
statustextattached, detached or closed.
attached_attextWhen it was reported.
detached_attextWhen the bench reported it gone.
closed_attextWhen the stretch truly ended.
end_reasontextmoved, replaced, disconnected, lost or retired.
driver_statetextJSON snapshot of the driver state.

A disconnect is not the end of the stretch. A bench that loses its socket reports the device as detached and the studio holds that row open for a grace period; if the device comes back inside the window it is the same row, with detached_at cleared. Only when the window passes does the stretch close, and the reason says which kind of ending it was β€” the device moved to another bench, the bench lost the device, or the bench itself went away and took the device with it.

The device page reads that history three ways, and the choice is remembered: a list of stretches with the time each one ran, a timeline that lays the stretches on a time axis so the gaps between them show β€” each stretch marked with the point it connected and the point it dropped β€” and a stream of the connect and disconnect events themselves.

agent_sessions is the same idea for benches: when an agent connected, when it went away, when the stretch truly ended, and the machine identity it reported at handshake time.

The agent page reads that history the same way the device page does β€” a list of stretches, a timeline of the gaps between them, and a stream of the connect and disconnect events β€” and adds the two numbers that matter at a glance: how long the agent has been connected in total, and how many times it has dropped. Each stretch carries the machine identity the agent reported when it connected, so a bench that was moved to another machine says so.

The screens

ScreenWhat it is for
/devicesThe inventory, open to everyone. Every device the studio knows, with its kind, model, OS version, current bench and service state, filterable by kind, state and bench.
/devices/<id>One device in full: its record, and the timeline of every stretch it has spent on a bench, with the reason each one ended.
/admin/devicesThe registry, for admins. The same list plus the lifecycle actions β€” send a unit to repair, return it to service, retire it.

Every row and card carries two states side by side: presence β€” on a bench, reported gone, or not on a bench β€” and the device's own service state, in service, in repair, or retired. Presence is whether the bench reports the device plugged in right now; service state is whether it can be used at all. A retired device can still be sitting on a bench, and a device in repair is not on any bench.

The inventory keeps itself current: when a bench reports a device plugged in or unplugged, the row follows without a reload.

Both lists read either way: a table for scanning and comparing, a grid of cards for one device at a time. The choice is remembered, so the list looks the same the next time you open it.

A device opens its own record from wherever you meet it: the name is a link, and so is the row it sits in. On a bench page the click lands on the same record, with the same presence history.

The inventory answers "what do we have and where is it right now". The registry answers "is it still fit to be used", and it is the one screen that changes a device's life. A device that has been sent to repair keeps its row and its history; it simply stops being counted as in service.

Vehicles

A vehicle is a car on a rig.

ColumnTypeMeaning
nametext, not nullThe human handle. Not unique.
maketext, not nullManufacturer name.
modeltext, not nullModel name.
yeartextModel year.
vintextVehicle identification number β€” the handle you quote when you mean one specific car.

A vehicle is attached to an environment through the environment's config: record the vin of the car that sits in that rig, and the environment carries it. The same convention applies to a vehicle-only environment β€” an environment of type vehicle describes the car it is built around, and the vehicle record carries the details.

OEMs

An OEM is a manufacturer: name, a unique code, and an optional description.

ColumnTypeMeaning
nametext, not nullThe manufacturer name, matching the make on its vehicles.
codetextShort key, unique across the table.
descriptiontextOptional notes.

code is the short handle to use when you name things and when you write configuration β€” it is the one field in the asset model that is guaranteed to be unique, so it is the one you can key on.

Naming and tagging

name is the handle on every asset table, and it is the first thing your team sees in a list. Put the same information in it every time, in the same order, so that a name can be read at a glance and searched for without opening a record.

A convention that works across a lab:

PartExampleWhy
Locationbench-02Which bench in the room.
Role-dut, -rig, -hmiWhat the asset is for: device under test, rig, head unit.
Tag-icc, -ccc, -fleet-bThe protocol, the campaign, or the variant being tested.

So bench-02-dut-icc is the ICC device under test on bench 02, and bench-02-rig-ccc is the CCC rig on the same bench. The same convention applied to environments, devices and agents means that a search for bench-02 returns everything that lives on that bench, and a search for -icc returns every ICC asset in the lab.

Tags are names, so pick them once

Because the tag lives in the name, it is only a tag if everyone writes it the same way. Agree the suffixes before the lab grows, and keep to them β€” renaming half the inventory later costs more than the convention did.

How to verify

  • Open /environments and check that each rig has a type that says what it is and a config that lists what is on it.
  • Open /devices and look for the unit on your bench: its kind, its device_id, its OS version and which bench it is sitting on.
  • Open the unit itself and read its presence history β€” every stretch it has spent on a bench, and why each one ended.
  • Open /admin/devices as an admin, send a unit to repair, and watch it counted as out of service in /devices.
  • Move a device to another bench and back β€” it keeps one registry row and gains a new stretch in its history.
  • Compare the inventory with the live pool: GET /api/agents shows which benches are actually connected, and the agent name should match the environment name.