Skip to content

Data Model and Storage

TalarMustafa edited this page Oct 17, 2026 · 1 revision

Class Diagrams

Backend / web domain model

classDiagram
    direction TB
    class Organization {
        +UUID id
        +String name
    }
    class User {
        +UUID id
        +String email
        +Role role
        +authenticate() bool
    }
    class Role {
        <<enumeration>>
        USER
        ON_SITE_ADMIN
    }
    class Site {
        +UUID id
        +String name
        +String address
    }
    class Robot {
        +UUID id
        +String serialNumber
        +RobotStatus status
        +isOnline() bool
    }
    class Session {
        +UUID id
        +SessionMode mode
        +SessionStatus status
        +String videoStorageKey
        +start() void
        +end() void
    }
    class SessionMode {
        <<enumeration>>
        AUTONOMOUS
        LIVE_WALKTHROUGH
    }
    class Mesh {
        +UUID id
        +MeshVisibility visibility
        +String objectStorageKey
        +String tilesetKey
        +int version
        +getSignedUrl() String
    }
    class Annotation {
        +UUID id
        +Geometry position
        +String label
        +Severity severity
    }
    class Measurement {
        +UUID id
        +float valueMeters
    }
    class InspectionReport {
        +UUID id
        +String pdfStorageKey
        +generate() void
    }
    class ScheduledInspection {
        +UUID id
        +DateTime runAt
        +createJob() InspectionJob
    }
    class InspectionJob {
        +UUID id
        +JobStatus status
        +store() void
        +notifyRobot() void
    }
    Organization "1" --> "*" User : employs
    Organization "1" --> "*" Site : owns
    Organization "1" --> "*" Robot : owns
    User "1" --> "0..*" Session : operates
    Site "1" --> "*" Session : hosts
    Site "1" --> "0..1" Robot : assigned
    Robot "1" --> "*" Session : performs
    Session "1" --> "0..1" Mesh : produces
    Mesh "0..1" --> "0..1" Mesh : previousVersion
    Mesh "1" --> "*" Annotation : has
    Mesh "1" --> "*" Measurement : has
    User "1" --> "*" Annotation : authors
    Session "1" --> "0..1" InspectionReport : generates
    Site "1" --> "*" ScheduledInspection : schedules
    Robot "1" --> "*" ScheduledInspection : target
    ScheduledInspection "1" --> "0..1" InspectionJob : creates
    InspectionJob "1" --> "0..1" Session : resultsIn
    User ..> Role : role
    Session ..> SessionMode : mode
Loading

Domain Model

classDiagram
    direction TB

    class Organization {
        name
    }
    class User {
        email
        role
    }
    class Site {
        name
        address
    }
    class Robot {
        serialNumber
        status
    }
    class Session {
        mode
        status
        startedAt
        endedAt
    }
    class Mesh {
        <<3D Map>>
        visibility
        version
        objectStorageKey
    }
    class Annotation {
        label
        severity
        position
    }
    class Measurement {
        type
        valueMeters
    }
    class InspectionReport {
        title
        generatedAt
    }
    class ScheduledInspection {
        runAt
        status
    }
    class InspectionJob {
        status
    }

    Organization "1" --> "*" User : employs
    Organization "1" --> "*" Site : owns
    Organization "1" --> "*" Robot : owns
    User "1" --> "0..*" Session : operates
    Site "1" --> "*" Session : hosts
    Site "1" --> "0..1" Robot : assigned
    Robot "1" --> "*" Session : performs
    Session "1" --> "0..1" Mesh : produces
    Mesh "0..1" --> "0..1" Mesh : previousVersion
    Mesh "1" --> "*" Annotation : has
    Mesh "1" --> "*" Measurement : has
    User "1" --> "*" Annotation : authors
    User "1" --> "*" Measurement : authors
    Session "1" --> "0..1" InspectionReport : generates
    Site "1" --> "*" ScheduledInspection : schedules
    Robot "1" --> "*" ScheduledInspection : target
    ScheduledInspection "1" --> "0..1" InspectionJob : creates
    InspectionJob "1" --> "0..1" Session : resultsIn
Loading

Core entities

Organization — Tenant that owns users, sites, and robots. User — Signs in to RoamView. Role is User or On-Site Admin. Site — Physical location. At most one robot is assigned to a site. Robot — On-site unit. Runs an autonomous inspection or a live walkthrough. Session — One walkthrough. Mode is autonomous or live walkthrough. Status moves from recording through processing to ready. Mesh — 3D map from a session. Public or private. previousVersion links maps for compare-mesh-version. File bytes are in object storage; this entity holds the key. Annotation / Measurement — Pins and distances on a generated mesh. Inspection report — Summary of a session. PDF is in object storage. Scheduled inspection / Inspection job — On-site admin books a run; that creates a job; the robot is notified; a completed job becomes a session.

