TofuPilotTofuPilot

Units

Track serial numbers, part numbers, and revision history for every unit under test. View full test history and traceability per unit in TofuPilot.

Unit under test header

Overview

A Unit is a physical object being tested. Each Unit has a unique serial number and is defined by part number and revision for complete traceability.

In TofuPilot, we use "Unit" as the standard term. However, you may also see "Unit under test" (UUT) or "Device under test" (DUT) in OpenHTF and other testing frameworks. All refer to the same concept.

Create Units

You can create units automatically when creating a Run, or directly with the API client.

Parameters

  • OpenHTF: Define parameters in the Test constructor (part_number, revision, batch_number) and pass the serial number to test.execute().
  • Python: Use client.units.create() to define serial_number, part_number, revision_number, and batch_number.
PropTypeDefault
unit_under_test?
dict
serial_number?
str
part_number?
str
revision?
str (optional)
A
batch_number?
str (optional)
from openhtf import Test
from tofupilot.openhtf import TofuPilot

def main():
  test = Test(
      procedure_id="FVT1",
      part_number="PCB01",             # required
      revision="A",                    # optional
      batch_number="12-24",            # optional
  )
  with TofuPilot(test):
      test.execute(lambda: "SN-0001")  # Unit serial number (required)

if __name__ == "__main__":
  main()
from tofupilot.v2 import TofuPilot

client = TofuPilot()

# Create a unit directly
unit = client.units.create(
  serial_number="SN-0001",
  part_number="PCB01",
  revision_number="A",
)
print(f"Unit created: {unit.serial_number}")
using TofuPilot;
using TofuPilot.Models.Requests;

var client = new TofuPilot();

var unit = await client.Units.CreateAsync(new UnitCreateRequest
{
  SerialNumber = "SN-0001",
  PartNumber = "PCB01",
  RevisionNumber = "A",
});
Console.WriteLine($"Unit created: {unit.SerialNumber}");

With OpenHTF, you can decide whether to assign the serial number at the beginning of the test or later during execution. For more details, see the Operator UI section in the OpenHTF documentation.

Batch Number

Include the batch_number field in your script. TofuPilot will display it with the run and let you filter analytics by batch.

Batch number in run metadata

Revision

Specify the revision field. If omitted, revision A is assumed. Revisions are shown in the Inventory and Unit pages, and can be used to segment your analytics.

Revision displayed in inventory

Sub-units

Sub-units are smaller Units that get assembled into a larger Unit. Each sub-unit has its own serial number and test history.

  • OpenHTF: Define sub_units parameter in the Test constructor as a list of dictionaries with serial_number of each sub-unit.
  • Python: Use client.units.add_child() to attach previously tested sub-units by serial_number.
PropTypeDefault
sub_units?
array (optional)
serial_number?
str (optional)
from openhtf import PhaseResult, Test
from tofupilot.openhtf import TofuPilot

# Please ensure both units PCB1A001 and LEN1A001 exist before running this script

def main():
  test = Test(
      procedure_id="FVT2",  # Create the procedure first in the Dashboard
      part_number="CAM1",
      sub_units=[{"serial_number": "PCB1A001"},
                  {"serial_number": "LEN1A001"}],
  )
  with TofuPilot(test):
      test.execute(lambda: "CAM1A001")


if __name__ == "__main__":
  main()
from tofupilot.v2 import TofuPilot

client = TofuPilot()

# Add sub-units to a parent unit
client.units.add_child(serial_number="CAM1A001", child_serial_number="PCB1A001")
client.units.add_child(serial_number="CAM1A001", child_serial_number="LEN1A001")

# Remove a sub-unit
client.units.remove_child(serial_number="CAM1A001", child_serial_number="LEN1A001")
using TofuPilot;
using TofuPilot.Models.Requests;

var client = new TofuPilot();

// Add sub-units to a parent unit
await client.Units.AddChildAsync("CAM1A001", new UnitAddChildRequestBody { ChildSerialNumber = "PCB1A001" });
await client.Units.AddChildAsync("CAM1A001", new UnitAddChildRequestBody { ChildSerialNumber = "LEN1A001" });

// Remove a sub-unit
await client.Units.RemoveChildAsync("CAM1A001", "LEN1A001");

