Offline Test Data Sync with TofuPilot
Not every test station has an internet connection. Cleanrooms, field deployments, secure facilities, and factory floors with spotty WiFi all need to capture test data reliably. TofuPilot's Python client handles offline scenarios by letting you store results locally and sync when connectivity returns.
When Offline Testing Happens
| Scenario | Why it's offline |
|---|---|
| Air-gapped facility | Security requirements prohibit internet access |
| Field testing | Remote location with no connectivity |
| Cleanroom | No network access inside the controlled environment |
| Factory floor | Unreliable WiFi, can't depend on it for every test |
| Mobile test station | Traveling between sites |
In all these cases, the test must run and record data regardless of network status. You can't tell a production line to stop because the WiFi is down.
Architecture for Offline-First Testing
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Test Station │────▶│ Local Store │────▶│ TofuPilot │
│ (runs tests) │ │ (JSON files) │ │ (cloud) │
└──────────────┘ └──────────────┘ └──────────────┘
Offline When connected
The test station writes results to local storage. A sync process uploads them to TofuPilot when a connection is available.
Step 1: Store Results Locally
Write test results to JSON files on the local machine. Each file represents one test run.
import json
import os
from datetime import datetime
OFFLINE_DIR = "/data/test-results/pending"
os.makedirs(OFFLINE_DIR, exist_ok=True)
def run_test(serial_number):
"""Run the test and store results locally."""
# Run your actual test measurements
vcc = measure_voltage(channel=1)
current = measure_current()
result = {
"procedure_id": "BOARD-FUNCTIONAL-V3",
"unit_under_test": {"serial_number": serial_number},
"run_passed": 3.25 <= vcc <= 3.35 and 30 <= current <= 60,
"tested_at": datetime.utcnow().isoformat(),
"steps": [{
"name": "Power Rail Check",
"step_type": "measurement",
"status": 3.25 <= vcc <= 3.35,
"measurements": [{
"name": "vcc_3v3",
"value": vcc,
"unit": "V",
"limit_low": 3.25,
"limit_high": 3.35,
}],
}, {
"name": "Current Draw",
"step_type": "measurement",
"status": 30 <= current <= 60,
"measurements": [{
"name": "idle_current_ma",
"value": current,
"unit": "mA",
"limit_low": 30,
"limit_high": 60,
}],
}],
}
filename = f"{serial_number}_{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}.json"
filepath = os.path.join(OFFLINE_DIR, filename)
with open(filepath, "w") as f:
json.dump(result, f, indent=2)
print(f"Result saved locally: {filepath}")
return result
# Run tests regardless of network status
run_test("UNIT-5501")
run_test("UNIT-5502")Step 2: Sync When Connected
A separate sync script uploads pending results to TofuPilot and moves them to a "synced" directory.
import json
import os
import shutil
from tofupilot import TofuPilotClient
PENDING_DIR = "/data/test-results/pending"
SYNCED_DIR = "/data/test-results/synced"
os.makedirs(SYNCED_DIR, exist_ok=True)
def sync_pending_results():
"""Upload all pending results to TofuPilot."""
client = TofuPilotClient()
pending_files = sorted(os.listdir(PENDING_DIR))
if not pending_files:
print("No pending results to sync.")
return
print(f"Syncing {len(pending_files)} pending results...")
for filename in pending_files:
filepath = os.path.join(PENDING_DIR, filename)
with open(filepath) as f:
result = json.load(f)
try:
client.create_run(
procedure_id=result["procedure_id"],
unit_under_test=result["unit_under_test"],
run_passed=result["run_passed"],
steps=result["steps"],
)
# Move to synced directory
shutil.move(filepath, os.path.join(SYNCED_DIR, filename))
print(f"Synced: {filename}")
except Exception as e:
print(f"Failed to sync {filename}: {e}")
# Leave in pending for retry
remaining = len(os.listdir(PENDING_DIR))
print(f"Sync complete. {remaining} files still pending.")
sync_pending_results()Step 3: Automate the Sync
Run the sync script automatically when connectivity is available.
Option A: Cron Job
# Try to sync every 15 minutes
*/15 * * * * /usr/bin/python3 /opt/test-station/sync_results.py >> /var/log/test-sync.log 2>&1Option B: Network Event Trigger
import subprocess
import time
def has_internet():
try:
subprocess.check_call(
["curl", "-s", "--max-time", "5", "https://app.tofupilot.com/api/health"],
stdout=subprocess.DEVNULL,
)
return True
except subprocess.CalledProcessError:
return False
while True:
if has_internet():
subprocess.run(["python3", "sync_results.py"])
time.sleep(300) # Check every 5 minutesData Integrity
Offline sync introduces a risk: what if a file gets corrupted or a sync partially fails?
| Risk | Mitigation |
|---|---|
| File corruption | Write to temp file first, then rename (atomic write) |
| Duplicate upload | TofuPilot deduplicates by procedure + serial + timestamp |
| Partial sync failure | Files stay in pending until successfully uploaded |
| Clock drift on offline machines | Use monotonic timestamps for ordering, UTC for absolute time |
When to Use This Pattern
Use offline-first testing when:
- Your test station can't guarantee network connectivity
- Network latency would slow down test cycle time
- You need tests to run even during network outages
- Security requirements prevent direct cloud access from the test floor
For stations with reliable connectivity, push results directly to TofuPilot from within the test script. The offline pattern adds complexity that's only justified when connectivity is unreliable.