Skip to content

Workflows

Learn common workflows and best practices for using the Roboscope 2 platform.

Complete workflow for conducting an inspection session.

Terminal window
curl -X POST http://localhost:8080/api/v1/spaces \
-H "Content-Type: application/json" \
-d '{
"key": "warehouse-a1",
"name": "Warehouse A1",
"description": "Main storage warehouse",
"calibration_vector": [0, 0, 0]
}'

Response: Space object with id

Terminal window
curl -X POST http://localhost:8080/api/v1/work-sessions \
-H "Content-Type: application/json" \
-d '{
"space_id": "SPACE_UUID",
"session_type": "inspection",
"status": "active",
"started_at": "2025-01-16T09:00:00Z"
}'

Response: Work session object with id

let sessionId = UUID(uuidString: "SESSION_UUID")!
// Join presence
try await PresenceService.shared.joinSession(sessionId)
// Start heartbeat
startHeartbeat(sessionId: sessionId)
// In AR view, capture 4 corner points
let corners: [SIMD3<Float>] = [point1, point2, point3, point4]
let marker = CreateMarker(
workSessionId: sessionId,
label: "Crack in wall",
p1: corners[0].toDoubleArray(),
p2: corners[1].toDoubleArray(),
p3: corners[2].toDoubleArray(),
p4: corners[3].toDoubleArray(),
color: "#FF0000",
customProps: [
"severity": AnyCodable("medium"),
"type": AnyCodable("crack"),
"inspector": AnyCodable("John Doe")
]
)
let created = try await MarkerService.shared.createMarker(marker)
Terminal window
# Auto-calculate all marker details for the session
curl -X POST http://localhost:8080/api/v1/work-sessions/SESSION_UUID/markers/calculate-details

Response: Count of calculated details

let update = UpdateWorkSession(
status: .done,
completedAt: Date(),
version: currentSession.version
)
let completed = try await WorkSessionService.shared.updateWorkSession(
id: sessionId,
update: update
)
try await PresenceService.shared.leaveSession(sessionId)
stopHeartbeat()
Terminal window
# Export for backup or transfer
curl http://localhost:8080/api/v1/work-sessions/SESSION_UUID/export \
-o inspection_session.json

Align a 3D model with a real-world LiDAR scan.

  • 3D model file (USDC/USDZ)
  • LiDAR-capable iOS device
  • Alignment server running
Terminal window
curl -X POST http://localhost:8080/api/v1/spaces \
-H "Content-Type: application/json" \
-d '{
"key": "room-101",
"name": "Conference Room 101",
"model_usdc_url": "https://storage.example.com/models/room101.usdc",
"calibration_vector": [0, 0, 0]
}'
import RealityKit
// Load USDC model
let modelEntity = try await ModelEntity(named: "room.usdc")
// Place model 1.5m in front of camera
let transform = simd_float4x4(
translation: SIMD3<Float>(0, 0, -1.5)
)
let anchor = AnchorEntity(world: transform)
anchor.addChild(modelEntity)
arView.scene.addAnchor(anchor)
// Start mesh reconstruction
captureSession.startScanning()
// Scan for 10-15 seconds to capture environment
// UI shows scan progress and mesh coverage
let progress: (Double) -> Void = { percent in
print("Export progress: \(percent)%")
}
let objData = try await captureSession.exportMeshData(
progress: progress,
completion: { data in
return data
}
)
let serverURL = "http://localhost:6000/align"
// POST OBJ data
let response = try await URLSession.shared.upload(
for: URLRequest(url: URL(string: serverURL)!),
from: objData
)
// Parse transformation matrix
let result = try JSONDecoder().decode(AlignmentResult.self, from: response)
let transformMatrix = result.matrix // [[Float; 4]; 4]
// Convert to simd_float4x4
let transform = simd_float4x4(transformMatrix)
// Update model anchor
anchor.transform.matrix = transform
// Visual inspection
// Model should align with real-world geometry
// Optional: Create markers to verify alignment accuracy
let verificationMarkers = createAlignmentVerificationMarkers()

Apply calibration to transform marker coordinates.

