Data Model
Data Model Reference
Section titled “Data Model Reference”Overview
Section titled “Overview”The Roboscope 2 data model consists of four core entities that work together to represent spatial inspection data:
- Spaces - Physical locations or rooms
- Work Sessions - Inspection or repair activities
- Markers - 3D spatial annotations
- Marker Details - Measurement data for markers
Entity Relationships
Section titled “Entity Relationships”Space (1) ────┬───→ (N) Work Session │ └───→ (1) Calibration Vector
Work Session (1) ───→ (N) Marker
Marker (1) ───→ (0..1) Marker DetailsSpaces
Section titled “Spaces”Represents a physical location or room where work sessions take place.
Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | Unique identifier |
key | String | Yes | Human-readable unique key (e.g., “room-101”) |
name | String | Yes | Display name |
description | String | No | Detailed description |
model_glb_url | String | No | URL to GLB model file |
model_usdc_url | String | No | URL to USDC model file |
preview_url | String | No | URL to preview image |
scan_url | String | No | URL to LiDAR scan data |
calibration_vector | [f64; 3] | Yes | Translation vector for coordinate calibration |
meta | JSON | No | Arbitrary metadata |
created_at | Timestamp | Yes | Creation time |
updated_at | Timestamp | Yes | Last update time |
Calibration Vector
Section titled “Calibration Vector”The calibration_vector is a 3D translation vector [x, y, z] used to transform raw AR coordinates into a calibrated coordinate system.
Default: [0.0, 0.0, 0.0] (no transformation)
Example: [1.5, 0.0, 2.0] translates all markers by +1.5m in X and +2.0m in Z
Example
Section titled “Example”{ "id": "550e8400-e29b-41d4-a716-446655440000", "key": "warehouse-a1", "name": "Warehouse A1", "description": "Main warehouse storage area", "model_glb_url": "https://storage.example.com/models/warehouse-a1.glb", "model_usdc_url": "https://storage.example.com/models/warehouse-a1.usdc", "preview_url": "https://storage.example.com/previews/warehouse-a1.jpg", "scan_url": "https://storage.example.com/scans/warehouse-a1.obj", "calibration_vector": [1.5, 0.0, 2.0], "meta": { "building": "North Facility", "floor": 1 }, "created_at": "2025-01-15T10:30:00Z", "updated_at": "2025-01-15T10:30:00Z"}Work Sessions
Section titled “Work Sessions”Represents an inspection, repair, or other work activity within a space.
Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | Unique identifier |
space_id | UUID | Yes | Foreign key to space |
session_type | Enum | Yes | Type: inspection, repair, other |
status | Enum | Yes | Status: draft, active, done, archived |
started_at | Timestamp | No | When work started |
completed_at | Timestamp | No | When work completed |
version | i64 | Yes | Optimistic concurrency version |
meta | JSON | No | Arbitrary metadata |
created_at | Timestamp | Yes | Creation time |
updated_at | Timestamp | Yes | Last update time |
Session Types
Section titled “Session Types”inspection: Assessment or survey workrepair: Maintenance or fix activityother: Custom work type
Session Status
Section titled “Session Status”draft: Being prepared, not yet startedactive: Currently in progressdone: Completed successfullyarchived: Historical record
Optimistic Concurrency
Section titled “Optimistic Concurrency”The version field enables optimistic locking. When updating:
- Client reads current version
- Client sends update with version number
- Server only applies if version matches
- Server increments version on success
Example
Section titled “Example”{ "id": "660e8400-e29b-41d4-a716-446655440001", "space_id": "550e8400-e29b-41d4-a716-446655440000", "session_type": "inspection", "status": "active", "started_at": "2025-01-16T09:00:00Z", "completed_at": null, "version": 3, "meta": { "inspector": "John Doe", "weather": "clear" }, "created_at": "2025-01-16T08:55:00Z", "updated_at": "2025-01-16T09:15:00Z"}Markers
Section titled “Markers”3D spatial annotations placed in AR. Each marker is defined by four corner points forming a quadrilateral.
Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
id | UUID | Yes | Unique identifier |
work_session_id | UUID | Yes | Foreign key to work session |
label | String | No | Display label |
p1 | [f64; 3] | Yes | First corner point [x, y, z] |
p2 | [f64; 3] | Yes | Second corner point [x, y, z] |
p3 | [f64; 3] | Yes | Third corner point [x, y, z] |
p4 | [f64; 3] | Yes | Fourth corner point [x, y, z] |
color | String | No | Hex color code (e.g., “#FF0000”) |
version | i64 | Yes | Optimistic concurrency version |
meta | JSON | No | Arbitrary metadata |
custom_props | JSON | No | Domain-specific properties |
calibrated_data | JSON | No | Calibrated point coordinates |
created_at | Timestamp | Yes | Creation time |
updated_at | Timestamp | Yes | Last update time |
Point Array Format
Section titled “Point Array Format”Each point is a 3D coordinate in meters:
[x, y, z]Coordinate System (ARKit conventions):
- X-axis: Right (positive) / Left (negative)
- Y-axis: Up (positive) / Down (negative)
- Z-axis: Forward (negative) / Backward (positive)
Calibrated Data
Section titled “Calibrated Data”When a space has a calibration vector, markers can store calibrated coordinates:
{ "p1": [x, y, z], "p2": [x, y, z], "p3": [x, y, z], "p4": [x, y, z], "center": [x, y, z]}Calibrated coordinates = Raw coordinates + Space calibration vector
Custom Properties
Section titled “Custom Properties”Store arbitrary domain-specific metadata:
{ "severity": "high", "category": "structural", "inspector": "Jane Smith", "priority": 1, "tags": ["crack", "urgent"], "measurements": { "width_mm": 5.2, "depth_mm": 12.5 }}Validation
Section titled “Validation”Markers must pass validation:
- All four edge lengths ≥ 5mm
- Points must form a valid quadrilateral
- Coordinates must be finite numbers
Example
Section titled “Example”{ "id": "770e8400-e29b-41d4-a716-446655440002", "work_session_id": "660e8400-e29b-41d4-a716-446655440001", "label": "Crack in north wall", "p1": [-0.230, 0.060, 0.030], "p2": [-0.165, 0.057, 0.037], "p3": [-0.167, 0.050, 0.262], "p4": [-0.232, 0.053, 0.255], "color": "#FF0000", "version": 1, "meta": {}, "custom_props": { "severity": "medium", "type": "crack", "inspector": "John Doe" }, "calibrated_data": { "p1": [1.270, 0.060, 2.030], "p2": [1.335, 0.057, 2.037], "p3": [1.333, 0.050, 2.262], "p4": [1.268, 0.053, 2.255], "center": [1.302, 0.055, 2.146] }, "created_at": "2025-01-16T09:30:00Z", "updated_at": "2025-01-16T09:30:00Z"}Marker Details
Section titled “Marker Details”One-to-one measurement data for markers. Stores calculated spatial metrics.
Fields
Section titled “Fields”| Field | Type | Required | Description |
|---|---|---|---|
marker_id | UUID | Yes | Primary key & foreign key to marker |
center_location_long | f32 | Yes | Center Z coordinate (longitudinal) |
center_location_cross | f32 | Yes | Center X coordinate (cross) |
x_negative | f32 | Yes | X distance (left side, signed) |
x_positive | f32 | Yes | X distance (right side, signed) |
z_negative | f32 | Yes | Z distance (near edge) |
z_positive | f32 | Yes | Z distance (far edge) |
long_size | f32 | Yes | Length along Z axis |
cross_size | f32 | Yes | Width along X axis |
custom_props | JSON | No | Custom measurement properties |
created_at | Timestamp | Yes | Creation time |
updated_at | Timestamp | Yes | Last update time |
Metric Calculations
Section titled “Metric Calculations”All metrics are calculated from the four marker corner points:
Center Location (Longitudinal)
center_location_long = (p1[2] + p2[2] + p3[2] + p4[2]) / 4Center Location (Cross)
center_location_cross = (p1[0] + p2[0] + p3[0] + p4[0]) / 4X Negative/Positive
x_negative = min_abs(p1[0], p2[0], p3[0], p4[0]) // keeps signx_positive = max_abs(p1[0], p2[0], p3[0], p4[0]) // keeps signZ Negative/Positive
z_negative = min(p1[2], p2[2], p3[2], p4[2])z_positive = max(p1[2], p2[2], p3[2], p4[2])Size Measurements
long_size = z_positive - z_negativecross_size = max(p1[0], p2[0], p3[0], p4[0]) - min(p1[0], p2[0], p3[0], p4[0])Example
Section titled “Example”{ "marker_id": "770e8400-e29b-41d4-a716-446655440002", "center_location_long": 0.146, "center_location_cross": -0.198, "x_negative": -0.232, "x_positive": -0.165, "z_negative": 0.030, "z_positive": 0.262, "long_size": 0.232, "cross_size": 0.067, "custom_props": { "measurement_method": "automatic", "confidence": 0.95 }, "created_at": "2025-01-16T09:31:00Z", "updated_at": "2025-01-16T09:31:00Z"}Database Schema
Section titled “Database Schema”PostgreSQL Types
Section titled “PostgreSQL Types”-- EnumsCREATE TYPE work_session_status AS ENUM ('draft', 'active', 'done', 'archived');CREATE TYPE work_session_type AS ENUM ('inspection', 'repair', 'other');
-- TablesCREATE TABLE spaces ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), key VARCHAR(255) UNIQUE NOT NULL, name VARCHAR(255) NOT NULL, description TEXT, model_glb_url TEXT, model_usdc_url TEXT, preview_url TEXT, scan_url TEXT, calibration_vector DOUBLE PRECISION[3] NOT NULL DEFAULT '{0,0,0}', meta JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now());
CREATE TABLE work_sessions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), space_id UUID NOT NULL REFERENCES spaces(id) ON DELETE CASCADE, session_type work_session_type NOT NULL, status work_session_status NOT NULL DEFAULT 'draft', started_at TIMESTAMPTZ, completed_at TIMESTAMPTZ, version BIGINT NOT NULL DEFAULT 1, meta JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now());
CREATE TABLE markers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), work_session_id UUID NOT NULL REFERENCES work_sessions(id) ON DELETE CASCADE, label TEXT, p1 DOUBLE PRECISION[3] NOT NULL, p2 DOUBLE PRECISION[3] NOT NULL, p3 DOUBLE PRECISION[3] NOT NULL, p4 DOUBLE PRECISION[3] NOT NULL, color VARCHAR(7), version BIGINT NOT NULL DEFAULT 1, meta JSONB NOT NULL DEFAULT '{}', custom_props JSONB NOT NULL DEFAULT '{}', calibrated_data JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now());
CREATE TABLE marker_details ( marker_id UUID PRIMARY KEY REFERENCES markers(id) ON DELETE CASCADE, center_location_long REAL NOT NULL, center_location_cross REAL NOT NULL, x_negative REAL NOT NULL, x_positive REAL NOT NULL, z_negative REAL NOT NULL, z_positive REAL NOT NULL, long_size REAL NOT NULL, cross_size REAL NOT NULL, custom_props JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now());Indexes
Section titled “Indexes”-- Improve query performanceCREATE INDEX idx_work_sessions_space_id ON work_sessions(space_id);CREATE INDEX idx_work_sessions_status ON work_sessions(status);CREATE INDEX idx_markers_work_session_id ON markers(work_session_id);
-- JSON property queriesCREATE INDEX idx_markers_custom_props ON markers USING GIN(custom_props);CREATE INDEX idx_marker_details_custom_props ON marker_details USING GIN(custom_props);Type Mapping
Section titled “Type Mapping”Rust ↔ PostgreSQL
Section titled “Rust ↔ PostgreSQL”| Rust Type | PostgreSQL Type | Notes |
|---|---|---|
Uuid | UUID | Standard UUID |
String | TEXT / VARCHAR | UTF-8 strings |
i64 | BIGINT | 64-bit integer |
f64 | DOUBLE PRECISION | 64-bit float |
f32 | REAL | 32-bit float |
[f64; 3] | DOUBLE PRECISION[3] | Fixed-size array |
serde_json::Value | JSONB | JSON with binary storage |
DateTime<Utc> | TIMESTAMPTZ | Timezone-aware timestamp |
WorkSessionStatus | work_session_status | Custom enum |
WorkSessionType | work_session_type | Custom enum |
Swift ↔ JSON
Section titled “Swift ↔ JSON”| Swift Type | JSON Type | Notes |
|---|---|---|
UUID | String | UUID string format |
String | String | UTF-8 strings |
Int / Int64 | Number | Integer values |
Float / Double | Number | Floating-point values |
[Double] | Array | Number array |
[String: AnyCodable] | Object | Flexible JSON object |
Date | String | ISO 8601 format |
| Enums | String | Lowercase snake_case |
Best Practices
Section titled “Best Practices”1. Use UUIDs for IDs
Section titled “1. Use UUIDs for IDs”- All primary keys are UUIDs
- Generated server-side or client-side
- Enables distributed systems and offline support
2. Leverage Custom Properties
Section titled “2. Leverage Custom Properties”- Store domain-specific data in
custom_props - Keep core schema clean and focused
- Use GIN indexes for JSON queries
3. Apply Optimistic Concurrency
Section titled “3. Apply Optimistic Concurrency”- Always include
versionin updates - Handle version conflicts gracefully
- Retry with fresh data on conflict
4. Validate Marker Geometry
Section titled “4. Validate Marker Geometry”- Check edge lengths (minimum 5mm)
- Ensure points form valid quadrilateral
- Validate coordinate ranges
5. Use Calibration for Accuracy
Section titled “5. Use Calibration for Accuracy”- Set calibration vector per space
- Store both raw and calibrated coordinates
- Transform at API layer, not client
6. Index for Performance
Section titled “6. Index for Performance”- Add indexes on foreign keys
- Use GIN for JSON property searches
- Consider partial indexes for common filters