The sub-units must already exist as Units in TofuPilot when creating the Run. Ensure each sub-unit has been tested and registered before referencing it in the assembly.

Attachments

You can attach files like photos, calibration data, or diagnostic logs directly to a unit.

from tofupilot.v2 import TofuPilot

client = TofuPilot()

# Upload and attach a file to a unit
upload_id = client.attachments.upload("data/pcb-inspection.jpg")
client.units.update(serial_number="SN-0001", attachments=[upload_id])

# Attach multiple files at once
ids = [client.attachments.upload(f) for f in ["data/pcb-inspection.jpg", "data/calibration.csv"]]
client.units.update(serial_number="SN-0001", attachments=ids)
using TofuPilot;
using TofuPilot.Models.Requests;

var client = new TofuPilot();

// Upload and attach a file to a unit
var uploadId = await client.Attachments.UploadAsync("data/pcb-inspection.jpg");
await client.Units.UpdateAsync("SN-0001", new UnitUpdateRequestBody { Attachments = new List<string> { uploadId } });

// Attach multiple files at once
var files = new[] { "data/pcb-inspection.jpg", "data/calibration.csv" };
var ids = new List<string>();
foreach (var f in files)
  ids.Add(await client.Attachments.UploadAsync(f));
await client.Units.UpdateAsync("SN-0001", new UnitUpdateRequestBody { Attachments = ids });

Supported formats include images (JPEG, PNG), documents (PDF, CSV, TXT), and any binary file.

Browse & Filter Units

You can browse and filter units from the Dashboard or with the API client.

from tofupilot.v2 import TofuPilot

client = TofuPilot()

# List all units for a part number
units = client.units.list(part_numbers=["PCB01"])
for u in units.data:
  print(f"{u.serial_number}")

# Filter by batch
units = client.units.list(batch_numbers=["2024-001"])

# Get a specific unit
unit = client.units.get(serial_number="SN-0001")
print(f"Serial: {unit.serial_number}")
print(f"Part: {unit.part.number} rev {unit.part.revision.number}")
using TofuPilot;

var client = new TofuPilot();

// List all units for a part number
var units = await client.Units.ListAsync(partNumbers: new List<string> { "PCB01" });
foreach (var u in units.Data)
  Console.WriteLine(u.SerialNumber);

// Filter by batch
var batchUnits = await client.Units.ListAsync(batchNumbers: new List<string> { "2024-001" });

// Get a specific unit
var unit = await client.Units.GetAsync("SN-0001");
Console.WriteLine($"Serial: {unit.SerialNumber}");

Download Attachments

You can download attachments from a unit.

from tofupilot.v2 import TofuPilot

client = TofuPilot()

unit = client.units.get(serial_number="SN-0001")

for a in unit.attachments:
  client.attachments.download(a)
  print(f"Downloaded {a.name} ({a.size} bytes)")
using TofuPilot;

var client = new TofuPilot();

var unit = await client.Units.GetAsync("SN-0001");

foreach (var a in unit.Attachments)
{
  await client.Attachments.DownloadAsync(a.DownloadUrl, a.Name);
  Console.WriteLine($"Downloaded {a.Name} ({a.Size} bytes)");
}

Update Units

You can update units from the Dashboard or with the API client.

Modify unit metadata

from tofupilot.v2 import TofuPilot

client = TofuPilot()

# Update serial number, part, revision, or batch
client.units.update(
  serial_number="SN-0001",
  new_serial_number="SN-0002",
  part_number="PCB02",
  revision_number="B",
)
using TofuPilot;
using TofuPilot.Models.Requests;

var client = new TofuPilot();

await client.Units.UpdateAsync("SN-0001", new UnitUpdateRequestBody
{
  NewSerialNumber = "SN-0002",
  PartNumber = "PCB02",
  RevisionNumber = "B",
});

Delete Units

You can delete units from the Dashboard or with the API client.

from tofupilot.v2 import TofuPilot

client = TofuPilot()

client.units.delete(serial_numbers=["SN-0001"])
using TofuPilot;

var client = new TofuPilot();

await client.Units.DeleteAsync(new List<string> { "SN-0001" });

Unit Activity

Track unit activity on the unit’s page in the Dashboard. Shows all tests performed on the unit, grouped by Procedure name, and changes made when creating Runs.

Unit and sub-unit view with their serial numbers

How is this guide?