ER-Diagram

erDiagram
    direction TB
    organizations {
        uuid id PK
        string name
        string slug
        timestamp created_at
    }
    users {
        uuid id PK
        uuid org_id FK
        string email
        string password_hash
        string role
        timestamp created_at
    }
    sites {
        uuid id PK
        uuid org_id FK
        string name
        string address
        geometry footprint
        timestamp created_at
    }
    robots {
        uuid id PK
        uuid org_id FK
        uuid site_id FK
        string name
        string serial_number
        string status
        int battery_pct
        timestamp last_seen_at
    }
    sessions {
        uuid id PK
        uuid site_id FK
        uuid robot_id FK
        uuid operator_id FK
        string mode
        string status
        string video_storage_key
        timestamp started_at
        timestamp ended_at
    }
    meshes {
        uuid id PK
        uuid session_id FK
        uuid previous_mesh_id FK
        string visibility
        int version
        string object_storage_key
        string tileset_key
        string preview_key
        timestamp created_at
    }
    annotations {
        uuid id PK
        uuid mesh_id FK
        uuid author_id FK
        geometry position
        string label
        string severity
        text notes
        timestamp created_at
    }
    measurements {
        uuid id PK
        uuid mesh_id FK
        uuid author_id FK
        string type
        jsonb points
        float value_meters
        timestamp created_at
    }
    reports {
        uuid id PK
        uuid session_id FK
        uuid author_id FK
        string title
        string pdf_storage_key
        timestamp generated_at
    }
    scheduled_inspections {
        uuid id PK
        uuid site_id FK
        uuid robot_id FK
        timestamp run_at
        string status
    }
    inspection_jobs {
        uuid id PK
        uuid schedule_id FK
        uuid session_id FK
        string status
    }
    organizations ||--o{ users : has
    organizations ||--o{ sites : owns
    organizations ||--o{ robots : owns
    sites ||--o| robots : assigned
    users ||--o{ sessions : operates
    sites ||--o{ sessions : hosts
    robots ||--o{ sessions : performs
    sessions ||--o| meshes : produces
    sessions ||--o| reports : generates
    meshes |o--o| meshes : previous_version
    meshes ||--o{ annotations : has
    meshes ||--o{ measurements : has
    users ||--o{ annotations : authors
    users ||--o{ measurements : authors
    sites ||--o{ scheduled_inspections : schedules
    robots ||--o{ scheduled_inspections : target
    scheduled_inspections ||--o| inspection_jobs : creates
    inspection_jobs ||--o| sessions : results_in
Loading

3D map storage

3D maps are not stored as PostgreSQL BLOBs. meshes.object_storage_key and meshes.tileset_key point at object storage. The same pattern applies to sessions.video_storage_key and reports.pdf_storage_key. The API returns a signed URL; the client streams bytes from object storage / CDN.

Notes

  • PostgreSQL stores structured metadata. Large binaries (3D maps, video, tiles) are stored in object storage and referenced by key.

Notation

Crow’s-foot marks show how many rows participate in a relationship.

Symbol Meaning
|| Exactly one (required)
○| Zero or one (optional)
○< Zero or more (optional many)

PK is the primary key (unique id of a row). FK is a foreign key (points at another table’s PK).

Data Model & Storage

RoamView keeps data in two storage systems:

System Technology What it holds Access pattern
General storage (metadata) PostgreSQL Structured, queryable rows: organizations, users, sites, robots, sessions, jobs, meshes, annotations, reports Small rows, transactional, joined and filtered by the API
Object storage (BLOBs) S3-compatible object store (e.g. AWS S3 / MinIO) Large binaries: raw photo sets, depth frames, telemetry bags, video, meshes, 3D tilesets, report PDFs Written once, read many times, streamed to clients through signed URLs / CDN

Rule: PostgreSQL never stores file bytes. Tables store the object key (the file's path in object storage), as in the ER diagram on Software Architecture. This replaces the "BLOB storage for 3D maps" note in External Libraries.

Naming used in this page vs. the task card

The task card uses early names. This page uses the names from Software Architecture and the Domain Model.

Task card term Name in this model
Facility sites
User users
ScanJob inspection_jobs
Tile meshes.tileset_key (the tileset's root file). Each tile file exists only in object storage (see 3D tiles)

1. General Storage (Metadata) — ER Diagram

The tables from the ER diagram on Software Architecture are unchanged. One table is added, storage_objects, which lists every file in object storage. It does not replace any existing column. It sits alongside them and is matched to them by key (see section 3).

erDiagram
    direction TB
    organizations {
        uuid id PK
        string name
        string slug
        timestamp created_at
    }
    users {
        uuid id PK
        uuid org_id FK
        string email
        string password_hash
        string role
        timestamp created_at
    }
    sites {
        uuid id PK
        uuid org_id FK
        string name
        string address
        geometry footprint
        timestamp created_at
    }
    robots {
        uuid id PK
        uuid org_id FK
        uuid site_id FK
        string name
        string serial_number
        string status
        int battery_pct
        timestamp last_seen_at
    }
    sessions {
        uuid id PK
        uuid site_id FK
        uuid robot_id FK
        uuid operator_id FK
        string mode
        string status
        string video_storage_key
        timestamp started_at
        timestamp ended_at
    }
    meshes {
        uuid id PK
        uuid session_id FK
        uuid previous_mesh_id FK
        string visibility
        int version
        string object_storage_key
        string tileset_key
        string preview_key
        timestamp created_at
    }
    annotations {
        uuid id PK
        uuid mesh_id FK
        uuid author_id FK
        geometry position
        string label
        string severity
        text notes
        timestamp created_at
    }
    measurements {
        uuid id PK
        uuid mesh_id FK
        uuid author_id FK
        string type
        jsonb points
        float value_meters
        timestamp created_at
    }
    reports {
        uuid id PK
        uuid session_id FK
        uuid author_id FK
        string title
        string pdf_storage_key
        timestamp generated_at
    }
    scheduled_inspections {
        uuid id PK
        uuid site_id FK
        uuid robot_id FK
        timestamp run_at
        string status
    }
    inspection_jobs {
        uuid id PK
        uuid schedule_id FK
        uuid session_id FK
        string status
    }
    storage_objects {
        uuid id PK
        uuid org_id FK
        uuid session_id FK "nullable"
        string bucket
        string object_key UK "matches the *_key columns"
        string kind "RGB_SET | DEPTH_SET | TELEMETRY | SLAM_MAP | VIDEO | MESH | TILESET | PREVIEW | REPORT | INTERMEDIATE"
        string content_type
        bigint size_bytes
        string sha256
        string status "PENDING | AVAILABLE | ARCHIVED | DELETED"
        timestamp expires_at "from retention policy"
        timestamp created_at
    }

    organizations ||--o{ users : has
    organizations ||--o{ sites : owns
    organizations ||--o{ robots : owns
    sites ||--o| robots : assigned
    users ||--o{ sessions : operates
    sites ||--o{ sessions : hosts
    robots ||--o{ sessions : performs
    sessions ||--o| meshes : produces
    sessions ||--o| reports : generates
    meshes |o--o| meshes : previous_version
    meshes ||--o{ annotations : has
    meshes ||--o{ measurements : has
    users ||--o{ annotations : authors
    users ||--o{ measurements : authors
    sites ||--o{ scheduled_inspections : schedules
    robots ||--o{ scheduled_inspections : target
    scheduled_inspections ||--o| inspection_jobs : creates
    inspection_jobs ||--o| sessions : results_in
    organizations ||--o{ storage_objects : "owns files"
    sessions |o--o{ storage_objects : "raw capture, video"
Loading

Why storage_objects is added

The existing *_key columns already say where each file is. storage_objects adds what those columns can't:

Need How storage_objects covers it
Capacity tracking size_bytes per file → total usage per organization, site or session with one query
Upload status status shows whether an upload is pending, finished, archived or deleted
Integrity sha256 checks that a file arrived undamaged
Retention and cleanup expires_at + status drive the lifecycle process (section 3)
Raw capture link Raw photos, depth frames, telemetry and SLAM maps have no column in the existing tables. session_id + kind links them to their session

Indexes on storage_objects

  • UNIQUE (bucket, object_key).
  • (session_id, kind) to find all files of a session.
  • (status, expires_at) for the retention sweeper.

2. Object Storage — ER Diagram

Object storage has no tables or foreign keys. Its structure comes from buckets and key prefixes (folders). The diagram shows that structure as entities. Each "attribute" is the key pattern, file format or object metadata.

Buckets

Bucket Content Access Lifecycle
roamview-raw Raw capture from the robot: RGB frames, depth frames, telemetry bag, SLAM map Write: Ingestion Service. Read: Processing Service only Standard tier for 30 days → cold/infrequent-access tier → delete at 365 days
roamview-work Processing intermediates (COLMAP sparse model, OpenMVS dense cloud) Processing Service only Delete after 7 days
roamview-assets Client-facing 3D data: mesh, tileset + tiles, preview image Read by clients through signed URLs / CDN Kept while the mesh is kept (365 days by default, see SP-GU05.03)
roamview-media Session video recordings, inspection report PDFs Read by clients through signed URLs Video: 90 days. Reports: 365 days

Keys use IDs, never names. Renaming a site does not move files. The orgs/{org_id}/ prefix makes per-tenant access policies and usage totals simple.

erDiagram
    direction TB
    BUCKET {
        string name "roamview-raw | -work | -assets | -media"
        string region
        string encryption "SSE (AES-256)"
        string lifecycle_rules
    }
    ORG_PREFIX {
        string key "orgs/{org_id}/"
    }
    SITE_PREFIX {
        string key "orgs/{org_id}/sites/{site_id}/"
    }
    SESSION_PREFIX {
        string key "…/sessions/{session_id}/"
    }
    CAPTURE_MANIFEST {
        string key "…/capture/manifest.json"
        json frames "seq, file, timestamp, pose, sha256"
        string meta "x-amz-meta-session-id"
    }
    RGB_FRAME {
        string key "…/capture/rgb/{seq:06d}.jpg"
        string format "JPEG 3840x2160"
        float size_mb "~2.5"
    }
    DEPTH_FRAME {
        string key "…/capture/depth/{seq:06d}.png"
        string format "PNG uint16 640x480"
        float size_mb "~0.3"
    }
    TELEMETRY_BAG {
        string key "…/telemetry/session.mcap"
        string format "ROS 2 MCAP (LiDAR, IMU, odom, tf)"
        float size_mb "~0.05 per second"
    }
    SLAM_MAP {
        string key "…/slam/map.pgm + map.yaml"
        float size_mb "~1"
    }
    VIDEO_RECORDING {
        string key "…/video/recording.mp4"
        string format "H.264 720p30 ~2.5 Mbps"
    }
    WORK_PREFIX {
        string key "…/sessions/{session_id}/work/"
    }
    SPARSE_MODEL {
        string key "…/sparse/ (cameras, images, points3D)"
    }
    DENSE_CLOUD {
        string key "…/dense/scene_dense.ply"
    }
    MESH_VERSION_PREFIX {
        string key "…/meshes/{mesh_id}/v{version}/"
    }
    MESH_FILE {
        string key "…/mesh.glb"
        string format "glTF binary, textured"
        float size_mb "~150"
    }
    TILESET_ROOT {
        string key "…/tiles/tileset.json"
        string format "3D Tiles 1.1"
    }
    TILE {
        string key "…/tiles/{level}/{x}_{y}_{z}.glb"
        string format "glTF content per LOD node"
    }
    PREVIEW {
        string key "…/preview.webp"
    }
    REPORT_PDF {
        string key "orgs/{org_id}/reports/{report_id}.pdf"
    }

    BUCKET ||--o{ ORG_PREFIX : contains
    ORG_PREFIX ||--o{ SITE_PREFIX : contains
    ORG_PREFIX ||--o{ REPORT_PDF : "contains (media)"
    SITE_PREFIX ||--o{ SESSION_PREFIX : "contains (raw, media, work)"
    SITE_PREFIX ||--o{ MESH_VERSION_PREFIX : "contains (assets)"
    SESSION_PREFIX ||--o| CAPTURE_MANIFEST : "raw"
    CAPTURE_MANIFEST ||--|{ RGB_FRAME : lists
    CAPTURE_MANIFEST ||--o{ DEPTH_FRAME : lists
    SESSION_PREFIX ||--o| TELEMETRY_BAG : "raw"
    SESSION_PREFIX ||--o| SLAM_MAP : "raw"
    SESSION_PREFIX ||--o| VIDEO_RECORDING : "media"
    SESSION_PREFIX ||--o| WORK_PREFIX : "work"
    WORK_PREFIX ||--o| SPARSE_MODEL : contains
    WORK_PREFIX ||--o| DENSE_CLOUD : contains
    MESH_VERSION_PREFIX ||--|| MESH_FILE : contains
    MESH_VERSION_PREFIX ||--o| TILESET_ROOT : contains
    TILESET_ROOT ||--|{ TILE : references
    MESH_VERSION_PREFIX ||--o| PREVIEW : contains
Loading

3D tiles

A tileset can hold hundreds or thousands of tile files. A database row per tile would add rows without adding anything the API needs. The client only needs the root tileset.json, and Cesium follows its references to fetch tiles. For that reason:

  • meshes.tileset_key points at the root tileset.json, as in the existing ER diagram.
  • storage_objects has one row of kind TILESET for that root. Its size_bytes is the total of the whole tiles/ prefix.
  • A signed URL (or CDN signed cookie) is issued for the tiles/ prefix so the client can fetch child tiles.

The raw photo set follows the same approach: one manifest.json row, not one row per frame.


3. Links Between Metadata and Object Storage

flowchart LR
    subgraph PG["PostgreSQL (metadata)"]
        SES["sessions<br/>video_storage_key"]
        MSH["meshes<br/>object_storage_key · tileset_key · preview_key"]
        REP["reports<br/>pdf_storage_key"]
        SO[("storage_objects<br/>bucket + object_key<br/>size, sha256, status")]
    end
    subgraph OS["Object storage"]
        RAW["roamview-raw<br/>capture/manifest.json · rgb/* · depth/*<br/>telemetry/session.mcap · slam/map.*"]
        WORK["roamview-work<br/>work/sparse · dense"]
        AST["roamview-assets<br/>meshes/{id}/v{n}/mesh.glb · tiles/* · preview.webp"]
        MED["roamview-media<br/>video/recording.mp4 · reports/{id}.pdf"]
    end
    SES -- "key = object_key" --> SO
    MSH -- "key = object_key" --> SO
    REP -- "key = object_key" --> SO
    SES -- "session_id (raw capture)" --> SO
    SO -. "(bucket, object_key)" .-> RAW
    SO -. "(bucket, object_key)" .-> WORK
    SO -. "(bucket, object_key)" .-> AST
    SO -. "(bucket, object_key)" .-> MED
Loading

Links in both directions:

  • Metadata → object: a domain row's *_key column holds the object key. The same key appears in storage_objects.object_key, which adds size, checksum and status. Raw capture files are found through storage_objects.session_id.
  • Object → metadata: every object is uploaded with the headers x-amz-meta-storage-object-id, x-amz-meta-org-id and x-amz-meta-session-id. The key prefix also contains the org, site and session IDs. An orphaned file can therefore be traced back to its rows without a database lookup.

Upload and read lifecycle

sequenceDiagram
    autonumber
    participant R as Robot (Pi 5)
    participant I as Ingestion Service
    participant DB as PostgreSQL
    participant S3 as Object Storage
    participant P as Processing Service
    participant C as Web / Mobile client

    R->>I: POST /sessions/{id}/uploads (kind, size, sha256)
    I->>DB: INSERT storage_objects (status = PENDING)
    I-->>R: pre-signed PUT URL(s)
    R->>S3: PUT objects (multipart, resumable)
    R->>I: POST /uploads/{object_id}/complete
    I->>S3: HEAD object (check size + checksum)
    I->>DB: UPDATE storage_objects SET status = AVAILABLE<br/>UPDATE sessions SET video_storage_key, status = PROCESSING
    I->>P: enqueue processing for session
    P->>S3: GET raw capture, PUT intermediates + mesh + tiles
    P->>DB: INSERT storage_objects + meshes (object_storage_key,<br/>tileset_key, preview_key) in one transaction<br/>UPDATE sessions SET status = READY
    C->>DB: GET /sites/{id}/meshes (via API, auth + visibility check)
    DB-->>C: mesh metadata + short-lived signed URL
    C->>S3: GET tileset.json → tiles (via CDN)
Loading

Consistency rules

  1. Upload first, then commit. A *_key column is only filled once the matching storage_objects row is AVAILABLE. PENDING rows older than 24 h are marked failed, and their partial uploads are aborted.
  2. Orphan sweep (daily). Objects without an AVAILABLE/ARCHIVED registry row are deleted. Registry rows whose object is missing are flagged and reported.
  3. Deletion is status-driven. Retention sets expires_at. The sweeper deletes the object, then sets status = DELETED. The row is kept for audit and usage history.
  4. Privacy. Every bucket is private and encrypted at rest. Clients never get bucket credentials, only signed URLs that expire within minutes. PUBLIC meshes skip the ownership check but still use signed URLs. See Ethics & Accountability.

4. Capacity Estimation

All figures are estimates from stated assumptions. They should be revisited when hardware tests (Hardware Testing) produce measured values. Units: 1 GB = 1000 MB. Script used: capacity.py.

4.1 Assumptions

Parameter Value Source / rationale
Autonomous inspection length 30 min (1800 s) ~6 rooms × T_room = 300 s (SP-GU01.03)
Live walkthrough length 6 min (360 s) T_max control lease (SP-GU01.03)
Photogrammetry keyframe rate 0.5 fps → 900 frames / inspection 1 frame every 2 s gives enough overlap for COLMAP at walking speed
RGB keyframe 3840×2160 JPEG, ~2.5 MB OAK-D Lite IMX214 colour sensor (max 4208×3120, 4K30)
Depth frame 640×480 uint16 PNG, ~0.3 MB OAK-D Lite stereo pair; 614 KB raw, ~50% lossless compression
Telemetry bag (MCAP, zstd) ~0.05 MB/s Waveshare D500: 5000 samples/s at 10 Hz → ~40 KB/s raw, plus IMU/odom/tf, ~2:1 compression
Video recording 720p30 H.264, 2.5 Mbps Pi Camera Module 3 Wide. The Pi 5 has no hardware H.264 encoder, so software encoding at 720p is the safe baseline
Intermediate files ~1 GB / inspection COLMAP sparse model + OpenMVS dense point cloud
Textured mesh ~150 MB ~1M faces + textures, .glb
3D tileset 4/3 × mesh ≈ 200 MB Quadtree LOD pyramid: 1 + ¼ + 1⁄16 + … = 4/3
Preview / report PDF 0.5 MB / 5 MB —
Retention Raw 365 d (hot 30 d, then cold) · intermediates 7 d · video 90 d · mesh, tiles, reports 365 d Proposed policy. Mesh retention to be confirmed by SP-GU05.03

4.2 Per-session footprint

Object class Bucket Autonomous (30 min) Live walkthrough (6 min)
RGB photo set (900 frames) raw 2,250 MB —
Depth frames (900) raw 270 MB —
Telemetry bag raw 90 MB 18 MB
SLAM map raw 1 MB —
Video recording media 563 MB 113 MB
Intermediates (temporary) work 1,000 MB —
Mesh assets 150 MB —
3D tileset assets 200 MB —
Preview + report assets / media 6 MB —
Total ≈ 4.5 GB ≈ 0.13 GB

The raw RGB photo set is ~50% of each inspection and ~70% of long-term storage. Its retention period is the main factor in the total.

4.3 Growth scenarios (steady state after 12 months)

Scenario Sites Sessions / site / week (auto + live) Sessions / year Ingest / month Hot tier Cold tier Total Total + 20% headroom
A. Capstone demo 1 2 + 3 104 + 156 40 GB 81 GB 252 GB 333 GB 400 GB
B. Pilot 10 1 + 5 521 + 2,607 222 GB 456 GB 1.3 TB 1.75 TB 2.1 TB
C. Growth 100 3 + 10 15,643 + 52,143 6.4 TB 12.9 TB 38.3 TB 51 TB 62 TB

Steady state for each class = daily ingest × retention days. Hot tier = everything except raw capture older than 30 days.

Sensitivity: shortening raw retention from 365 to 90 days reduces Scenario B from 1.75 TB to ≈ 0.69 TB. Reprocessing is then only possible within 90 days.

4.4 PostgreSQL (metadata)

Item Estimate
Rows per autonomous inspection ~46 (1 session, 1 mesh, ~11 storage objects, ~20 annotations, ~10 measurements, 1 report, 1 schedule, 1 job)
Rows per live walkthrough ~3 (1 session, 2 storage objects)
Average row size (with indexes) ~300 B × 2
Year-1 size A: ~3 MB · B: ~19 MB · C: ~0.5 GB

The metadata database is small at every scenario. One managed PostgreSQL instance with the smallest storage tier (e.g. 20 GB) and daily backups is enough. The design keeps it small because frames and tiles are not stored as rows.

4.5 Robot local buffer and upload

Item Estimate
Data to upload per autonomous inspection (raw + video) ~3.2 GB
Upload time at 20 Mbps effective Wi-Fi uplink ~21 min (an input for SP-GU08.02, "Save Video Recording Job Max Time")
Local storage needed 64 GB microSD/NVMe minus ~16 GB OS/ROS → buffer for ~15 inspections if Wi-Fi is down. Local copies are deleted after the upload is confirmed (status = AVAILABLE)

Clone this wiki locally