// Measure known reference point in AR
let referencePointAR = SIMD3<Float>(1.2, 0.5, 3.4)
// Known position in real-world coordinate system
let referencePointReal = SIMD3<Float>(0.0, 0.5, 0.0)
// Calculate calibration vector
let calibrationVector = referencePointReal - referencePointAR
// Result: [-1.2, 0.0, -3.4]
Terminal window
curl -X PATCH http://localhost:8080/api/v1/spaces/SPACE_UUID \
-H "Content-Type: application/json" \
-d '{
"calibration_vector": [-1.2, 0.0, -3.4]
}'
Terminal window
curl -X POST http://localhost:8080/api/v1/work-sessions/SESSION_UUID/calibrate-markers

Response: Array of markers with populated calibrated_data

func verifyCalibration(marker: Marker, calibrationVector: [Double]) {
guard let calibrated = marker.calibratedData else {
print("No calibrated data")
return
}
// Check that calibrated = raw + calibration vector
let expected = [
marker.p1[0] + calibrationVector[0],
marker.p1[1] + calibrationVector[1],
marker.p1[2] + calibrationVector[2]
]
let diff = abs(calibrated.p1[0] - expected[0])
print("Calibration difference: \(diff)m")
// Should be very small (<1mm)
}

Multiple users working on the same session.

// User A
try await PresenceService.shared.joinSession(
sessionId,
userId: "user-a",
userName: "Alice"
)
// Start heartbeat
startHeartbeat(sessionId: sessionId, userId: "user-a")
// User B
try await PresenceService.shared.joinSession(
sessionId,
userId: "user-b",
userName: "Bob"
)
// Start heartbeat
startHeartbeat(sessionId: sessionId, userId: "user-b")
let activeUsers = try await PresenceService.shared.listActiveUsers(sessionId)
// Returns: ["Alice", "Bob"]
let acquired = try await LockService.shared.acquireLock(
sessionId: sessionId,
userId: "user-a",
ttl: 60 // 60 second lock
)
if acquired {
// User A can now edit
print("Lock acquired")
} else {
print("Session is locked by another user")
}
// Create markers, update session, etc.
let marker = try await MarkerService.shared.createMarker(newMarker)
// Update session status
let updated = try await WorkSessionService.shared.updateWorkSession(
id: sessionId,
update: UpdateWorkSession(status: .active, version: session.version)
)
try await LockService.shared.releaseLock(
sessionId: sessionId,
userId: "user-a"
)
// Now User B can acquire the lock
let acquired = try await LockService.shared.acquireLock(
sessionId: sessionId,
userId: "user-b",
ttl: 60
)
do {
try await WorkSessionService.shared.updateWorkSession(
id: sessionId,
update: update
)
} catch APIError.conflict(let message) {
// Version mismatch - another user modified the session
// Refetch latest version
let latest = try await WorkSessionService.shared.getWorkSession(id: sessionId)
// Merge changes or prompt user
showConflictResolution(current: session, latest: latest)
// Retry with latest version
update.version = latest.version
try await WorkSessionService.shared.updateWorkSession(
id: sessionId,
update: update
)
}

Transfer sessions between spaces or environments.

Terminal window
curl http://localhost:8080/api/v1/work-sessions/SOURCE_SESSION_UUID/export \
-o session_export.json

Exported Data:

{
"session_type": "inspection",
"status": "done",
"started_at": "2025-01-16T09:00:00Z",
"completed_at": "2025-01-16T12:00:00Z",
"meta": {},
"markers": [
{
"label": "Issue 1",
"p1": [0, 0, 0],
"p2": [0.1, 0, 0],
"p3": [0.1, 0.1, 0],
"p4": [0, 0.1, 0],
"color": "#FF0000",
"custom_props": {
"severity": "high"
},
"details": {
"center_location_long": 0.05,
"center_location_cross": 0.05,
...
}
}
]
}
Terminal window
curl -X POST http://localhost:8080/api/v1/spaces \
-H "Content-Type: application/json" \
-d '{
"key": "target-space",
"name": "Target Space",
"calibration_vector": [0, 0, 0]
}'
Terminal window
curl -X POST http://localhost:8080/api/v1/work-sessions/import \
-H "Content-Type: application/json" \
-d '{
"space_id": "TARGET_SPACE_UUID",
"session": '$(cat session_export.json)'
}'

Result: New work session created with all markers and details

func exportAndImport(
sourceSessionId: UUID,
targetSpaceId: UUID
) async throws {
// 1. Export
let exported = try await WorkSessionService.shared.exportSession(
id: sourceSessionId
)
// 2. Import to target space
let imported = try await WorkSessionService.shared.importSession(
spaceId: targetSpaceId,
session: exported
)
print("Imported session: \(imported.id)")
}

Create multiple markers efficiently.

var markerBatch: [CreateMarker] = []
// Scan area and collect multiple marker positions
for defect in detectedDefects {
let marker = CreateMarker(
workSessionId: sessionId,
label: defect.label,
p1: defect.corners[0].toDoubleArray(),
p2: defect.corners[1].toDoubleArray(),
p3: defect.corners[2].toDoubleArray(),
p4: defect.corners[3].toDoubleArray(),
color: defect.severityColor,
customProps: [
"severity": AnyCodable(defect.severity),
"type": AnyCodable(defect.type),
"detected_at": AnyCodable(Date())
]
)
markerBatch.append(marker)
}
// Create all markers in one request
let created = try await MarkerService.shared.bulkCreateMarkers(markerBatch)
print("Created \(created.count) markers")
Terminal window
# Auto-calculate details for all markers
curl -X POST http://localhost:8080/api/v1/work-sessions/SESSION_UUID/markers/calculate-details

Benefits:

  • Single network request
  • Atomic operation
  • Better performance
  • Reduced overhead

Validate and verify marker data.

struct MarkerValidator {
static func validate(_ marker: CreateMarker) -> [String] {
var errors: [String] = []
// Check edge lengths
let edges = [
(marker.p1, marker.p2),
(marker.p2, marker.p3),
(marker.p3, marker.p4),
(marker.p4, marker.p1)
]
for (i, (a, b)) in edges.enumerated() {
let dist = distance(a, b)
if dist < 0.005 {
errors.append("Edge \(i+1) too short: \(dist)m")
}
}
// Check coordinate bounds
let allCoords = [marker.p1, marker.p2, marker.p3, marker.p4].flatMap { $0 }
for coord in allCoords {
if !coord.isFinite {
errors.append("Invalid coordinate: \(coord)")
}
}
return errors
}
static func distance(_ a: [Double], _ b: [Double]) -> Double {
let dx = a[0] - b[0]
let dy = a[1] - b[1]
let dz = a[2] - b[2]
return sqrt(dx*dx + dy*dy + dz*dz)
}
}
let errors = MarkerValidator.validate(newMarker)
if errors.isEmpty {
try await MarkerService.shared.createMarker(newMarker)
} else {
showValidationErrors(errors)
}
func verifyMarkerDetails(_ marker: Marker) async throws {
// Calculate details
try await MarkerDetailsService.shared.calculateDetails(markerId: marker.id)
// Fetch details
let details = try await MarkerDetailsService.shared.getDetails(markerId: marker.id)
// Verify size is reasonable
if details.longSize < 0.01 || details.longSize > 5.0 {
print("⚠️ Unusual length: \(details.longSize)m")
}
if details.crossSize < 0.01 || details.crossSize > 2.0 {
print("⚠️ Unusual width: \(details.crossSize)m")
}
}

// Include version in updates
let update = UpdateWorkSession(
status: .done,
completedAt: Date(),
version: session.version // ✅ Include version
)
// Handle conflicts
do {
try await service.updateWorkSession(id: id, update: update)
} catch APIError.conflict {
// Refetch and retry
}
class SessionManager {
private var sessionId: UUID?
func startSession(_ id: UUID) async {
self.sessionId = id
try? await PresenceService.shared.joinSession(id)
}
func endSession() async {
if let id = sessionId {
try? await PresenceService.shared.leaveSession(id)
}
}
deinit {
// Ensure cleanup
Task {
await endSession()
}
}
}
// ❌ Slow - multiple network requests
for marker in markers {
try await service.createMarker(marker)
}
// ✅ Fast - single request
try await service.bulkCreateMarkers(markers)
Terminal window
# ✅ Efficient - calculate all at once
curl -X POST /api/v1/work-sessions/{id}/markers/calculate-details
# ❌ Inefficient - calculate individually
curl -X POST /api/v1/markers/{id1}/details/calculate
curl -X POST /api/v1/markers/{id2}/details/calculate
func loadSessionWithRetry(id: UUID, maxRetries: Int = 3) async throws -> WorkSession {
var lastError: Error?
for attempt in 1...maxRetries {
do {
return try await WorkSessionService.shared.getWorkSession(id: id)
} catch {
lastError = error
if attempt < maxRetries {
try await Task.sleep(nanoseconds: UInt64(attempt) * 1_000_000_000)
}
}
}
throw lastError ?? APIError.networkError(NSError())
}