# Rhopoint Elements Hub > Section of the Rhopoint Instruments Manual. This file bundles all 35 pages of this section as Markdown. # Elements Hub Overview > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. ### Elements Hub Elements Hub is a background service that makes Rhopoint instruments visible to the rest of your factory and laboratory systems. It sits between devices and third‑party software, collecting measurements and status from instruments and then sharing this information in a simple, consistent way. Typical connections include SPC and quality systems, PLCs, robots, cobots and other automation controllers that need live appearance data to make decisions. By using Elements Hub as a single connection point, external systems do not need their own custom drivers for each Rhopoint device. Instead, they can subscribe to measurement values, pass/fail results and basic control signals from instruments such as Rhopoint Aesthetix, Rhopoint TAMS and Rhopoint ID through standard interfaces. This reduces integration effort, speeds up commissioning and makes it easier to maintain a connected appearance measurement environment over time. The Rhopoint Elements Hub web service exposes a versioned REST API for discovering devices, controlling connected hardware, collecting measurements, and administering the hub. The API is documented via Swagger/OpenAPI and ships with an auto-configured Swagger UI at runtime. Use this guide to start the service, explore the available endpoints, and generate strongly typed clients from the OpenAPI description. --- # API Overview > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The Elements Hub REST API provides comprehensive access to device discovery, connection, measurement acquisition, calibration, image streaming, and self-administration. This page is the top-level map — every section links to the detailed reference for the matching endpoints. For an interactive way to explore and try the API see the [Swagger UI Guide](rhopoint-elements-hub-swagger-ui-guide.md). For strongly typed client libraries see [Client Code Generation](rhopoint-elements-hub-client-code-generation.md). ### API Version Current version: **1.0** All endpoints are versioned and follow the pattern: `/v1/{resource}`. The default base URL is `http://localhost:42042` — see [Network Binding](rhopoint-elements-hub-security-notice.md#network-binding) for how to change it. ### Workflow at a Glance Almost every integration follows the same sequence. See [Endpoints — Functionality](rhopoint-elements-hub-endpoints-functionality.md) for the prose walkthrough and step-by-step links: 1. [Initialize lifecycle services](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) 2. [Start a device scan](rhopoint-elements-hub-endpoints-functionality-device-scans.md) 3. [Connect to a discovered device](rhopoint-elements-hub-endpoints-functionality-connected-devices.md) 4. [Calibrate](rhopoint-elements-hub-endpoints-functionality-calibrations.md) (where required) 5. [Trigger measurements](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md) and / or [consume image streams](rhopoint-elements-hub-endpoints-functionality-image-streams.md) 6. [Disconnect](rhopoint-elements-hub-endpoints-functionality-connected-devices.md#disconnecting-a-device) 7. [Shut down lifecycle services](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) ### Endpoint Reference | Area | Reference | Endpoints | | --- | --- | --- | | Service lifecycle | [Lifecycle](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) | `POST /v1/lifecycle/initialize`, `POST /v1/lifecycle/shutdown` | | Device discovery | [Device Scans](rhopoint-elements-hub-endpoints-functionality-device-scans.md) | `POST/GET/DELETE /v1/device-scans`, `GET /v1/available-devices` | | Device connection | [Connected Devices](rhopoint-elements-hub-endpoints-functionality-connected-devices.md) | `POST/GET/DELETE /v1/devices`, property lookup | | Measurements | [Measurement Triggers](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md) | `GET/POST /v1/devices/{deviceId}/measurement-triggers`, `POST /v1/measurements/decode` | | Calibration | [Calibrations](rhopoint-elements-hub-endpoints-functionality-calibrations.md) | `GET/POST /v1/devices/{deviceId}/calibrations`, `GET /calibrations/status` | | Live imaging | [Image Streams](rhopoint-elements-hub-endpoints-functionality-image-streams.md) | `POST/DELETE /v1/devices/{deviceId}/image-streams/{sourceKey}`, `start`/`configure`/`snapshot`/`stream` | | Host diagnostics | [System](rhopoint-elements-hub-endpoints-functionality-system.md) | `GET /v1/system/version`, `GET /v1/system/checks`, `POST /v1/system/errors/raise-exception` | | Self-update | [App Updates](rhopoint-elements-hub-endpoints-functionality-app-updates.md) | `GET /v1/app-updates/info`, `POST /v1/app-updates/action` | ### Cross-Cutting Topics - [Error Handling](rhopoint-elements-hub-error-handling.md) — uniform `ErrorInfo` response format and the `id.rhopointservice.com/E` reference URLs. - [Security Notice](rhopoint-elements-hub-security-notice.md) — current authentication and transport posture; **important to read before exposing the hub on a network**. - [Measurement Image Formats](rhopoint-elements-hub-measurement-image-formats.md) — encoding of device images inside measurement containers (PNG / JPEG / raw). - [RAE File Format](rhopoint-elements-hub-rae-file-format.md) — binary layout of the measurement container produced by `Measurement Triggers`. - [Mock Devices](rhopoint-elements-hub-mock-devices.md) — running the API without physical hardware for development and CI. ### Core Concepts #### Devices - **Available Devices** — devices discovered by a running scan that can be connected. See [Polling Discovered Devices](rhopoint-elements-hub-endpoints-functionality-device-scans.md#polling-discovered-devices). - **Connected Devices** — devices currently connected and ready for operations. See [Connected Devices](rhopoint-elements-hub-endpoints-functionality-connected-devices.md). - **Device Class** — identifies the model behind a device record (e.g. `aesthetix`, `aesthetix-inline`, `id-inline`, plus `-mock` variants). The class drives which calibrations, measurements and image sources are available — see [Aesthetix](rhopoint-elements-hub-instruments-aesthetix.md) for one example. #### Scans - **Device Scans** — background processes that discover available devices on the network or attached via USB. See [Device Scans](rhopoint-elements-hub-endpoints-functionality-device-scans.md). - One scan covers exactly one device class. To search for multiple classes in parallel, start multiple scans. #### Measurements & Operations - **Measurement Triggers** — commands that initiate data acquisition from connected devices. See [Measurement Triggers](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md). - **Calibrations** — operations that re-reference the device's measurement chain against known standards. See [Calibrations](rhopoint-elements-hub-endpoints-functionality-calibrations.md). - **Commands** — device-class–specific actions, discoverable via `GET /v1/devices/{deviceId}/commands`. - **Container Formats** — output containers for measurements, currently `raeBinary` (default) and `raeJson`. See [Container Formats](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md#container-formats) and [RAE File Format](rhopoint-elements-hub-rae-file-format.md). ### HTTP Methods Used | Method | Usage | | --- | --- | | `GET` | Retrieve information (devices, scans, status, snapshots). | | `POST` | Create resources or initiate operations (connect devices, start scans, trigger measurements, run calibrations). | | `DELETE` | Remove resources (disconnect devices, stop scans, stop image streams). | ### Content Types #### Request - `application/json` — used by every endpoint that accepts a body, except `POST /v1/measurements/decode`. - `multipart/form-data` — only by `POST /v1/measurements/decode` (file upload for RAE decoding, see [Decoding the Container Client-Side](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md#decoding-the-container-client-side)). #### Response | Content type | Endpoint(s) | | --- | --- | | `application/json` | Standard API responses, including `ErrorInfo` payloads — see [Error Handling](rhopoint-elements-hub-error-handling.md). | | `application/vnd.rhopoint.rae+binary` | `POST /v1/devices/{deviceId}/measurement-triggers` with `containerFormat: raeBinary` — see [RAE File Format](rhopoint-elements-hub-rae-file-format.md). | | `image/png` / `image/jpeg` / `image/bmp` | Snapshots and single-image endpoints — see [Snapshot — Single Frame](rhopoint-elements-hub-endpoints-functionality-image-streams.md#snapshot-single-frame). | | `image/x-raw` | Raw device images embedded in a measurement container — see [Measurement Image Formats](rhopoint-elements-hub-measurement-image-formats.md). | | `text/event-stream` | Live image streams — see [Consuming the Stream (SSE)](rhopoint-elements-hub-endpoints-functionality-image-streams.md#consuming-the-stream-sse). | | `text/plain` | `GET /v1/logs`. | ### Response Patterns Successful responses use the shape documented on each endpoint's reference page. There is no uniform "envelope" wrapping every response — for example, `GET /v1/devices` returns a JSON array of device records, `GET /v1/system/version` returns an object, and a successful measurement returns the raw RAE container as a binary body. Error responses always follow a single schema, regardless of which endpoint produced them. See [Error Handling](rhopoint-elements-hub-error-handling.md) for the full `ErrorInfo` format, the `errorCode`/`errorUrl` contract, and recommended client patterns. ### Authentication None. Any caller that can reach the listening socket can call every endpoint. Read [Security Notice](rhopoint-elements-hub-security-notice.md) before binding the hub to anything other than `localhost`. ### Rate Limiting Currently, no rate limiting is implemented, but clients should: - Avoid excessive polling of [`GET /v1/available-devices`](rhopoint-elements-hub-endpoints-functionality-device-scans.md) or status endpoints — once a second is typically sufficient. - Allow adequate time between measurement triggers — many measurements take several seconds on the device side. - Reuse a single connection over many measurements rather than connect/disconnect cycles; see [Connected Devices — Best Practices](rhopoint-elements-hub-endpoints-functionality-connected-devices.md#best-practices). ### Asynchronous Operations A few operations follow a start-then-poll pattern rather than blocking on the response: - [Device scanning](rhopoint-elements-hub-endpoints-functionality-device-scans.md) — `POST` starts the scan, `GET /v1/available-devices` polls for results, `DELETE` stops. - [Application download](rhopoint-elements-hub-endpoints-functionality-app-updates.md) — `POST .../action "download"` stages the update, `GET .../info` reports `updateDownloaded`. - [Image streams](rhopoint-elements-hub-endpoints-functionality-image-streams.md) — `POST .../start` enables the stream, `GET .../stream` opens an SSE subscription, `DELETE` tears down. Connect / disconnect, measurements and calibrations are **synchronous**: the `POST` does not return until the operation has finished on the device. Make sure the HTTP client timeout is long enough — at least 30 s is a safe baseline for measurements and calibrations. --- # Changelog > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The changelog for Elements Hub is publicly available online. You can view the full, up-to-date list of changes at:\ [https://changelog.rhopointservice.com/products/elements-hub](https://changelog.rhopointservice.com/products/elements-hub) All change-entries are listed there, typically ordered by date or release version, so you can easily follow along from earliest releases to the most recent. ## What is a Changelog A changelog is a curated, chronologically ordered list of all the notable changes made in a project. It records enhancements, bug fixes, new features, removals, and technical adjustments. The purpose is to provide users, developers, and stakeholders with a transparent view of how the product has evolved over time. ## Purpose of the Changelog The changelog serves several key purposes: - **Transparency**: Users can see what has changed, fixed, added or removed. - **Tracking Progress**: Helps maintainers and contributors track what work has been completed, what remains, and what has been delivered. - **User Communication**: Users can decide whether to upgrade or migrate based on what changes are relevant to them. - **Historical Reference**: Provides a record for debugging, auditing, or reviewing the evolution of the product. - **Planning**: Helps align future expectations by showing past patterns and the pace of development. --- # Client Code Generation > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. This guide explains how to obtain the OpenAPI specification (swagger.json) from the Elements Hub API server and generate client code for various programming languages using [OpenAPI Generator](https://openapi-generator.tech/docs/generators). ## Getting the OpenAPI Specification ### 1. Access Swagger UI The Elements Hub server provides an interactive Swagger UI interface accessible at: `http://localhost:42042/swagger/` > [!note] Replace localhost:42042 with your actual server address and port. ### 2. Download swagger.json The OpenAPI specification is available in JSON format at:\ [http://localhost:42042/swagger/v1/swagger.json](http://localhost:42042/swagger/v1/swagger.json) You can download this file using: **curl**\ `curl -o swagger.json http://localhost:42042/swagger/v1/swagger.json` **wget**\ `wget -O swagger.json http://localhost:42042/swagger/v1/swagger.json` **PowerShell**\ `Invoke-WebRequest -Uri "http://localhost:42042/swagger/v1/swagger.json" -OutFile "swagger.json"` ## Client Code Generation with OpenAPI Generator ### Method 1: Using Docker (Recommended) The easiest way to generate client code is using the official OpenAPI Generator Docker image. #### Basic Usage ```bash docker run --rm \ -v ${PWD}:/local \ openapitools/openapi-generator-cli generate \ -i /local/swagger.json \ -g "generator-name" \ -o /local/out/generated-client \ --additional-properties=your-additional-properties ``` #### Language-Specific Examples **C# Client:** ```bash docker run --rm \ -v ${PWD}:/local \ openapitools/openapi-generator-cli generate \ -i /local/swagger.json \ -g csharp \ -o /local/generated-client-csharp \ --additional-properties=apiName=RhopointElementsHubClient,packageName=Rhopoint.ElementsHub.Client,nullableReferenceTypes=true,targetFramework=net9.0 ``` **C++ Client:** ```bash docker run --rm \ -v ${PWD}:/local \ openapitools/openapi-generator-cli generate \ -i /local/swagger.json \ -g cpp-restsdk \ -o /local/out/cpp-client \ --additional-properties=apiPackage=com.rhopointinstruments.elementshub.api,modelPackage=com.rhopointinstruments.elementshub.model,packageName=RhopointHeadlessElements ``` **Python Client:** ```bash docker run --rm \ -v ${PWD}:/local \ openapitools/openapi-generator-cli generate \ -i /local/swagger.json \ -g python \ -o /local/generated-client-python \ --additional-properties=packageName=rhopoint_elements_hub_client,projectName=rhopoint-elements-hub-client ``` **JavaScript/TypeScript Client:**\ This example uses the Angular framework. ```bash docker run --rm \ -v ${PWD}:/local \ openapitools/openapi-generator-cli generate \ -i /local/swagger.json \ -g typescript-angular \ -o /local/generated-client-typescript \ --additional-properties=npmName=rhopoint-elements-hub-client ``` #### PowerShell Examples (Windows) **C# Client:** ```powershell docker run --rm ` -v ${PWD}:/local ` openapitools/openapi-generator-cli generate ` -i /local/swagger.json ` -g csharp ` -o /local/generated-client-csharp ` --additional-properties=apiName=RhopointElementsHubClient,packageName=Rhopoint.ElementsHub.Client,nullableReferenceTypes=true,targetFramework=net9.0 ``` ### Method 2: Using NPM Package You can also install and use the OpenAPI Generator via NPM.\ This example uses the Angular framework. ```bash # Install globally npm install -g @openapitools/openapi-generator-cli # Generate client code openapi-generator-cli generate \ -i swagger.json \ -g typescript-angular \ -o generated-client-typescript ``` ## Supported Generators OpenAPI Generator supports numerous programming languages and frameworks. Here are some popular options: ### Client Libraries - **csharp** - C# client library - **python** - Python client library - **java** - Java client library - **javascript** - JavaScript client library - **typescript-axios** - TypeScript client with Axios - **typescript-fetch** - TypeScript client with Fetch API - **go** - Go client library - **php** - PHP client library - **ruby** - Ruby client library - **swift5** - Swift 5 client library - **kotlin** - Kotlin client library - **dart** - Dart client library - [more](https://openapi-generator.tech/docs/generators) ### Documentation - **html2** - HTML documentation - **markdown** - Markdown documentation ## Automation Scripts ### Bash Script for Multiple Languages Create a script `generate-clients.sh`: ```bash #!/bin/bash # Download latest OpenAPI spec curl -o swagger.json http://localhost:42042/swagger/v1/swagger.json # Generate clients for multiple languages languages=("csharp" "cpp-client" "python" "typescript-angular" "java" "go") for lang in "${languages[@]}"; do echo "Generating $lang client..." docker run --rm \ -v ${PWD}:/local \ openapitools/openapi-generator-cli generate \ -i /local/swagger.json \ -g $lang \ -o /local/generated-client-$lang \ --additional-properties=packageName=RhopointElementsHubClient done echo "Client generation completed." ``` ### PowerShell Script for Windows Create a script `Generate-Clients.ps1`: ```powershell # Download latest OpenAPI spec Invoke-WebRequest -Uri "http://localhost:42042/swagger/v1/swagger.json" -OutFile "swagger.json" # Define languages to generate $languages = @("csharp", "cpp-client", "python", "typescript-angular", "java", "go") foreach ($lang in $languages) { Write-Host "Generating $lang client..." docker run --rm ` -v ${PWD}:/local ` openapitools/openapi-generator-cli generate ` -i /local/swagger.json ` -g $lang ` -o /local/generated-client-$lang ` --additional-properties=packageName=RhopointElementsHubClient } Write-Host "Client generation completed." ``` ## Best Practices ### 1. Version Control - Keep the `swagger.json` file in version control. - Generate clients as part of your build process. - Tag client versions to match API versions. - Use a wrapper project to better separate the generated client from your actual project. ### 2. CI/CD Integration ```yaml # Example GitHub Actions workflow name: Generate API Clients on: push: paths: - 'swagger.json' jobs: generate-clients: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Generate C# Client run: | docker run --rm \ -v ${PWD}:/local \ openapitools/openapi-generator-cli generate \ -i /local/swagger.json \ -g csharp \ -o /local/clients/csharp ``` ### 3. Customization - Use custom templates when the default generation doesn't meet your needs - Validate generated code with your coding standards - Consider post-processing scripts for additional customization ## Troubleshooting ### Common Issues **Docker Volume Mounting:** - On Windows, ensure Docker Desktop has access to the drive containing your files. - Use absolute paths if relative paths don't work. **OpenAPI Specification Validation** ```bash # Validate your OpenAPI spec docker run --rm \ -v ${PWD}:/local \ openapitools/openapi-generator-cli validate \ -i /local/swagger.json ``` **Generator-Specific Issues:** - Check the [OpenAPI Generator documentation](https://openapi-generator.tech/docs/generators) for generator-specific options - Use `--help` flag to see available options for each generator ### Getting Help - Review available generators: https://openapi-generator.tech/docs/generators - Check configuration options: https://openapi-generator.tech/docs/configuration - Community support: https://github.com/OpenAPITools/openapi-generator/issues --- # Error Handling > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. All Elements Hub endpoints return a uniform JSON error response when a request cannot be fulfilled. The response carries enough information for a client to react programmatically (a stable, machine-readable `errorCode`) as well as for a human to debug the issue (a `message` and an `errorUrl`). ### Response Format Every error response uses the same `ErrorInfo` schema, regardless of which endpoint produced the error: ```json { "httpStatusCode": 404, "errorCode": "E5", "errorUrl": "https://id.rhopointservice.com/E5", "message": "No connected device was found with identifier '12f1d7dd07ac42a088c8f961b39d68ff'.", "exceptionType": "ConnectedDeviceNotFoundException", "stackTrace": null, "innerError": null, "details": null } ``` | Field | Type | Description | | --- | --- | --- | | `httpStatusCode` | int | The HTTP status code of the response (also present in the response headers). | | `errorCode` | string | Stable, machine-readable identifier of the form `E` (e.g. `E5`, `E42`). The single source of truth for what failed. Document and react to this value in your client. | | `errorUrl` | string \| null | Permanent URL with details about the error, of the form `https://id.rhopointservice.com/`. | | `message` | string | Human-readable explanation of the failure, often with the offending identifier or value interpolated. | | `exceptionType` | string \| null | The .NET exception type that produced the error. Useful for support tickets but should not drive client logic — use `errorCode` instead. | | `stackTrace` | string \| null | Only populated in development builds. Always `null` in production. | | `innerError` | object \| null | If the error wraps another error, a nested `ErrorInfo` describing the cause. | | `details` | object \| null | Additional contextual information about the error as a free-form key/value map (e.g. parameter that failed validation). | ### The `errorUrl` Pattern Every `errorCode` resolves to a dedicated page under `https://id.rhopointservice.com/`. For example: - `https://id.rhopointservice.com/E5` — connected device not found - `https://id.rhopointservice.com/E42` — invalid `imageFormat` parameter - `https://id.rhopointservice.com/E16` — unrecognised container format The page is the authoritative reference for each code and is kept in sync with the codebase. Treat it as your primary lookup when you encounter an unfamiliar `errorCode`. Linking directly to this URL from your own application's error UI is encouraged — the URL is stable across hub releases. ### How to React on the Client Recommended pattern: 1. Parse the response body as JSON whenever the HTTP status is in the `4xx` or `5xx` range. 2. Switch on `errorCode` — not on `message` and not on `exceptionType` — to drive recovery logic. 3. Surface `message` (and optionally `errorUrl`) in user-facing error displays. 4. Log the full `ErrorInfo` payload, including `innerError`, for support and debugging. Example client-side handling: ```bash curl -i http://localhost:42042/v1/devices/does-not-exist ``` ```http HTTP/1.1 404 Not Found Content-Type: application/json { "httpStatusCode": 404, "errorCode": "E5", "errorUrl": "https://id.rhopointservice.com/E5", "message": "No connected device was found with identifier 'does-not-exist'.", "exceptionType": "ConnectedDeviceNotFoundException", "stackTrace": null, "innerError": null, "details": null } ``` ### Validation Errors from ASP.NET In addition to `ErrorInfo` responses, a small number of endpoints — those that use built-in ASP.NET model validation — may return the standard `ProblemDetails` format on HTTP `400 Bad Request` when a request body is structurally invalid (missing required field, wrong type). These responses look like: ```json { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "One or more validation errors occurred.", "status": 400, "errors": { "DeviceIdentifier": [ "The DeviceIdentifier field is required." ] } } ``` Treat `ProblemDetails` as an indicator that the request never reached business logic — fix the request shape, then retry. Business-logic errors always use the `ErrorInfo` format above. ### Testing Error Handling The hub exposes a dedicated endpoint to provoke each major error category, useful for verifying client-side handling: ```http POST /v1/system/errors/raise-exception Content-Type: application/json { "errorType": "device" } ``` | `errorType` | Triggers | | --- | --- | | `device` | A simulated device-layer exception (`ErrorInfo`, with a non-zero `errorCode`). | | `notfound` | An HTTP 404 with `ErrorInfo`. | | `unhandled` | An uncaught exception, exercising the global exception handler. | Use these in your integration tests to ensure your client correctly parses `ErrorInfo` and reacts on `errorCode`, not on transport-level details. ### Best Practices - React on `errorCode`, not on `message` text — the message is localisable and may change between releases. The `errorCode` is contractually stable. - Always log `innerError` chains in full. The root cause is often deeper than the top-level message suggests. - For user-facing dialogs, show `message` and link to `errorUrl`. Do not surface `exceptionType` or `stackTrace` to end-users. - Treat any `5xx` response as transient unless `errorCode` indicates otherwise — retry with backoff before failing the operation. - Treat any `4xx` response (except `408`, `429`) as a permanent failure of that request — fix the request before retrying. --- # Install Elements Hub > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The latest version can be downloaded from [https://download.rhopointservice.net/elements-hub](https://download.rhopointservice.net/elements-hub) If you want to download a specific version, you can do so by specifying the version parameter, for example: [https://download.rhopointservice.net/elements-hub?version=1.6.4](https://download.rhopointservice.net/elements-hub?version=1.6.4) --- # Measurement Image Formats > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. This guide explains how to control the encoding of the device images that Elements Hub returns inside a measurement (RAE) container, and how to decode the raw image payload from C++. ## Background When a measurement is triggered against an Aesthetix device, the resulting RAE container can include one or more device images — for example the scratch view, the surface view, the LED sparkle stack or the LED shade stack. By default these images are encoded as PNG. Callers can choose the encoding per measurement: PNG, JPEG, or the unaltered raw bytes that the device sent. ## Selecting the encoding The encoding is selected through the optional `imageFormat` operation parameter when triggering a measurement: ```http POST /v1/devices/{deviceId}/measurement-triggers Content-Type: application/json { "measurementKey": "measure", "containerFormat": "raeBinary", "parameters": [ { "key": "metricGroupKeys", "value": ["..."] }, { "key": "imageFormat", "value": "image/x-raw" } ] } ``` | `imageFormat` value | Encoding used in the container | Container MIME type | |---|---|---| | *(omitted)* | PNG (default) | `image/png` | | `image/png` | PNG | `image/png` | | `image/jpeg` | JPEG, quality 90 | `image/jpeg` | | `image/x-raw` | Raw device bytes (see below) | `image/x-raw` | Unknown values are rejected with HTTP 400 and the error code `E42InvalidImageFormat`. > [!note] > A small number of device images (surface, waviness, spot) are post-processed (cropped) on the hub side before being encoded. With `image/png` and `image/jpeg` you receive the cropped variant; with `image/x-raw` you receive the original, **uncropped** bytes as emitted by the device. Most images — including the shade images (`ledShadeImages_*`) — are not post-processed, so all three encodings cover identical pixel data. ## Locating images in the container The container exposes the device images as `File` components in the measurement tree. Array images such as the shade stack use one entry per slot with a numeric suffix: ``` ledShadeImages (Array) ├── ledShadeImages_0 (File, MimeType=image/x-raw) ├── ledShadeImages_1 (File) ├── ledShadeImages_2 (File) ├── ledShadeImages_3 (File) ├── ledShadeImages_4 (File) └── ledShadeImages_5 (File) ``` The `BinaryData` field of each `File` component holds the byte stream — either the encoded PNG/JPEG or the raw layout described in the next section, depending on the `imageFormat` you requested. ## Layout of `image/x-raw` Every raw image starts with a fixed 16-byte little-endian header, immediately followed by the pixel data: ``` +------------------------------------------+ | Header (16 bytes, little-endian) | +------------------------------------------+ | Offset 0..3 : Height (int32)| | Offset 4..7 : Width (int32)| | Offset 8..11 : Channels (int32)| | Offset 12..15 : BytesPerPixel (int32)| +------------------------------------------+ | Pixel data (Height * Stride bytes) | | Row-major, top-down | +------------------------------------------+ ``` - **Endianness**: little-endian for every header field. - **Stride** (bytes per row): `(Width * Channels * BytesPerPixel * 8 + 7) / 8` — byte-aligned, no row padding. For all current device images this is simply `Width * Channels * BytesPerPixel`. - **No padding** between the header and the pixel data. ### Pixel formats The combination of `Channels` and `BytesPerPixel` describes the pixel layout: | Channels | BytesPerPixel | Pixel format | Element type | |---|---|---|---| | 1 | 1 | 8-bit grayscale | `uint8` | | 1 | 4 | 32-bit float grayscale (e.g. physical measurement values) | `float32` | | 3 | 1 | 24-bit colour, **BGR order** (not RGB) | `uint8[3]` | No other combinations occur in current Aesthetix firmware. > [!tip] > Parse the format out of the header rather than hard-coding a format per image type. The device firmware may change which format it uses for a given image in future updates. ## Parsing the raw stream in C++ ### Header & pixel view (endian-safe, no external dependencies) ```cpp #include #include #include struct AesthetixRawImage { int32_t height; int32_t width; int32_t channels; int32_t bytesPerPixel; const uint8_t* pixels; // points into the original buffer std::size_t pixelByteCount; }; inline int32_t readInt32LE(const uint8_t* p) { return static_cast( static_cast(p[0]) | static_cast(p[1]) << 8 | static_cast(p[2]) << 16 | static_cast(p[3]) << 24); } AesthetixRawImage parseAesthetixRaw(const uint8_t* data, std::size_t size) { if (size < 16) throw std::runtime_error("Header too short"); AesthetixRawImage img{}; img.height = readInt32LE(data + 0); img.width = readInt32LE(data + 4); img.channels = readInt32LE(data + 8); img.bytesPerPixel = readInt32LE(data + 12); const std::size_t expected = static_cast(img.height) * img.width * img.channels * img.bytesPerPixel; if (size - 16 < expected) throw std::runtime_error("Pixel buffer too short"); img.pixels = data + 16; img.pixelByteCount = expected; return img; } ``` ### Building an OpenCV `cv::Mat` OpenCV uses **BGR** internally, so it matches the Aesthetix colour layout without any `cvtColor` conversion. ```cpp #include cv::Mat toCvMat(const AesthetixRawImage& img) { int cvType; if (img.channels == 1 && img.bytesPerPixel == 1) cvType = CV_8UC1; else if (img.channels == 1 && img.bytesPerPixel == 4) cvType = CV_32FC1; else if (img.channels == 3 && img.bytesPerPixel == 1) cvType = CV_8UC3; // BGR else throw std::runtime_error("Unsupported pixel format"); // Wraps the bytes without copying; clone if the source buffer may be freed. return cv::Mat(img.height, img.width, cvType, const_cast(img.pixels)).clone(); } ``` ### Normalising float images for display ```cpp // channels=1, bytesPerPixel=4 holds real float32 values (e.g. physical // quantities) whose range is not bounded to [0,1]. Scale before rendering: cv::Mat displayable; cv::normalize(floatMat, displayable, 0, 255, cv::NORM_MINMAX, CV_8UC1); ``` ## Common pitfalls - **BGR is not RGB.** Three-channel images use BGR ordering. Swap the channels (or rely on OpenCV's native BGR handling) before passing the data to libraries that expect RGB. - **Float images need scaling.** `bytesPerPixel = 4` images are real `float32` data whose value range is not bounded to `[0, 1]`. Renderers must normalise the values. - **Read header values with `memcpy` or byte-shifting.** A `reinterpret_cast` is undefined behaviour on architectures with strict alignment when the buffer is not 4-byte aligned. The `readInt32LE` helper above sidesteps this and is also endian-portable. - **Compute the stride; don't hard-code it.** Although the byte size today is `Width * Channels * BytesPerPixel`, computing the stride keeps your code robust if new pixel formats are introduced. - **Validate header values.** Reject negative or implausibly large `Height`/`Width` values before multiplying them; a corrupt stream could otherwise overflow `size_t`. - **PNG/JPEG of post-processed images vs. raw originals.** A handful of device images (surface, waviness, spot) are cropped server-side before being encoded as PNG or JPEG. The `image/x-raw` payload is the **uncropped** original. The shade images and most other images are not post-processed, so all three encodings cover identical pixel data. ## See also - [Swagger UI Guide](rhopoint-elements-hub-swagger-ui-guide.md) — explore the `measurement-triggers` endpoint interactively and try the different `imageFormat` values. - [Client Code Generation](rhopoint-elements-hub-client-code-generation.md) — generate a strongly typed REST client for C++ (`cpp-restsdk`) from the OpenAPI specification. --- # Mock Devices > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. Mock devices in Elements Hub provide simulated device behaviour for development, testing and demonstration. They implement the same interfaces as real devices but return predictable, simulated data without requiring physical hardware. Every endpoint described in this manual works against a mock device — including [scans](rhopoint-elements-hub-endpoints-functionality-device-scans.md), [connect/disconnect](rhopoint-elements-hub-endpoints-functionality-connected-devices.md), [measurements](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md) and [image streams](rhopoint-elements-hub-endpoints-functionality-image-streams.md). ### When to Use Mock Devices - **Development**: build and test integrations without physical instruments. - **CI / automated tests**: deterministic, hardware-free test runs. - **Demonstrations**: show the API surface without a hardware setup. - **Training**: learn the workflow before touching real equipment. ### Available Mock Device Classes | Device class | Backed by | Behaviour | | --- | --- | --- | | `aesthetix-mock` | `AesthetixMockDevice` | Simulates an Aesthetix; loads measurement data from a `.rae` file (see [Mock data file](#mock-data-file) below); fixed serial number `AEX8000000`. | | `aesthetix-inline-mock` | `AesthetixMockDevice` (alias) | Identical to `aesthetix-mock` — same implementation, exposed under the inline class name for client code that scans by device class. | | `id-inline-mock` | `IdInlineMockDevice` | Simulates an ID Inline; returns predefined inspection metrics and procedurally generated test images; fixed serial number `12345`. | Apart from the device class, mock devices are addressed and used exactly like real devices — there is no separate URL prefix or parameter. ### Using a Mock Device Just substitute the device class in a regular [scan request](rhopoint-elements-hub-endpoints-functionality-device-scans.md): ```http POST /v1/device-scans Content-Type: application/json { "deviceClass": "aesthetix-mock" } ``` ```bash curl -X POST http://localhost:42042/v1/device-scans \ -H "Content-Type: application/json" \ -d '{ "deviceClass": "aesthetix-mock" }' ``` Then poll for the discovered device, connect, and use it as documented for the real variant: ```bash # Wait for discovery curl http://localhost:42042/v1/available-devices # Connect using the identifier from the response curl -X POST http://localhost:42042/v1/devices \ -H "Content-Type: application/json" \ -d '{ "deviceIdentifier": "" }' # Trigger a measurement — same payload as for a real device curl -X POST http://localhost:42042/v1/devices//measurement-triggers \ -H "Content-Type: application/json" \ -d '{ "measurementKey": "measure", "containerFormat": "raeBinary" }' \ --output measurement.rae ``` The `` is returned by `GET /v1/available-devices` and is a hex string generated by the hub at scan time — it is **not** derived from the device class or serial number. ### What the Mocks Actually Return #### `aesthetix-mock` / `aesthetix-inline-mock` - **Connect**: succeeds instantly, no hardware initialisation. - **Calibrations**: same set as the real device (see [Calibration](rhopoint-elements-hub-instruments-aesthetix-calibration.md)) but with simulated execution. - **Measurements**: the hub reads a pre-recorded `.rae` file from disk and returns its first measurement (see [Mock data file](#mock-data-file)). If the file is missing, `POST /measurement-triggers` fails with [`E20UnableToFindMockFile`](rhopoint-elements-hub-error-handling.md). - **Images**: procedurally generated colour-grid patterns, PNG-encoded, with the source-key label overlaid. #### `id-inline-mock` - **Connect**: succeeds instantly. Accepts the real device's `flipX` / `flipY` parameters but does not act on them. - **Measurements**: returns a small fixed set of metrics (haze, sharpness, clarity, waviness, transmission) plus a generated sample image. - **Images**: procedurally generated 1280 × 1024 BMP patterns, returned with MIME type `image/bmp`. ### Mock Data File The Aesthetix mock devices need a real `.rae` file on disk to return measurement data. The path is fixed: ``` {dataDirectory}/aesthetix-mock.rae ``` `{dataDirectory}` is the `dataDirectory` passed to [`POST /v1/lifecycle/initialize`](rhopoint-elements-hub-endpoints-functionality-lifecycle.md). If the file is missing, every measurement request fails with `E20UnableToFindMockFile`. There is no `appsettings.json` switch to point the mock at a different file — drop a recorded `.rae` at the path above instead. > [!tip] > A working starting point is any RAE file produced by a real Aesthetix measurement. Copy it to `{dataDirectory}/aesthetix-mock.rae` once and every subsequent mock measurement replays it. ### Mock vs Real Devices | Aspect | Mock device | Real device | | --- | --- | --- | | Scanning | Instant discovery | Hardware-dependent timing | | Connection | Always succeeds | May fail; see [`E8`](rhopoint-elements-hub-error-handling.md) | | Calibrations | Simulated, no physical target needed | Requires the physical reference target | | Measurements | File-replay (Aesthetix) or fixed data (ID Inline) | Live sensor readings | | Images | Generated grid patterns | Camera captures | | Streams | Same SSE protocol, generated frames | Real-time camera frames | | Performance | Deterministic | Variable | ### Best Practices - Develop and CI-test against the mock; switch to the real device only for final validation. - Pin the test `.rae` file in version control alongside the integration tests that consume it. - Keep mock-only assumptions out of integration tests — the workflow should be identical to the real-device path, only the device class changes. - For tests that exercise the failure path, deliberately omit the mock `.rae` file and assert on `E20UnableToFindMockFile`. ### Related Pages - [Device Scans](rhopoint-elements-hub-endpoints-functionality-device-scans.md) — scan request shape and response format - [Connected Devices](rhopoint-elements-hub-endpoints-functionality-connected-devices.md) — connect/disconnect lifecycle - [Measurement Triggers](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md) — measurement payload - [Error Handling](rhopoint-elements-hub-error-handling.md) — `ErrorInfo` schema and error codes --- # Security Notice > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. > [!warning] > Elements Hub does **not** currently provide any built-in authentication, authorisation or transport security. Anyone who can reach the listening socket can call every endpoint described in this manual, including connecting and disconnecting devices, triggering calibrations, and reading live image streams. Deploy the hub only on trusted networks, behind appropriate network controls. This page summarises the security-relevant defaults of the hub, the threat model they imply, and the practical mitigations available to integrators. ## Current Security Posture | Layer | Default | Implication | | --- | --- | --- | | Authentication | None | Any caller is treated as fully privileged. | | Authorisation | None | All endpoints are equally reachable. | | Transport | HTTP (cleartext) | Traffic is observable on the local network. HTTPS is not configured out of the box. | | Network binding | `http://localhost:42042` by default | Only loopback connections are accepted unless explicitly changed. | | CORS | `AllowedHosts: "*"` | Cross-origin requests are not restricted by the hub. The browser still enforces the same-origin policy unless CORS is configured. | | Rate limiting | None | A misbehaving client can pin a device or saturate the host. | The hub is designed to live next to the instrument on a controlled workstation or in a controlled production network — not as an internet-facing service. ## Network Binding The hub listens on `http://localhost:42042` by default. The chosen port can be observed in the startup log and is also exposed via the [Swagger UI Guide](rhopoint-elements-hub-swagger-ui-guide.md) at `http://:/swagger/`. To bind a different address — for example to expose the hub to other machines on the LAN — pass `--urls`: ```bash # Loopback only (default behaviour, explicit form) ElementsHub.exe --urls "http://localhost:42042" # All interfaces — exposes the hub to every reachable network ElementsHub.exe --urls "http://0.0.0.0:42042" # A specific NIC IP ElementsHub.exe --urls "http://192.168.10.5:42042" ``` > [!warning] > Binding to `0.0.0.0` or to a non-loopback address makes every endpoint reachable from any host that can route to the chosen IP. Combined with the lack of authentication, this is equivalent to placing administrative control of the instrument on an open network share. Do this only in segregated networks where every participating host is trusted. If the default port is already in use, the hub falls back to a free port on the same loopback address and prints the actual binding in the startup log. Clients that hard-code `42042` must be prepared to receive an alternative port. ## Cleartext Traffic The hub does not configure HTTPS by default. Measurement results, image streams and configuration data are transmitted in cleartext. On a single workstation this is generally acceptable; over a shared network it is not. If you must expose the hub across a network, place an HTTPS-terminating reverse proxy (IIS, nginx, Caddy, Envoy) in front of it and disable the hub's direct binding on the external interface. The proxy can also add the authentication that the hub itself lacks. ## Threat Model Assume any process or user that can connect to the hub's listening socket can: - Connect any discovered device and read live image streams from it. - Trigger calibrations, including ones that overwrite previously stored reference values. - Read or download measurement containers — including ones still in progress. - Read the hub's log file via `GET /v1/logs`, which may contain device serial numbers and operational metadata. - Initiate an in-place application update via `POST /v1/app-updates/action`. Do **not** treat the listening socket as confidential by virtue of running on a workstation. On a multi-user host, every local user account can reach `localhost:42042`. ## Recommended Mitigations For each deployment scenario, pick the smallest set that brings residual risk into your operational comfort zone: | Scenario | Mitigation | | --- | --- | | Single-user lab workstation, single integrator | Keep the default loopback binding. Do not change `--urls`. | | Multi-user workstation | Restrict access to the hub's listening port via Windows Firewall to the integrator's account. | | LAN-attached production cell | Run the hub behind a reverse proxy that terminates TLS and enforces authentication (mTLS, OAuth or API key headers). Bind the hub itself to loopback so it is only reachable through the proxy. | | Multiple instruments per workstation | One hub instance per device, bound to distinct loopback ports. | | Remote support / diagnostics | Use an out-of-band channel (RDP, SSH tunnel) rather than exposing the hub directly. | ## Future Work Authenticated, authorised and TLS-protected access is on the roadmap. Until shipped, treat this page as the authoritative description of the hub's security posture — do not assume any implicit protection. --- # Starting the application > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. To start the application, find it in the Start menu or locate the executable in `%LOCALAPPDATA%\com.rhopointinstruments.elements-hub.release\current` --- # Swagger UI Guide > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. This guide explains how to use the interactive Swagger UI to explore, test, and understand the Elements Hub REST API without writing any code. ## Accessing Swagger UI ### 1. Start Elements Hub Server Ensure your Elements Hub server is running. By default, it runs on [http://localhost:42042](http://localhost:42042) > [!note] Replace localhost:42042 with your actual server address and port. ### 2. Open Swagger UI Navigate to the Swagger UI in your web browser: [http://localhost:42042/swagger/](http://localhost:42042/swagger/)\ The server automatically redirects the root URL (`/`) to the Swagger UI, so you can also simply visit: [http://localhost:42042](http://localhost:42042) ![Swagger UI](../_images/1758204210663-swagger-ui.png) ## Swagger UI Interface Overview ### Main Components The Swagger UI interface consists of several key sections: 1. **API Information Header** - API title and version - Server information - Base URL 2. **Endpoint Groups (Tags)** - Organized by functionality (System, Lifecycle, Devices, etc.) - Collapsible sections for better navigation 3. **Individual Endpoints** - HTTP method and path - Brief description - Parameters and response information 4. **Global Controls** - Authorization settings - Server selection - Response format options ## Exploring API Endpoints ### Expanding Endpoint Groups Click on any endpoint group to expand it and see the available operations. ### Understanding HTTP Methods Each endpoint shows its HTTP method with colour coding: - **🟢 GET** (Green) - Retrieve data - **🔵 POST** (Blue) - Create or trigger operations - **🟡 PUT** (Yellow) - Update existing resources - **🔴 DELETE** (Red) - Remove resources ### Viewing Endpoint Details Click on any individual endpoint to expand its details: ![Open GET](../_images/1758204963242-swagger-ui-open.png) ## Testing API Endpoints ### Simple GET Request Example Let's test the system version endpoint: 1. **Expand the System group** and click on `GET /v1/system/version` 2. **Click "Try it out"** button ![Try it out](../_images/1758205633602-swagger-ui-try-it.png) 3. **Click "Execute"** to make the request ![Execute GET](../_images/1758205861241-swagger-ui-execute.png) The response will show: - **Response Code** (e.g., 200 for success) - **Response Body** with the actual data - **Response Headers** - **Request URL** that was called ![GET response](../_images/1758205951464-swagger-ui-get-response.png) ### POST Request with Parameters Let's test initialising the lifecycle services: 1. **Expand the Lifecycle group** and click on `POST /v1/lifecycle/initialize` 2. **Click "Try it out"** 3. **Edit the request body** in the text area: ```json { "dataDirectory": "C:\\ProgramData\\ElementsHub\\Data", "logDirectory": "C:\\ProgramData\\ElementsHub\\Logs" } ``` ![POST with payload](../_images/1758206445946-swagger-ui-payload.png) 4. **Click "Execute"** ### POST Request with Path Parameters For endpoints that require path parameters: 1. **Expand Connected Devices** and click on `GET /v1/devices/{deviceId}` 2. **Click "Try it out"** 3. **Enter the device ID** in the parameter field 4. **Click "Execute"** ## Understanding Request and Response Schemas ### Request Body Schemas When an endpoint accepts a request body, Swagger UI shows: - **Property names** and their types - **Required fields** (marked with \*) - **Example values** - **Property descriptions** You can click on the schema to see more details and copy the example JSON. ### Response Schemas Each endpoint shows possible response codes and their schemas: - **200 Success** responses with data structure - **400 Bad Request** error format - **404 Not Found** responses - **Other status codes** as applicable ## Working with Different Content Types ### Image Responses Some endpoints return images (like device camera captures): 1. **Navigate to** `GET /v1/devices/{deviceId}/images/latest` 2. **Select the appropriate Accept header** (e.g., `image/jpeg`) 3. **Execute the request** The response will show the image data or provide a download link. ### File Downloads For endpoints that return files, Swagger UI will provide options to download or view the file content. ## Error Handling and Debugging ### Common Error Responses When requests fail, Swagger UI displays helpful error information. ### Request Validation Errors If your request doesn't match the expected schema, Swagger UI will highlight: - Missing required fields - Invalid data types - Out-of-range values - Format violations ### Network and Server Errors For connectivity issues check: - Server is running - Correct URL and port - Network connectivity - Firewall settings ## Advanced Features ### Multiple API Versions If multiple API versions are available, you can switch between them: ### Server Selection If multiple servers are configured, you can select which one to use: ### Downloading OpenAPI Specification You can download the raw OpenAPI specification: Look for a link to download the `swagger.json` file for use with code generators. ## Best Practices for Testing ### 1. Start with System Endpoints Always begin by testing basic system endpoints: - `GET /v1/system/version` - Verify server is running - `POST /v1/lifecycle/initialize` - Initialize services ### 2. Follow the Logical Flow For device operations, follow this sequence: 1. Initialize lifecycle services 2. Start a device scan 3. Check available devices 4. Connect to a device 5. Perform device operations 6. Disconnect when done ### 3. Check Dependencies Some endpoints depend on others being called first: - Device operations require initialization. - Device connections require scanning. - Measurements require connected devices. ### 4. Use Realistic Test Data When testing with sample data: - Use valid file paths for directory parameters. - Use realistic device identifiers. - Follow the expected data formats. ### 5. Monitor Response Times Pay attention to response times for operations: - Some operations (like device scanning) may take time. - Check for appropriate timeout handling. - Consider async patterns for long-running operations. ## Troubleshooting Common Issues ### "Try it out" Button Not Working - Ensure JavaScript is enabled in your browser. - Check for browser console errors (press `F12` on your keyboard). - Try refreshing the page. ### CORS Errors If testing from a different domain: - CORS may need to be configured on the server or proxy. - Try accessing from the same domain as the API. ### Large Response Handling For endpoints that return large amounts of data: - UI may slow down or freeze. - Responses may be truncated in the UI. - Consider using dedicated API clients for large data sets. - Check response size limits. ## Integrating with Development Workflow ### Documentation and Discovery Use Swagger UI for: - **API Documentation** - Understanding available endpoints. - **Schema Discovery** - Learning request/response formats. - **Quick Testing** - Validating API behaviour. - **Example Generation** - Getting sample requests for development. ### Code Generation Preparation After exploring with Swagger UI: 1. Download the OpenAPI specification. 2. Use it with code generators (see chapter Client Code Generation). 3. Reference the tested examples in your generated clients. ## Tips for Effective API Exploration ### 1. Start Simple Begin with read-only operations (GET requests) before attempting modifications. ### 2. Keep Notes Document successful request patterns for later reference in your applications. ### 3. Test Edge Cases Try invalid inputs to understand error handling and validation rules. ### 4. Understand State Some operations change system state - be aware of the order of operations. ### 5. Use Browser Developer Tools Monitor network requests in browser development tools for additional debugging information. --- # Endpoints - Functionality > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The process and order of calls is nearly identical for all instruments. This ensures consistency and simplifies integration across different devices. Following a standardized sequence also makes it easier to debug, extend, and maintain the system over time. The typical workflow follows these steps: 1. **[Lifecycle — Initialize](rhopoint-elements-hub-endpoints-functionality-lifecycle.md)** The system is initialized and prepared for operation. This step sets up the environment, allocates necessary resources, and ensures the application is ready to interact with instruments. 2. **[Start a Device Scan](rhopoint-elements-hub-endpoints-functionality-device-scans.md)** The system performs a scan to discover available instruments. This ensures that only devices that are present, powered, and accessible are considered for connection. 3. **[Connect to Device](rhopoint-elements-hub-endpoints-functionality-connected-devices.md)** Once a specific instrument is identified, you can establish a connection. At this point, communication channels are opened and verified. 4. **[Stop the Device Scan](rhopoint-elements-hub-endpoints-functionality-device-scans.md#stopping-a-scan)** After a successful connection, scanning should be stopped to reduce resource usage and avoid unnecessary interference with the established session. 5. **[Trigger Measurements or Execute Commands](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md)** The core functionality takes place here. Measurements can be triggered, data can be acquired, or commands can be executed depending on the device's capabilities and the needs of the workflow. 6. **[Disconnect from Device](rhopoint-elements-hub-endpoints-functionality-connected-devices.md#disconnecting-a-device)** When all operations are completed, the instrument should be disconnected. This step ensures a clean release of resources and prevents potential conflicts with future connections. 7. **[Lifecycle — Shutdown](rhopoint-elements-hub-endpoints-functionality-lifecycle.md)** The system is shut down gracefully. Resources are released, processes are closed, and the environment is returned to a safe, stable state. --- ### Why This Process Matters - **Consistency**: All instruments follow the same flow, which reduces the learning curve for developers. - **Reliability**: Structured initialization and shutdown help prevent crashes and resource leaks. - **Scalability**: Adding new instruments is easier when they fit into a standardized lifecycle. - **Debugging**: Problems are easier to isolate when every session follows a predictable sequence. --- ### Endpoint Reference | Page | Endpoints | | --- | --- | | [Lifecycle](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) | `POST /v1/lifecycle/initialize`, `POST /v1/lifecycle/shutdown` | | [Device Scans](rhopoint-elements-hub-endpoints-functionality-device-scans.md) | `POST/GET/DELETE /v1/device-scans`, `GET /v1/available-devices` | | [Connected Devices](rhopoint-elements-hub-endpoints-functionality-connected-devices.md) | `POST/GET/DELETE /v1/devices`, property lookup | | [Measurement Triggers](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md) | `GET/POST /v1/devices/{deviceId}/measurement-triggers`, `POST /v1/measurements/decode` | | [Calibrations](rhopoint-elements-hub-endpoints-functionality-calibrations.md) | `GET/POST /v1/devices/{deviceId}/calibrations`, `GET /calibrations/status` | | [Image Streams](rhopoint-elements-hub-endpoints-functionality-image-streams.md) | `POST/DELETE /v1/devices/{deviceId}/image-streams/{sourceKey}`, `start`/`configure`/`snapshot`/`stream` | | [System](rhopoint-elements-hub-endpoints-functionality-system.md) | `GET /v1/system/version`, `GET /v1/system/checks`, `POST /v1/system/errors/raise-exception` | | [App Updates](rhopoint-elements-hub-endpoints-functionality-app-updates.md) | `GET /v1/app-updates/info`, `POST /v1/app-updates/action` | Cross-cutting topics relevant to every endpoint: - [Error Handling](rhopoint-elements-hub-error-handling.md) — uniform `ErrorInfo` response format and `id.rhopointservice.com/E` reference URLs - [Security Notice](rhopoint-elements-hub-security-notice.md) — current authentication/transport posture and recommended deployment mitigations - [Measurement Image Formats](rhopoint-elements-hub-measurement-image-formats.md) — encoding of device images inside measurement containers (PNG / JPEG / raw) - [RAE File Format](rhopoint-elements-hub-rae-file-format.md) — binary layout of the measurement container This lifecycle ensures that instruments are used efficiently, reliably, and in a way that can be repeated across multiple environments and use cases. --- # App Updates > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The app-update endpoints expose the hub's self-update mechanism. The hub uses [Velopack](https://velopack.io/) under the hood to discover, download and apply releases. The endpoints surface that machinery through a small REST API so integrators can check for new versions, drive the update process from their own UI, and observe the resulting state without restarting the hub themselves until they are ready. ### Endpoints Overview | Method | Endpoint | Description | | --- | --- | --- | | GET | `/v1/app-updates/info` | Query the current update state (also performs a fresh update check) | | POST | `/v1/app-updates/action` | Drive the update workflow (Check / Download / Apply and Restart) | ### Update State ```http GET /v1/app-updates/info ``` ```bash curl http://localhost:42042/v1/app-updates/info ``` Performs an update check against the configured release channel and returns the resulting state: ```json { "currentVersion": "1.7.0", "availableVersion": "1.8.0", "updateAvailable": true, "updateDownloaded": false, "pendingRestart": false, "portable": false, "installed": true } ``` | Field | Type | Description | | --- | --- | --- | | `currentVersion` | string | The running hub version. `"###version###"` indicates an unbuilt developer copy. | | `availableVersion` | string | The newest version offered by the release feed, or `"-"` if no newer release is available. | | `updateAvailable` | bool | `true` if `availableVersion` is newer than `currentVersion`. | | `updateDownloaded` | bool | `true` once `Download` has finished and the package is staged on disk. | | `pendingRestart` | bool | `true` after `ApplyAndRestart` has prepared the swap and is waiting for the hub process to exit. | | `installed` | bool | `true` if the hub is running from an installed location (vs. a portable copy). | | `portable` | bool | `true` if the hub is running as a portable distribution. Updates may behave differently in this mode. | Calling `GET /info` triggers a fresh check against the release feed, so it is safe to use as the primary refresh entry point — there is no separate "refresh" verb. ### Driving the Update Workflow ```http POST /v1/app-updates/action?action=download ``` ```bash curl -X POST "http://localhost:42042/v1/app-updates/action?action=download" ``` The action is passed as a query-string parameter. No request body is required. | `action` value | Effect | Preconditions | | --- | --- | --- | | `check` | Re-runs the update check, refreshing `availableVersion` and `updateAvailable`. | None. | | `download` | Downloads the available release package in the background and stages it on disk. After completion `updateDownloaded` flips to `true`. | An update must be available (`check` has reported one). | | `applyAndRestart` | Applies the downloaded package and restarts the hub process. The client should expect the connection to drop. | `updateDownloaded` must be `true`. | Values are matched case-insensitively against the enum names, so `Check`, `check` and `CHECK` are equivalent. The response body of `POST /action` has the same shape as `GET /info` — call it once and observe the resulting state. ### Update Workflow ``` ┌──────────────────────┐ │ GET /info │ Initial state — reports updateAvailable └──────────┬───────────┘ │ updateAvailable == true ▼ ┌──────────────────────┐ │ POST /action "check" │ (optional, refreshes the check) └──────────┬───────────┘ │ ▼ ┌──────────────────────────┐ │ POST /action "download" │ Stages the package └──────────┬───────────────┘ │ updateDownloaded == true ▼ ┌──────────────────────────────────┐ │ POST /action "applyAndRestart" │ Hub restarts; connection drops └──────────────────────────────────┘ ``` Typical client implementation: 1. Call `GET /info` at start-up (or on a slow timer) and surface the update banner when `updateAvailable` is `true`. 2. When the user agrees to update, `POST /action "download"`. Show progress as indeterminate (the API does not currently expose a percentage). 3. Once `updateDownloaded` is `true`, prompt the user to apply. On confirmation, `POST /action "applyAndRestart"` and treat the next failed request as the expected restart signal. 4. After the hub is back up, call `GET /info` again. `currentVersion` should now match the previous `availableVersion` and `updateAvailable` should be `false`. ### Error Cases | Condition | HTTP | Body | | --- | --- | --- | | `download` / `applyAndRestart` called before `check` ever reported an update | 400 | `"There is new version available for update. Have you performed the update check first?"` | | `applyAndRestart` called before `download` finished | 424 | `"Update is not downloaded. Please download the update first."` | | Unknown action value | 400 | `"Unknown action \"\"."` | The body of these failures is a plain string, not an [`ErrorInfo`](rhopoint-elements-hub-error-handling.md) record, because the responses come from ASP.NET's built-in validation rather than the device error pipeline. Treat any non-2xx response as an indication that the request did not advance the update state. ### Best Practices - Poll `GET /info` no more often than once every few minutes. There is no rate-limiting, but the underlying check hits the release feed. - Do not call `applyAndRestart` without explicit user confirmation — connected devices are disconnected when the hub exits, which can interrupt a running measurement workflow. - Stop all device scans and measurements before applying an update. The hub graceful-shutdown path will handle this, but proactive cleanup avoids surprise errors on the client side. - Use `portable` and `installed` to decide whether to show the update UI at all. A portable copy can be replaced manually; an installed one is the typical update target. --- # Calibrations > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. Calibration brings a connected device's measurement chain back into agreement with a known reference (white, black, or height target). For instruments such as Aesthetix, a valid calibration is a precondition for trustworthy measurements — running a measurement on an uncalibrated device returns numbers, but they are not metrologically meaningful. The hub tracks the last calibration time per device and exposes a `status` endpoint so clients can prompt for re-calibration before it goes stale. ### Endpoints Overview | Method | Endpoint | Description | | --- | --- | --- | | GET | `/v1/devices/{deviceId}/calibrations` | List calibrations available on the connected device | | POST | `/v1/devices/{deviceId}/calibrations` | Execute a calibration | | GET | `/v1/devices/{deviceId}/calibrations/status` | Get current calibration status per calibration type | ### Listing Available Calibrations ```http GET /v1/devices/{deviceId}/calibrations ``` ```bash curl http://localhost:42042/v1/devices/12f1d7dd07ac42a088c8f961b39d68ff/calibrations ``` Returns the device's calibration definitions, including the parameters each one accepts. Example shape: ```json [ { "identifier": "white", "title": "White Calibration", "description": "Performs white reference calibration using the white calibration standard.", "category": "calibration", "parameters": [ { "key": "reflectance", "type": "Double", "description": "Percent, 0.42 ≙ 42 %" }, { "key": "visualContrast", "type": "Double", "description": "Percent, 0.42 ≙ 42 %" } ] } ] ``` The set of calibrations is device-class–specific. For example, an Aesthetix supports `white`, `black` and `height`, an Aesthetix Inline only `black`. See [Aesthetix-specific calibration details](rhopoint-elements-hub-instruments-aesthetix-calibration.md). ### Executing a Calibration ```http POST /v1/devices/{deviceId}/calibrations Content-Type: application/json { "calibrationKey": "white", "parameters": [ { "key": "reflectance", "value": 0.95 }, { "key": "visualContrast", "value": 0.92 } ] } ``` ```bash curl -X POST http://localhost:42042/v1/devices/12f1d7dd07ac42a088c8f961b39d68ff/calibrations \ -H "Content-Type: application/json" \ -d '{ "calibrationKey": "white", "parameters": [ { "key": "reflectance", "value": 0.95 }, { "key": "visualContrast", "value": 0.92 } ] }' ``` #### Request Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `calibrationKey` | string | Yes | Identifier from `GET /v1/devices/{deviceId}/calibrations` (e.g. `white`, `black`, `height`). | | `parameters` | array | No | Calibration parameters as `{ "key": "...", "value": ... }` entries. Keys and value types are device-class–specific (see `GET` response). | #### Response ```json { "calibrationKey": "white", "success": true } ``` The call is synchronous and only returns once the calibration has completed on the device. Calibration can take several seconds; clients should set a generous HTTP timeout (≥30 s). > [!warning] > The physical calibration target must be in place **before** the request is sent. Calibrating against the wrong target will produce a successful response but invalid downstream measurements. See the device-specific calibration page for which target to use with which `calibrationKey`. ### Getting the Calibration Status ```http GET /v1/devices/{deviceId}/calibrations/status ``` Returns the current state of every calibration type the device supports: ```json { "deviceId": "12f1d7dd07ac42a088c8f961b39d68ff", "deviceSerialNumber": "AXI8000027", "calibrations": [ { "calibrationKey": "white", "lastCalibrationTime": "2025-11-10T07:32:14.000Z", "status": "CalibrationValid", "recommendedInterval": "2.00:00:00", "overdueInterval": "7.00:00:00" }, { "calibrationKey": "black", "lastCalibrationTime": null, "status": "NotCalibrated", "recommendedInterval": "2.00:00:00", "overdueInterval": "7.00:00:00" } ] } ``` #### Status Values | Status | Meaning | Recommended action | | --- | --- | --- | | `NotCalibrated` | No calibration of this type has ever been performed for this device's serial number. | Calibrate before measuring. | | `CalibrationValid` | Last calibration is younger than `recommendedInterval`. | Continue measuring. | | `CalibrationRecommended` | Last calibration is older than `recommendedInterval` but younger than `overdueInterval`. | Calibrate at the next convenient point. | | `CalibrationOverdue` | Last calibration is older than `overdueInterval`. | Calibrate before further measurements. | The intervals follow .NET `TimeSpan` formatting (`d.hh:mm:ss`). Typical defaults are 2 days (recommended) and 7 days (overdue), but the device definition is authoritative — read the values from the response rather than hard-coding them. ### Persistence Calibration data is stored per **serial number + device class**, not per session identifier. Disconnecting and re-connecting a device — or restarting the hub — does not invalidate previously persisted calibrations. The storage location is the `dataDirectory` passed to [`POST /v1/lifecycle/initialize`](rhopoint-elements-hub-endpoints-functionality-lifecycle.md). ### Best Practices - After connecting a device, call `GET /v1/devices/{deviceId}/calibrations/status` and surface any `NotCalibrated`, `CalibrationRecommended` or `CalibrationOverdue` entries to the operator before allowing measurements to start. - Calibrate in the order recommended by the device manufacturer (typically Black → White → Height for Aesthetix). Some calibrations depend on earlier ones being valid. - Read `parameters` from `GET /v1/devices/{deviceId}/calibrations` and only send the values listed there — sending unknown keys is silently ignored, which makes typos hard to spot. - Time the calibration prompts using the `status` endpoint, not your own scheduler — the intervals can change between firmware updates. ### Error Cases | Condition | HTTP | Error Code | | --- | --- | --- | | Connected device not found | 404 | `E5` | | Unknown `calibrationKey` for this device | 404 | `E28` | | Calibration timed out (hardware likely disconnected) | 502 | `E29` | See [Error Handling](rhopoint-elements-hub-error-handling.md) for the response format. --- # Connected Devices > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. Once a device has been discovered by a [device scan](rhopoint-elements-hub-endpoints-functionality-device-scans.md), it can be connected and used for measurements, calibrations and image acquisition. A connected device occupies the hub's communication channel to the hardware until it is explicitly disconnected (or the hub is shut down). ### Endpoints Overview | Method | Endpoint | Description | | --- | --- | --- | | POST | `/v1/devices` | Connect a previously discovered device | | GET | `/v1/devices` | List all currently connected devices | | GET | `/v1/devices/{deviceId}` | Get a single connected device by identifier | | GET | `/v1/devices/{deviceId}/{propertyName}` | Read a single property of a connected device | | DELETE | `/v1/devices/{deviceId}` | Disconnect a device | ### Typical Workflow 1. [Initialize lifecycle services](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) 2. [Start a device scan](rhopoint-elements-hub-endpoints-functionality-device-scans.md) for the relevant device class 3. Poll `GET /v1/available-devices` until the target device appears 4. Connect with `POST /v1/devices` 5. Use the device for [measurements](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md), calibrations and image acquisition 6. Disconnect with `DELETE /v1/devices/{deviceId}` 7. [Shut down lifecycle services](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) ### Connecting a Device ```http POST /v1/devices Content-Type: application/json { "deviceIdentifier": "12f1d7dd07ac42a088c8f961b39d68ff" } ``` ```bash curl -X POST http://localhost:42042/v1/devices \ -H "Content-Type: application/json" \ -d '{ "deviceIdentifier": "12f1d7dd07ac42a088c8f961b39d68ff" }' ``` #### Request Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `deviceIdentifier` | string | Yes | Identifier returned by `GET /v1/available-devices`. | | `parameters` | array | No | Optional list of device-class–specific connection parameters as `{ "key": "...", "value": ... }` entries. Most devices require no parameters. | #### Response A successful connect returns the device record, now extended with a `connectTime` timestamp: ```json { "identifier": "12f1d7dd07ac42a088c8f961b39d68ff", "deviceClass": "aesthetix-inline", "serialNumber": "AXI8000027", "serialNumbers": ["AXI8000027"], "hardwareVersion": "…", "firmwareVersion": "…", "softwareVersion": "…", "componentVersions": [], "connectTime": "2025-11-12T08:16:02.1234567+00:00" } ``` ### Listing Connected Devices ```http GET /v1/devices ``` Returns an array of connected devices in the same shape as the connect response. An empty array means no devices are currently connected. ### Getting a Single Connected Device ```http GET /v1/devices/{deviceId} ``` Use the `identifier` value as `{deviceId}`. Returns the full device record or HTTP 404 (`E5`) if no device with that identifier is connected. ### Reading a Single Property ```http GET /v1/devices/{deviceId}/serialNumber ``` The property-lookup endpoint returns the value of a single field of the device record. Useful when a client only needs one piece of information and wants to avoid parsing the full record. Returns HTTP 404 (`E4`) if the property does not exist on the device. ### Disconnecting a Device ```http DELETE /v1/devices/{deviceId} ``` ```bash curl -X DELETE http://localhost:42042/v1/devices/12f1d7dd07ac42a088c8f961b39d68ff ``` Disconnect cleanly releases the device's communication channel and persists any pending calibration or configuration state. Always disconnect explicitly before shutting down the hub if possible — although `POST /v1/lifecycle/shutdown` will also disconnect every device, an explicit disconnect surfaces errors immediately instead of during shutdown. ### Behaviour on Re-Connect - Disconnecting and re-connecting the same device returns a **new** identifier — the identifier is tied to a discovery session, not the hardware. After a re-connect, update any cached identifiers on the client side. - Calibration data is persisted by serial number and device class, so a re-connected device retains its previous calibration state without further action. ### Best Practices - Always check `GET /v1/available-devices` for the target serial number before calling `POST /v1/devices` — the identifier returned by an older scan may no longer be valid if the scan was stopped in between. - Wrap the connect/use/disconnect sequence in a try/finally on the client side to guarantee disconnect even when measurement errors occur. - For long-running integrations, prefer one persistent connection over repeated connect/disconnect cycles — the subprocess startup cost is the main reason connecting is slower than measuring. - Use `GET /v1/devices` after start-up to detect devices that may already be connected (for example, after the hub was restarted while clients still held the previous identifier). ### Error Cases | Condition | HTTP | Error Code | | --- | --- | --- | | Identifier does not match any discovered device | 404 | `E3` | | Connect failed (hardware error, device busy, …) | 502 | `E8` | | Requested connected device not found | 404 | `E5` | | Requested property does not exist on device | 404 | `E4` | See [Error Handling](rhopoint-elements-hub-error-handling.md) for the response format. --- # Device Scans > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. Device scans discover instruments that are available to the host machine but not yet connected. A scan is an asynchronous, long-running background process — typically backed by a device-class–specific subprocess that polls hardware or the local network. Clients start a scan, poll the list of available devices, and either stop the scan once a device has been found or keep it running for continuous discovery. ### Endpoints Overview | Method | Endpoint | Description | | --- | --- | --- | | POST | `/v1/device-scans` | Start a new scan for a specific device class | | GET | `/v1/device-scans` | List all active scans | | GET | `/v1/device-scans/{scanId}` | Get a single scan by identifier | | GET | `/v1/device-scans/{scanId}/{propertyName}` | Read a single property of a scan | | DELETE | `/v1/device-scans/{scanId}` | Stop and remove a scan | > [!note] > Scans must be started against a known device class. The same device class is used later when connecting (see [Connected Devices](rhopoint-elements-hub-endpoints-functionality-connected-devices.md)). Common values are `aesthetix`, `aesthetix-inline`, `id-inline` and their `-mock` variants (see [Mock Devices](rhopoint-elements-hub-mock-devices.md)). ### Starting a Scan ```http POST /v1/device-scans Content-Type: application/json { "deviceClass": "aesthetix-inline" } ``` ```bash curl -X POST http://localhost:42042/v1/device-scans \ -H "Content-Type: application/json" \ -d '{ "deviceClass": "aesthetix-inline" }' ``` #### Request Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `deviceClass` | string | Yes | Class of device to scan for (e.g. `aesthetix`, `aesthetix-inline`, `id-inline`) | | `serialNumberFilter` | string | No | Restrict discovery to devices whose serial number matches the given pattern | | `ipAddressFilter` | string | No | For network-discovered devices, restrict discovery to a specific IP address or range | #### What happens internally Starting a scan spawns the device-class–specific subprocess (for Aesthetix-family devices that is `Aesthetix.exe` / `AesthetixKernel.exe`). The subprocess handles direct communication with the device, including taking measurements and calculating metrics. It is kept alive for as long as the scan or any device connection of that class is active. ### Listing Active Scans ```http GET /v1/device-scans ``` Example response: ```json [ { "identifier": "9d8e2f1a-…", "scanFilter": { "deviceClass": "aesthetix-inline", "serialNumberFilter": null, "ipAddressFilter": null }, "status": "running", "foundDevices": [ "12f1d7dd07ac42a088c8f961b39d68ff" ] } ] ``` The `foundDevices` array contains the identifiers of every available device found by this scan so far. To retrieve the full device records, use [the available-devices endpoints](rhopoint-elements-hub-endpoints-functionality-connected-devices.md). ### Polling Discovered Devices While a scan is running, query `GET /v1/available-devices` to retrieve the discovered devices and their metadata: ```http GET /v1/available-devices ``` Example response: ```json [ { "discovered": "2025-11-12T08:15:26.7825826+00:00", "identifier": "12f1d7dd07ac42a088c8f961b39d68ff", "deviceClass": "aesthetix-inline", "serialNumber": "AXI8000027", "serialNumbers": [ "AXI8000027" ], "hardwareVersion": "…", "firmwareVersion": "…", "softwareVersion": "…", "componentVersions": [] } ] ``` The `identifier` field is what you pass to `POST /v1/devices` to establish a connection. See [Connected Devices](rhopoint-elements-hub-endpoints-functionality-connected-devices.md) for the follow-up step. ### Stopping a Scan ```http DELETE /v1/device-scans/{scanId} ``` ```bash curl -X DELETE http://localhost:42042/v1/device-scans/9d8e2f1a-… ``` Stopping a scan tears down the discovery loop. If no connected device of the same class remains, the associated subprocess is shut down as well. ### When to Stop the Scan - **USB-attached devices (Aesthetix, ID Inline)**: stop the scan once the device has been found and you are about to connect. Keeping the scan running adds no value and consumes a subprocess slot. - **Network-discovered devices (Aesthetix Inline)**: it is acceptable to keep the scan running while working with the device — discovery is low-cost, and clients may want to detect hot-plugged devices on the same network. ### Best Practices - Always start a scan **after** calling [`POST /v1/lifecycle/initialize`](rhopoint-elements-hub-endpoints-functionality-lifecycle.md). The hub rejects scan requests if the lifecycle services have not been initialized. - Treat the scan identifier as opaque; do not parse or rely on its format. - Poll `GET /v1/available-devices` rather than `GET /v1/device-scans/{scanId}` to read the discovered devices — `available-devices` returns full device records, `device-scans` returns identifiers only. - Stop scans you no longer need to free the underlying subprocess. - On shutdown, [`POST /v1/lifecycle/shutdown`](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) stops all scans automatically; explicit `DELETE` is only required when you want to stop a scan mid-session. --- # Image Streams > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. Image streams expose a continuous, low-latency video-like feed from a device's camera. They are intended for live preview, alignment and focus assistance — not for archival capture (use [measurements](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md) for that). A stream is configured once via `POST .../start`, then consumed either as a Server-Sent-Events feed for real-time delivery or polled frame-by-frame via the snapshot endpoint. ### Endpoints Overview | Method | Endpoint | Description | | --- | --- | --- | | POST | `/v1/devices/{deviceId}/image-streams/{sourceKey}/start` | Start a stream for a specific image source | | POST | `/v1/devices/{deviceId}/image-streams/{sourceKey}/configure` | Change exposure / scale / encoding while running | | GET | `/v1/devices/{deviceId}/image-streams/{sourceKey}/stream` | Subscribe to live frames via SSE (`text/event-stream`) | | GET | `/v1/devices/{deviceId}/image-streams/{sourceKey}/snapshot` | Pull a single frame (binary image) | | GET | `/v1/devices/{deviceId}/image-streams` | List all active streams on a connected device | | DELETE | `/v1/devices/{deviceId}/image-streams/{sourceKey}` | Stop and tear down the stream | ### Source Keys A device may expose more than one camera or imaging path. Each is addressed by a `sourceKey`. For Aesthetix devices the available keys are: | `sourceKey` | Camera | | --- | --- | | `spec` | Specular path | | `aspec` | Aspecular path | Other device classes may expose different keys; consult the device's documentation. ### Lifecycle ``` ┌──────────────────────────────┐ │ POST .../{sourceKey}/start │ ← configure exposure, scale, encoding └──────────────┬───────────────┘ │ ┌─────────┴─────────┐ │ │ ▼ ▼ GET .../stream GET .../snapshot (SSE feed) (single frame) │ │ │ POST .../configure (optional, while running) │ │ └─────────┬─────────┘ ▼ ┌──────────────────────────────┐ │ DELETE .../{sourceKey} │ └──────────────────────────────┘ ``` A stream remains active until explicitly stopped via `DELETE` or until the device is disconnected. Reconnecting to the device does not automatically restore previously running streams. ### Starting a Stream ```http POST /v1/devices/{deviceId}/image-streams/{sourceKey}/start Content-Type: application/json { "exposureTimeMilliseconds": 50, "scaleFactor": 0.5, "mimeType": "image/jpeg", "quality": 0.85 } ``` ```bash curl -X POST http://localhost:42042/v1/devices/12f1d7dd07ac42a088c8f961b39d68ff/image-streams/aspec/start \ -H "Content-Type: application/json" \ -d '{ "exposureTimeMilliseconds": 50, "scaleFactor": 0.5, "mimeType": "image/jpeg", "quality": 0.85 }' ``` #### Request Body All fields are optional — omit them to use the server defaults. | Field | Type | Default | Description | | --- | --- | --- | --- | | `exposureTimeMilliseconds` | double | device-dependent | Sensor exposure per frame. Typical range 1–1000 ms. Higher values yield brighter frames but lower frame rate. | | `scaleFactor` | double | `1.0` | Downscaling factor applied server-side before encoding. Lower values reduce bandwidth and CPU at the cost of resolution. Typical range 0.1–1.0. | | `mimeType` | string | `"image/png"` | Per-frame encoding. One of `image/png` or `image/jpeg`. PNG is lossless and larger, JPEG smaller and lossy. | | `quality` | double | `0.85` | JPEG quality (0.1–1.0). Ignored when `mimeType` is `image/png`. | #### Response ```json { "deviceId": "12f1d7dd07ac42a088c8f961b39d68ff", "sourceKey": "aspec", "exposureTimeMilliseconds": 50, "scaleFactor": 0.5, "mimeType": "image/jpeg", "quality": 0.85, "startedAt": "2025-11-12T08:20:15.4567890+00:00", "status": "active" } ``` ### Consuming the Stream (SSE) ```http GET /v1/devices/{deviceId}/image-streams/{sourceKey}/stream Accept: text/event-stream ``` The response is a Server-Sent-Events stream (RFC 8895). The hub sets `Content-Type: text/event-stream`, `Cache-Control: no-cache`, `Connection: keep-alive`. Each frame arrives as one SSE event: ``` data: {"mimeType":"image/jpeg","image":"","timestamp":"2025-11-12T08:20:15.5123456+00:00"} ``` #### Event Payload | Field | Type | Description | | --- | --- | --- | | `mimeType` | string | MIME type of the encoded frame (matches the stream's current `mimeType`). | | `image` | string | Base64-encoded image bytes. Decode and feed directly to your image viewer. | | `timestamp` | string | ISO-8601 capture time of the frame (with `+00:00` UTC offset). Useful for measuring end-to-end latency. | #### Error Events If the device errors out mid-stream, an SSE event with `event: error` is sent before the connection closes: ``` event: error data: {"errorCode":"","message":""} ``` Treat this as a terminal event — re-subscribe by issuing `GET .../stream` again, or stop and recreate the stream if the underlying error was caused by a configuration mismatch. #### Following the Stream from the Shell ```bash curl -N http://localhost:42042/v1/devices/12f1d7dd07ac42a088c8f961b39d68ff/image-streams/aspec/stream ``` `-N` disables curl's output buffering so each event prints as it arrives. Useful for smoke-testing the stream without writing a client. #### Consuming the Stream from a Browser `EventSource` is the standard Web API for SSE and the easiest way to consume the feed. The hub's bundled demo app (`/app/`) uses exactly this pattern: ```js const url = `http://localhost:42042/v1/devices/${deviceId}/image-streams/${sourceKey}/stream`; const source = new EventSource(url); source.onmessage = (event) => { const { mimeType, image, timestamp } = JSON.parse(event.data); document.getElementById('preview').src = `data:${mimeType};base64,${image}`; }; source.addEventListener('error', () => { source.close(); // terminal — re-subscribe by creating a new EventSource }); ``` The `data:` URL form works directly in `` tags, `.drawImage()` and `createImageBitmap()` without an explicit Base64 decode step. ### Snapshot — Single Frame ```http GET /v1/devices/{deviceId}/image-streams/{sourceKey}/snapshot ``` ```bash curl -o frame.jpg \ http://localhost:42042/v1/devices/12f1d7dd07ac42a088c8f961b39d68ff/image-streams/aspec/snapshot ``` Returns one frame as a raw binary image with the `Content-Type` set to the stream's current `mimeType` (`image/png`, `image/jpeg`, or `image/bmp`). Use this when you need a single frame on demand and do not want the overhead of opening an SSE connection. > [!note] > Snapshots and SSE both read from the same underlying camera. Pulling a snapshot while an SSE feed is active competes for the next frame and is rarely useful. The Elements Hub demo app disables the snapshot button while SSE is running for this reason. ### Reconfiguring a Running Stream ```http POST /v1/devices/{deviceId}/image-streams/{sourceKey}/configure Content-Type: application/json { "exposureTimeMilliseconds": 100, "scaleFactor": 0.75 } ``` The same field set as `start`; only the fields you include are changed. Already-subscribed SSE clients keep their connection — subsequent frames simply use the new configuration. Use this for live UI sliders that adjust exposure or scaling without forcing the user to disconnect and reconnect. ### Listing Active Streams ```http GET /v1/devices/{deviceId}/image-streams ``` Returns an array of `ImageStreamResponse` records (same shape as the `start` response), one per `sourceKey` that currently has an active stream on the device. An empty array means no stream is currently running. ### Stopping a Stream ```http DELETE /v1/devices/{deviceId}/image-streams/{sourceKey} ``` ```bash curl -X DELETE http://localhost:42042/v1/devices/12f1d7dd07ac42a088c8f961b39d68ff/image-streams/aspec ``` Stops the stream and releases the camera-side resources. Any SSE consumers see the connection close immediately after the next pending frame. `DELETE` is idempotent at the API level — calling it a second time returns `404` (`E37`), which most clients can ignore safely on shutdown paths. ### Best Practices - **Start before subscribe**: always issue `POST .../start` before `GET .../stream`. Subscribing without a started stream returns `404` (`E37`). - **Set `scaleFactor` for the consumer**: a 0.5 factor cuts bandwidth and CPU to roughly a quarter without a noticeable difference for live-preview use cases. Full-resolution streaming is rarely the right default. - **Prefer JPEG for live preview**: at quality 0.85 the file size is typically 5–10× smaller than PNG, and the artefacts are invisible at preview resolutions. - **Close `EventSource` on tab hide**: browsers throttle background tabs, which can starve a hot SSE connection. Listen for `visibilitychange` and close/reopen the stream as needed. - **Reconfigure rather than restart**: `POST .../configure` is faster than `stop` + `start` because the camera does not reinitialise. - **Stop on disconnect**: explicit `DELETE` on shutdown frees the camera immediately. `POST /v1/lifecycle/shutdown` will also stop all streams, but explicit cleanup surfaces errors earlier. ### Error Cases | Condition | HTTP | Error Code | | --- | --- | --- | | Connected device not found | 404 | `E5` | | Stream not started (subscribe/snapshot/configure/stop without a prior `start`) | 404 | `E37` | | Unsupported `sourceKey` for the device | 404 | device-class–specific | See [Error Handling](rhopoint-elements-hub-error-handling.md) for the response format. --- # Lifecycle > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. Lifecycle management controls the initialization and shutdown of Elements Hub services. Proper lifecycle management ensures devices are properly configured and resources are cleaned up correctly. ### Endpoints Overview | Method | Endpoint | Description | | --- | --- | --- | | POST | `/v1/lifecycle/initialize` | Initialize services | | POST | `/v1/lifecycle/shutdown` | Shutdown services | ### Initialize Services Initialize the Elements Hub services with configuration settings. This must be called before using device-related functionality. #### Request ```http POST /v1/lifecycle/initialize Content-Type: application/json { "dataDirectory": "C:\\ProgramData\\ElementsHub\\Data", "logDirectory": "C:\\ProgramData\\ElementsHub\\Logs" } ``` ```bash curl -X POST http://localhost:42042/v1/lifecycle/initialize \ -H "Content-Type: application/json" \ -d '{ "dataDirectory": "C:\\ProgramData\\ElementsHub\\Data", "logDirectory": "C:\\ProgramData\\ElementsHub\\Logs" }' ``` #### Request Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `dataDirectory` | string | Yes | Path for data storage | | `logDirectory` | string | Yes | Path for log files | The response body is the string `"Initialization complete."` on success. #### Directory Requirements ##### Data Directory - **Purpose**: Store device configurations, calibration data, measurement results. - **Permissions**: Read/write access required. - **Example**: `C:\ProgramData\ElementsHub\Data` ##### Log Directory - **Purpose**: Store device operation logs. - **Permissions**: Write access required. - **Example**: `C:\ProgramData\ElementsHub\Logs` ### Shutdown Services Gracefully shutdown all Elements Hub services, disconnect devices, and clean up resources. #### Request ```http POST /v1/lifecycle/shutdown ``` ```bash curl -X POST http://localhost:42042/v1/lifecycle/shutdown ``` The request body is empty. The response body is the string `"Service shutdown complete."` on success. #### Shutdown Process 1. **Stop Active Scans**: Cancel any running device scans. 2. **Disconnect Devices**: Safely disconnect all connected devices. 3. **Save State**: Persist any pending data or configurations. 4. **Release Resources**: Free system resources and handles. 5. **Close Logs**: Flush and close log files. ### Best Practices #### Initialization - Always initialize services before device operations. - Verify directory permissions before initialization. - Validate system health after initialization with [`GET /v1/system/checks`](rhopoint-elements-hub-endpoints-functionality-system.md#system-checks). #### Shutdown - Implement graceful shutdown in signal handlers. - Allow time for cleanup operations to complete. - Handle force shutdown scenarios. - Log shutdown events for troubleshooting. - Ensure resources are properly released. #### Directory Management - Use absolute paths for directories. - Ensure sufficient disk space. - Implement directory cleanup policies. - Monitor disk usage over time. - Backup critical data regularly. #### Error Recovery - Implement automatic retry for initialization failures. - Provide clear error messages to users. - Log all lifecycle events for debugging. - Handle partial initialization states. - Implement health checks after operations. --- # Measurement Triggers > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. Measurement triggers are the primary way to obtain data from a connected instrument. A trigger executes a device-class–specific measurement procedure and returns a measurement container — by default a binary RAE file (see [RAE File Format](rhopoint-elements-hub-rae-file-format.md)) — containing all metric values, metadata and any attached device images. ### Endpoints Overview | Method | Endpoint | Description | | --- | --- | --- | | GET | `/v1/devices/{deviceId}/measurement-triggers` | List the measurement triggers available on the device | | POST | `/v1/devices/{deviceId}/measurement-triggers` | Execute a measurement | ### Listing Available Triggers ```http GET /v1/devices/{deviceId}/measurement-triggers ``` ```bash curl http://localhost:42042/v1/devices/12f1d7dd07ac42a088c8f961b39d68ff/measurement-triggers ``` The response describes which triggers the connected device supports and which parameters they accept. Example shape: ```json [ { "identifier": "measure", "title": "Measure", "category": "measure", "parameters": [ { "key": "metricGroupKeys", "type": "EnumArray", "description": "Specify the metric group keys measured." }, { "key": "imageFormat", "type": "String", "description": "Image encoding for images in the RAE container. Allowed values: 'image/png' (default), 'image/jpeg', 'image/x-raw'." } ] } ] ``` Use the listing endpoint to discover the parameter keys that are valid for the specific device class and firmware combination — they may evolve between releases. ### Executing a Measurement ```http POST /v1/devices/{deviceId}/measurement-triggers Content-Type: application/json { "measurementKey": "measure", "containerFormat": "raeBinary", "parameters": [ { "key": "metricGroupKeys", "value": ["sparkle", "scratchLinear"] }, { "key": "imageFormat", "value": "image/png" } ] } ``` ```bash curl -X POST http://localhost:42042/v1/devices/12f1d7dd07ac42a088c8f961b39d68ff/measurement-triggers \ -H "Content-Type: application/json" \ -d '{ "measurementKey": "measure", "containerFormat": "raeBinary", "parameters": [ { "key": "metricGroupKeys", "value": ["sparkle", "scratchLinear"] } ] }' \ --output measurement.rae ``` #### Request Body | Field | Type | Required | Description | | --- | --- | --- | --- | | `measurementKey` | string | Yes | Identifier of the trigger to execute, as returned by `GET /v1/devices/{deviceId}/measurement-triggers`. | | `containerFormat` | string | Yes | Output container format. One of `raeBinary` or `raeJson`. | | `parameters` | array | No | Operation parameters, as `{ "key": "...", "value": ... }` entries. Parameter keys and value types depend on the device. | #### Container Formats | `containerFormat` | Response `Content-Type` | When to use | | --- | --- | --- | | `raeBinary` | `application/vnd.rhopoint.rae+binary` | Default. Compact, CBOR-encoded, GZip-compressed binary file. Recommended for storage and transfer. See [RAE File Format](rhopoint-elements-hub-rae-file-format.md). | | `raeJson` | `application/json` | Human-readable JSON representation of the same data. Easier to inspect from a browser or `curl`, but considerably larger. | > [!note] > The container always contains the full measurement tree: scalar metrics, hierarchical groups, attached images, and metadata. The choice between `raeBinary` and `raeJson` affects encoding only, not content. ### Image Encoding Device images embedded in the container are PNG-encoded by default. The optional `imageFormat` operation parameter on Aesthetix devices selects an alternative encoding: | `imageFormat` value | Encoding in the container | Container MIME type | | --- | --- | --- | | *(omitted)* or `image/png` | PNG | `image/png` | | `image/jpeg` | JPEG, quality 90 | `image/jpeg` | | `image/x-raw` | Original kernel bytes (16-byte header + pixel data) | `image/x-raw` | See [Measurement Image Formats](rhopoint-elements-hub-measurement-image-formats.md) for the binary layout of `image/x-raw`, parsing examples, and per-image notes (some images are cropped server-side before PNG/JPEG encoding; the raw payload is uncropped). ### Aesthetix Metric Groups For Aesthetix-family devices the `metricGroupKeys` parameter selects which metric groups participate in the measurement. Typical values include `sparkle`, `scratchLinear`, `scratchRadial`, `gloss`, `haze`, `cell`, `waviness`, `crossCut`, `shadeImages`. See [Aesthetix — Available Measurements](rhopoint-elements-hub-instruments-aesthetix.md#available-measurements) for the full list. Pass an empty array or omit the parameter to use the device's default selection. ### Decoding the Container Client-Side If you receive a binary RAE container and want to decode it without parsing the format yourself, the hub also exposes: ```http POST /v1/measurements/decode Content-Type: multipart/form-data ``` Upload a `.rae` file as form data and receive the parsed measurement components as JSON. Useful for testing and one-off decoding from environments where embedding a RAE parser is impractical. ### Best Practices - Call the `GET` endpoint once after connecting and cache the trigger metadata for the lifetime of the connection — the parameter list does not change while the device is connected. - Validate `parameters` against the metadata returned by `GET` to avoid runtime parameter errors. - For Aesthetix devices, restrict `metricGroupKeys` to the metrics you actually need. Each group adds processing time on the device side; an unrestricted measurement is several seconds slower than a single-group measurement. - Prefer `raeBinary` for any data that is going to be stored — the size difference compared to `raeJson` is significant for measurements that contain images. ### Error Cases | Condition | HTTP | Error Code | | --- | --- | --- | | Connected device not found | 404 | `E5` | | Unknown `measurementKey` | 404 | `E15` | | Unrecognised `containerFormat` | 400 | `E16` | | Invalid `imageFormat` value (Aesthetix) | 400 | `E42` | | Hardware error during measurement | 502 | device-class–specific (e.g. `E38`, `E39`, `E40`) | See [Error Handling](rhopoint-elements-hub-error-handling.md) for the response format. --- # System > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The system endpoints expose diagnostic information about the running Elements Hub instance and its host machine. They are useful during deployment, support sessions, and for verifying that a client's error-handling path works end-to-end. ### Endpoints Overview | Method | Endpoint | Description | | --- | --- | --- | | GET | `/v1/system/version` | Hub version and environment information | | GET | `/v1/system/checks` | Run the built-in host system checks | | POST | `/v1/system/errors/raise-exception` | Trigger a controlled error response (testing only) | ### Version ```http GET /v1/system/version ``` ```bash curl http://localhost:42042/v1/system/version ``` Returns the version of the running hub and which environment it was built for: ```json { "version": "1.7.0+abc1234", "environment": "release", "componentVersions": {} } ``` | Field | Type | Description | | --- | --- | --- | | `version` | string | Hub version including build metadata. | | `environment` | string | Build category (e.g. `release`, `debug`, `nightly`). | | `componentVersions` | object | Map of internal component → version. May be empty in production builds. | > [!note] > If `version` returns `"###version###"` and `environment` returns `"###category###"`, you are running a build whose version placeholders were not replaced — typically a developer build straight from source. Treat this as "unversioned" rather than a real release. ### System Checks ```http GET /v1/system/checks ``` ```bash curl http://localhost:42042/v1/system/checks ``` Runs a short series of host checks and returns the aggregated result: ```json { "passed": true, "runs": [ { "runIdentifier": 1, "status": "Passed", "details": "USB Controller Version 3" }, { "runIdentifier": 2, "status": "Passed", "details": "Minimum Memory" }, { "runIdentifier": 3, "status": "Passed", "details": "Free Memory" } ] } ``` #### Checks Performed | Check | Requirement | Severity | | --- | --- | --- | | **USB Controller Version 3** | At least one xHCI (USB 3.x) controller is present. | Error | | **Minimum Memory** | The host has at least 8 GB of installed RAM. | Error | | **Free Memory** | At least 2 GB of RAM is currently free. | Warning | `passed` reflects the aggregate: it is `true` only if every error-severity check passed. Warning-severity checks contribute to the per-run `status` but do not flip the top-level `passed` flag. #### When to Run the Checks - After installation, before connecting hardware for the first time. - When a support session starts — the output is small and pastes cleanly into a ticket. - As an automated readiness probe in container/VM deployments. ### Triggering a Controlled Error ```http POST /v1/system/errors/raise-exception?errorType=device ``` ```bash curl -X POST "http://localhost:42042/v1/system/errors/raise-exception?errorType=device" ``` Used to verify that a client correctly parses and reacts to [`ErrorInfo`](rhopoint-elements-hub-error-handling.md) responses. The endpoint deliberately fails with the chosen error category. | `errorType` | Triggers | | --- | --- | | `device` | A device-layer exception (`ErrorInfo` with `errorCode = E31`). | | `notfound` | An HTTP 404 (`ErrorInfo` with `errorCode = E31`). | | `unhandled` | An uncaught exception, exercising the global exception handler. | | anything else | HTTP 400 (`ErrorInfo` with `errorCode = E31`). | Use this in integration tests to ensure your client switches on `errorCode` and surfaces the `message` correctly. See [Error Handling](rhopoint-elements-hub-error-handling.md) for the full response format. > [!warning] > This endpoint exists exclusively for testing. Do not call it from production code paths. ### Best Practices - Cache the `version` response on the client; it does not change at runtime. - Run `/checks` once at start-up and surface failures to the operator before allowing measurements. Use it in CI to verify that test hosts meet the minimum requirements. - Pin client behaviour to `errorCode` values returned by the test endpoint — if you can handle `E31` correctly, the rest of the error surface tends to fall in line. --- # Instruments > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. Elements Hub supports the following instrument families. Each page covers the connect-parameters, available measurements, calibrations, and any device-specific quirks. - [Aesthetix](rhopoint-elements-hub-instruments-aesthetix.md) — dual-camera appearance sensor (gloss, haze, sparkle, scratch, texture, waviness, …) - [Calibration](rhopoint-elements-hub-instruments-aesthetix-calibration.md) — white, black and height calibration of the Aesthetix - [ID Inline](rhopoint-elements-hub-instruments-id-inline.md) — inline inspection device (pass/fail metrics, linearisation, MTF) - [Aesthetix Inline](rhopoint-elements-hub-instruments-aesthetix-inline.md) — network-attached Aesthetix variant For development and CI without physical hardware, every class above has a `-mock` counterpart — see [Mock Devices](rhopoint-elements-hub-mock-devices.md). --- # ID Inline > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. This guide provides detailed information about working with ID Inline instruments using the Elements Hub API. If you do not want to use the REST API directly, consider generating the SDK library. See [Client Code Generation](rhopoint-elements-hub-client-code-generation.md) ### Device Workflow The typical workflow for using an ID Inline device follows these steps: 1. [Initialize lifecycle services](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) 2. [Start a scan](rhopoint-elements-hub-endpoints-functionality-device-scans.md) with `deviceClass: id-inline` (or `id-inline-mock` for testing — see [Mock Devices](rhopoint-elements-hub-mock-devices.md)) 3. [Monitor available devices](rhopoint-elements-hub-endpoints-functionality-device-scans.md#polling-discovered-devices) 4. [Connect to the device](rhopoint-elements-hub-endpoints-functionality-connected-devices.md) using the discovered identifier 5. [Stop the scan](rhopoint-elements-hub-endpoints-functionality-device-scans.md#stopping-a-scan) 6. Calibrate / tare (measurementKey: `tare`) 7. [Measure](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md) (measurementKey: `measure`) 8. [Disconnect](rhopoint-elements-hub-endpoints-functionality-connected-devices.md#disconnecting-a-device) 9. [Shut down lifecycle services](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) ### Complete Workflow Example #### Step 1: Lifecycle Initialize Before any device operations, initialize the Elements Hub services: ```http POST /v1/lifecycle/initialize Content-Type: application/json { "dataDirectory": "C:\\ProgramData\\ElementsHub\\Data", "logDirectory": "C:\\ProgramData\\ElementsHub\\Logs" } ``` #### Step 2: Scan for ID Inline Devices Start scanning for available ID Inline devices: ```http POST /v1/device-scans Content-Type: application/json { "deviceClass": "id-inline" } ``` **Response:** ```json { "identifier": "abc123def", "scanFilter": { "deviceClass": "id-inline", "serialNumberFilter": null, "ipAddressFilter": null }, "status": "scanning", "foundDevices": [] } ``` #### Step 3: Get Available Devices Retrieve the list of currently discovered devices: ```http GET /v1/available-devices Accept: application/json ``` The endpoint returns every device discovered by any active scan; there is no filter parameter. Filter client-side on `deviceClass` if you need to. **Response:** ```json [ { "discovered": "2042-11-07T12:22:14.9762102+00:00", "identifier": "abc123def", "deviceClass": "id-inline", "serialNumber": "12345", "serialNumbers": [ "12345" ], "hardwareVersion": "…", "firmwareVersion": "…", "softwareVersion": "…", "componentVersions": [] } ] ``` #### Step 4: Connect to Device Connect to a specific ID Inline device using its identifier: ```http POST /v1/devices Content-Type: application/json { "deviceIdentifier": "abc123def", "parameters": [ { "key": "flipX", "value": false }, { "key": "flipY", "value": false } ] } ``` **Connection Parameters:** | Parameter | Type | Description | Default | | --- | --- | --- | --- | | `flipX` | Boolean | Flip image horizontally | `false` | | `flipY` | Boolean | Flip image vertically | `false` | **Response:** ```json { "connectTime": "2042-11-07T12:25:45Z", "identifier": "abc123def", "deviceClass": "id-inline", "serialNumber": "12345", "serialNumbers": ["12345"], "hardwareVersion": "…", "firmwareVersion": "…", "softwareVersion": "…", "componentVersions": [] } ``` #### Step 5: Stop Scan Once connected, stop the device scan to free up resources. Use the **scan** identifier (returned by `POST /v1/device-scans` in Step 2), not the device identifier: ```http DELETE /v1/device-scans/ ``` The `foundDevices` array in the scan response contains the **identifiers** of every device discovered during the scan, not their serial numbers. #### Step 6: Calibrate Device (Tare) Before taking measurements, calibrate the device using the "tare" measurement trigger. This establishes a baseline reference for subsequent measurements: The tare functionality will be moved to the calibrations endpoint in a future release. ```http POST /v1/devices/idInline-ABC12345/measurement-triggers Content-Type: application/json { "measurementKey": "tare", "containerFormat": "raeJson" } ``` #### Step 7: Take Measurements Execute measurement operations using the "measure" trigger: ```http POST /v1/devices/abc123def/measurement-triggers Content-Type: application/json { "measurementKey": "measure", "containerFormat": "raeBinary", "parameters": [ { "key": "align", "value": true }, { "key": "expose", "value": true }, { "key": "includeAttachments", "value": true } ] } ``` The two container formats `raeBinary` and `raeJson` are available. **Measurement Parameters:** | Parameter | Type | Description | Default | | --- | --- | --- | --- | | `align` | Boolean | Perform automatic alignment before measurement | `true` | | `expose` | Boolean | Perform automatic exposure adjustment before measurement | `true` | | `includeAttachments` | Boolean | Include image data in the measurement result | `true` | #### Step 8: Disconnect from Device When finished with measurements, disconnect from the device: ```http DELETE /v1/devices/abc123def ``` #### Step 9: Lifecycle Shutdown When shutting down your application, properly shutdown the internal Elements Hub services: ```http POST /v1/lifecycle/shutdown ``` ### Image Acquisition Retrieve images from the ID Inline device: ```http GET /v1/devices/abc123def/images/camera Accept: image/png ``` --- # Aesthetix > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. Rhopoint Aesthetix is a modular dual-camera–based sensor that captures detailed images of a surface under controlled lighting and derives a wide range of appearance metrics from them: gloss, haze, sparkle, scratches, texture, waviness, defects and more. It connects to the host PC over USB and is exposed to Elements Hub through the device class `aesthetix`. > [!note] > For the network-attached inline variant see [Aesthetix Inline](rhopoint-elements-hub-instruments-aesthetix-inline.md). Both variants share most of this guide, but the inline variant supports a reduced metric set and a single calibration type — the per-feature notes below call this out where it matters. ### Workflow Summary 1. [Initialize lifecycle services](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) 2. [Start a scan](rhopoint-elements-hub-endpoints-functionality-device-scans.md) with `deviceClass: aesthetix` 3. [Connect to the discovered device](rhopoint-elements-hub-endpoints-functionality-connected-devices.md) 4. [Calibrate](rhopoint-elements-hub-instruments-aesthetix-calibration.md) (white → black → height) before the first measurement of the session 5. [Trigger measurements](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md) with the metric groups your workflow needs 6. [Disconnect](rhopoint-elements-hub-endpoints-functionality-connected-devices.md) and [shut down](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) ### Connect Parameters Aesthetix devices accept no operation parameters on `POST /v1/devices`. Only the `deviceIdentifier` is required. ### Calibration The Aesthetix exposes three calibration types via the [calibration endpoints](rhopoint-elements-hub-endpoints-functionality-calibrations.md): | `calibrationKey` | Purpose | Parameters | | --- | --- | --- | | `white` | Reflectance + visual contrast reference, using the white standard | `reflectance` (0–1), `visualContrast` (0–1) | | `black` | Black specular **and** aspecular reference, using the black standard | `gloss` (GU) | | `height` | Height reference, using the texture standard | `height` (µm) | The Aesthetix Inline variant only exposes `black`. See [Calibration](rhopoint-elements-hub-instruments-aesthetix-calibration.md) for the recommended order, the physical setup for each target, and troubleshooting tips. ### Available Measurements The Aesthetix exposes one measurement trigger, `measure`, executed via [`POST /v1/devices/{deviceId}/measurement-triggers`](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md). Which metrics it actually computes is selected through the `metricGroupKeys` parameter — pass an array of the keys below: | Metric group key | What it produces | Notes | | --- | --- | --- | | `gloss` | Gloss value, visual gloss, gloss ROI, 3D gloss plot | Requires valid black calibration | | `fastGloss` | Faster, lower-fidelity gloss value | | | `haze` | Haze C, contrast haze, log-haze, MC-DOI, visual haze indoors/outside, haze ROIs | | | `visualContrast` | Visual contrast value | Requires valid white calibration | | `visualGloss` | Visual gloss value | Requires valid white/black calibration | | `visualHaze` | Visual haze value | | | `contrastHaze` | Contrast haze value | | | `sparkle` | Sparkle density, graininess, RGB, visibility / area / brightness / hue arrays, sparkle albedo image, 7 LED sparkle images, 7 LED sparkle overlays | | | `scratchLinear` | Scratch length, visibility, count, area, area-mean and length-mean for linear scratches; scratch image; horizontal and vertical overlays | | | `scratchRadial` | Same metrics as `scratchLinear` but for radial scratches | | | `cell` | Cell number, amplitude, size, std-dev, max/min, hill size, fill factor, texture R/Rc/Rv/Rh, roughness, watershed metrics, cell overlay, 3D texture plot | | | `roughness` | Surface roughness | | | `grit` | Grit metric | | | `waviness` | Waviness, PCI waviness, tension, waviness image | | | `pciWaviness` | PCI waviness value (subset of `waviness`) | | | `tension` | Tension value (subset of `waviness`) | | | `bloom` | Bloom metric | | | `crossCut` | Cross-cut class (ASTM, ISO), percent, image, full overlay, found overlay | Requires the cross-cut module | | `sharpness` | Sharpness metric | | | `spot` | Spot image | | | `shadeImages` | Six LED shade images | No metric values — image stack only | | `surface` | Surface RGB mean, surface image (cropped) | | The `metricGroupKeys` parameter accepts a list of these keys; pass only the groups you actually need. Each additional group adds processing time on the device side. ### Measurement Parameters In addition to `metricGroupKeys`, the `measure` trigger accepts: | Parameter | Type | Description | | --- | --- | --- | | `metricGroupKeys` | EnumArray | Selects which metric groups participate in the measurement. See table above. | | `includeAttachments` | Boolean | Whether to include attachments such as images in the result. Defaults to `true`. | | `imageFormat` | String | Image encoding inside the container: `image/png` (default), `image/jpeg`, or `image/x-raw`. See [Measurement Image Formats](rhopoint-elements-hub-measurement-image-formats.md). | ### Available Commands Beyond measurements and calibrations, the Aesthetix exposes one command via [the commands endpoint](rhopoint-elements-hub-endpoints-functionality-connected-devices.md): | Command identifier | Purpose | Parameters | | --- | --- | --- | | `updateCalibrationValues` | Update the stored calibration target values without performing a full calibration. Useful when only the target values for a known good calibration have changed. | `glossCalibrationTarget`, `visualContrastCalibrationTarget`, … (see `GET /commands`) | ### Image Output Device images embedded in a measurement container — surface, sparkle, scratch, cross-cut, cell, waviness, spot, shade — are PNG-encoded by default. Use the `imageFormat` parameter to request JPEG (smaller) or raw bytes (uncropped originals straight from the device kernel). See [Measurement Image Formats](rhopoint-elements-hub-measurement-image-formats.md) for the binary layout of `image/x-raw` and parsing examples in C++. ### Mock Variant For development and CI without physical hardware, the hub offers the device class `aesthetix-mock`. It exposes the same endpoints and parameter shapes but does not return real images or metric values. See [Mock Devices](rhopoint-elements-hub-mock-devices.md). --- # Calibration > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The Aesthetix is calibrated via the generic [calibration endpoints](rhopoint-elements-hub-endpoints-functionality-calibrations.md) — `GET/POST /v1/devices/{deviceId}/calibrations` and `GET /calibrations/status`. This page covers the Aesthetix-specific calibration types, what physical target each one needs, and the recommended order. ### Available Calibration Types | `calibrationKey` | Title | Physical target | Parameters | | --- | --- | --- | --- | | `white` | White Calibration | White calibration standard | `reflectance` (0–1), `visualContrast` (0–1) | | `black` | Black Calibration | Black calibration standard | `gloss` (GU) | | `height` | Height Calibration | Texture standard | `height` (µm) | Every calibration is valid for **two days** (recommended interval) and goes overdue after **seven days**. The hub tracks the timestamps per device serial number — see [`GET /calibrations/status`](rhopoint-elements-hub-endpoints-functionality-calibrations.md#getting-the-calibration-status). > [!note] > The Aesthetix Inline variant only exposes `black`. The `white` and `height` calibrations are not available there. ### Recommended Order Run the calibrations in this sequence the first time a device is calibrated, after a long idle period, or whenever the status endpoint reports `NotCalibrated` / `CalibrationOverdue`: 1. **Black** — establishes the optical zero for the specular and aspecular paths. 2. **White** — references the reflectance and visual-contrast scales against the white standard. 3. **Height** — references the height scale against the texture standard. White and height depend on a valid black calibration; running them first against an outdated black reference yields wrong scaling. The hub does not enforce this order, so the integrator's UI or workflow must. ### White Calibration Used to reference reflectance and visual-contrast measurements against the white calibration standard supplied with the instrument. ```http POST /v1/devices/{deviceId}/calibrations Content-Type: application/json { "calibrationKey": "white", "parameters": [ { "key": "reflectance", "value": 0.95 }, { "key": "visualContrast", "value": 0.92 } ] } ``` | Parameter | Unit | Notes | | --- | --- | --- | | `reflectance` | Fraction (`0.95` ≙ 95 %) | The reflectance value printed on the white standard. | | `visualContrast` | Fraction (`0.92` ≙ 92 %) | The visual-contrast value printed on the white standard. | **Before sending the request:** place the white standard over the measurement window with the white surface facing the optics. Hold the instrument steady — the device captures multiple frames during calibration. ### Black Calibration Establishes the optical zero. The Aesthetix performs both **specular** and **aspecular** black calibration in a single request; the Aesthetix Inline only performs specular. ```http POST /v1/devices/{deviceId}/calibrations Content-Type: application/json { "calibrationKey": "black", "parameters": [ { "key": "gloss", "value": 0.5 } ] } ``` | Parameter | Unit | Notes | | --- | --- | --- | | `gloss` | GU (Gloss Units) | The gloss value printed on the black standard. | **Before sending the request:** place the black standard over the measurement window with the black surface facing the optics. ### Height Calibration References the height (texture) scale against the texture standard. ```http POST /v1/devices/{deviceId}/calibrations Content-Type: application/json { "calibrationKey": "height", "parameters": [ { "key": "height", "value": 50.0 } ] } ``` | Parameter | Unit | Notes | | --- | --- | --- | | `height` | µm | The reference height of the texture standard, in micrometres. | **Before sending the request:** place the texture standard over the measurement window. Ensure it sits flat and centered. ### Verifying the Result After the call returns successfully, query the status to confirm the new timestamp: ```http GET /v1/devices/{deviceId}/calibrations/status ``` A freshly executed calibration moves to `CalibrationValid` and its `lastCalibrationTime` matches the request time (within network skew). If the status remains `NotCalibrated` after a successful response, the calibration parameter values were likely ignored — re-check the `key` names against `GET /v1/devices/{deviceId}/calibrations`. ### Troubleshooting | Symptom | Likely cause | Fix | | --- | --- | --- | | `E29 Calibration timeout` | The instrument disconnected during the calibration sequence. | Reconnect the device (re-scan + connect) and retry. | | Calibration succeeds but downstream measurements are clearly wrong | Wrong physical target was on the window, or the target values printed on the standard were entered with the wrong scale (e.g. `95` instead of `0.95`). | Re-calibrate with the correct target and verify `reflectance`/`visualContrast` are passed as fractions, not percentages. | | Status stays `NotCalibrated` after a 200 OK response | Parameter `key` was misspelled and ignored. | Read the accepted keys from `GET /v1/devices/{deviceId}/calibrations` and retry. | | Calibration takes longer than the HTTP client timeout | Default client timeouts (often 5–10 s) are too short for calibration. | Raise the per-request timeout to at least 30 s. | ### Error Cases | Condition | HTTP | Error Code | | --- | --- | --- | | Connected device not found | 404 | `E5` | | Unknown `calibrationKey` | 404 | `E28` | | Calibration timed out (likely hardware disconnect) | 502 | `E29` | See [Error Handling](rhopoint-elements-hub-error-handling.md) for the full response format. --- # Aesthetix Inline > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. This guide provides detailed information about working with Aesthetix Inline instruments using the Elements Hub API. ### Device Workflow The typical workflow for using an Aesthetix Inline device follows these steps: 1. [Initialize lifecycle services](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) 2. [Start a scan](rhopoint-elements-hub-endpoints-functionality-device-scans.md) with `deviceClass: aesthetix-inline` 3. [Poll the list of available devices](rhopoint-elements-hub-endpoints-functionality-device-scans.md#polling-discovered-devices) until the target appears 4. [Connect to the instrument](rhopoint-elements-hub-endpoints-functionality-connected-devices.md) using the discovered identifier 5. *(Optional)* Stop the scan — for Aesthetix Inline it is acceptable to keep the scan running while working with the device 6. Calibrate (see [Calibration](rhopoint-elements-hub-instruments-aesthetix-calibration.md)) 7. [Trigger measurements](rhopoint-elements-hub-endpoints-functionality-measurement-triggers.md) 8. [Disconnect the device](rhopoint-elements-hub-endpoints-functionality-connected-devices.md) 9. [Shut down lifecycle services](rhopoint-elements-hub-endpoints-functionality-lifecycle.md) --- # RAA File Format > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The RAA file format is the archive companion of the [RAE File Format](rhopoint-elements-hub-rae-file-format.md): a standard **ZIP archive** that bundles multiple RAE files. It is used to export and transfer whole sets of measurements in a single file. ### Structure - An RAA file is a regular ZIP archive (PKZIP format) with the file extension `.raa`. Any standard ZIP library or archive tool can open it. - Each entry in the archive is a complete binary RAE file containing **one measurement**, named after the measurement's source identifier: `.rae`. - There is no manifest or index file — the archive entries themselves are the content. ``` example.raa (ZIP) ├── 0d9f4c1e-6a2b-4f43-9c1a-2f5e8d7b3a10.rae ├── 3b7e02aa-91c4-4d0f-8f6d-64c2a92f4b77.rae └── b1a4f6d2-3c8e-45b9-a0d7-9e5c31f8e2c4.rae ``` ### Parsing 1. **Open** the file with a ZIP library. 2. **Iterate** over the entries and read each one into memory. 3. **Parse** each entry as a binary RAE file (see [Decompression and parsing](rhopoint-elements-hub-rae-file-format-decompression-and-parsing.md)). > [!note] > The RAE-level `Compression` setting varies per entry: some writers store the entries uncompressed at the RAE level and let the ZIP compression do the work, others gzip the RAE payload as well. Always evaluate the `Compression` property of each entry's [File Header](rhopoint-elements-hub-rae-file-format-file-header.md) instead of assuming one or the other. ### Where RAA files are used - **Rhopoint Appearance Elements (AE)** exports and imports measurement collections as `.raa` files. - **PDF reports** generated by AE can carry the underlying measurement data as an embedded `.raa` attachment, so the original measurements can be re-imported from the report itself. ### MIME type There is no dedicated MIME type for RAA files; since the container is a plain ZIP archive, use `application/zip` when transferring RAA files over HTTP. --- # RAE File Format > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The RAE file format is an open and flexible format to transport any kind of measurement. It consists of three main parts: - The "magic string" at the beginning of the file for file identification. - The container properties (the file header) - The actual data. The data containers are RFC 8949 Concise Binary Object Representation (CBOR) encoded ([cbor.io](https://cbor.io/), [Wikipedia CBOR](https://en.wikipedia.org/wiki/CBOR)). \ CBOR is a compact, efficient binary serialization format, designed to be easily parsed and interoperable. It is ideal for transmitting structured data. \ There are a lot of free implementations for reading/writing CBOR for every widely used programming language like C#, C++, Java, or TypeScript: [https://cbor.io/impls.html](https://cbor.io/impls.html) ### File structure - [File Identification](rhopoint-elements-hub-rae-file-format-file-identification.md) — the magic string at the start of every RAE file - [MIME Type](rhopoint-elements-hub-rae-file-format-mime-type.md) — `application/vnd.rhopoint.rae+binary` - [File Header](rhopoint-elements-hub-rae-file-format-file-header.md) — CBOR-encoded `RaeBinaryFile` record - [Decompression and parsing](rhopoint-elements-hub-rae-file-format-decompression-and-parsing.md) — gzip and CBOR decoding steps ### Payload structure - [MeasurementComponentMetaTuple](rhopoint-elements-hub-rae-file-format-measurementcomponentmetatuple.md) — the payload: an array of meta/component tuples - [MeasurementComponent](rhopoint-elements-hub-rae-file-format-measurementcomponent.md) — the measurement tree: components, metadata, source - [Measurement Data Types](rhopoint-elements-hub-rae-file-format-measurement-data-types.md) — polymorphic data records: single values and files ### Related formats - [JSON Variant](rhopoint-elements-hub-rae-file-format-json-variant.md) — the same content as a plain JSON document (`application/vnd.rhopoint.rae+json`) - [RAA File Format](rhopoint-elements-hub-raa-file-format.md) — an archive (ZIP) bundling multiple RAE files --- # File Identification > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The RAE file format begins with a specific sequence of bytes to uniquely identify the file type and its version. This sequence is known as the magic string. The purpose of the magic string is to allow software tools to quickly recognize and validate the file format before attempting to process it. The magic string structure is as follows:\ `RAECBOR<0xFF>` ### Breakdown of the Magic String 1. **`RAE`**:\ A fixed three-character prefix that identifies the file as an RAE format file. 2. **Version Byte** (``):\ A single byte that specifies the version of the RAE format. This allows for future backward-compatible updates to the format. - The current version is `0x01`, indicating the initial version of the RAE format. 3. **`CBOR`**:\ A variable length string indicating that the file's data container uses the CBOR (Concise Binary Object Representation) encoding format. 4. **Terminator Byte** (`<0xFF>`):\ A single byte with the value `0xFF`, marking the end of the magic string and the start of the file header. This ensures unambiguous parsing and serves as a delimiter for file processing tools. ### Example An example of the magic string in a hexadecimal representation for the current version (`0x01`) would look like this:\ `52 41 45 01 43 42 4F 52 FF` - `52 41 45`: ASCII for "RAE". - `01`: Version byte (current version). - `43 42 4F 52`: ASCII for "CBOR". - `FF`: Terminator byte. This sequence is the first part of the file and ensures that any system or tool attempting to parse the file can quickly verify its format, version, and encoding type. --- # File Header > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The file header, encoded using the CBOR format, contains critical metadata about the RAE file and its content. The `RaeBinaryFile` is the root record for the RAE file format in its binary representation. It starts directly after the terminator byte of the magic string (see [File Identification](rhopoint-elements-hub-rae-file-format-file-identification.md)) and extends to the end of the file. ### Properties - **Format** (required):\ Identifies the data format contained within the file. For now, only `"MeasurementComponentMetaTuple[]"` is supported — an array of [MeasurementComponentMetaTuple](rhopoint-elements-hub-rae-file-format-measurementcomponentmetatuple.md) records. - **Version** (required):\ Indicates the version of the file format. The current version is `1`. - **Compression** (optional):\ Specifies the compression algorithm used for the `Data` byte array. If not set, it defaults to `"none"`. Currently, only `"gzip"` is supported. - **EncryptionAlgorithm** (optional):\ Specifies the encryption algorithm applied to the data (if any). Encryption is not implemented in the current version; files are written with `"none"`. - **HashAlgorithm** (optional):\ Describes the hashing algorithm used to validate data integrity. Currently `"sha256"`. - **Hash** (optional):\ A hash string used for data integrity checks. It is calculated over the `Data` byte array **as stored in the file** (i.e. after compression), using the algorithm specified in `HashAlgorithm`. - **DataContainer** (required):\ Indicates the encoding of the payload inside `Data` after decompression. Currently, this is always `"cbor"`. - **DataSize** (required):\ The size of the `Data` byte array in bytes. - **Data** (required):\ The actual measurement data stored as a byte array. The data is compressed using the specified compression algorithm (or left uncompressed if `Compression = "none"`) and contains a CBOR-encoded container matching the `Format`. > [!note] > In the CBOR encoding, the map keys are the property names in **lowerCamelCase**: `format`, `version`, `compression`, `encryptionAlgorithm`, `hashAlgorithm`, `hash`, `dataContainer`, `dataSize`, `data`. The `Data` byte array is stored as a CBOR byte string. ### Declaration ```csharp public record RaeBinaryFile { public required string Format { get; set; } public required int Version { get; set; } public string? Compression { get; set; } public string? EncryptionAlgorithm { get; set; } public string? HashAlgorithm { get; set; } public string? Hash { get; set; } public required string DataContainer { get; set; } public required int DataSize { get; set; } public required byte[] Data { get; set; } } ``` --- # Decompression and parsing > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The `Data` byte array of the [File Header](rhopoint-elements-hub-rae-file-format-file-header.md) may be compressed using the `gzip` algorithm. Once decompressed, it is a CBOR container holding a collection of [MeasurementComponentMetaTuple](rhopoint-elements-hub-rae-file-format-measurementcomponentmetatuple.md) objects. ### Steps to parse an RAE file 1. **Validate** the magic string and version byte at the start of the file (see [File Identification](rhopoint-elements-hub-rae-file-format-file-identification.md)) and skip past the `0xFF` terminator. 2. **Deserialize** the remainder of the file with a CBOR parser to obtain the `RaeBinaryFile` header record (see [File Header](rhopoint-elements-hub-rae-file-format-file-header.md)). 3. **Verify** the integrity of the `Data` byte array (optional): compute the hash specified by `HashAlgorithm` over the `Data` bytes as stored and compare it against `Hash`. 4. **Decompress** the `Data` byte array (if `Compression` is not `"none"`). 5. **Deserialize** the decompressed data using a CBOR parser. 6. **Interpret** the result based on the `Format` value. For now, this is always an array of [MeasurementComponentMetaTuple](rhopoint-elements-hub-rae-file-format-measurementcomponentmetatuple.md) maps. --- # MIME Type > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The MIME type for the RAE file format is:\ `application/vnd.rhopoint.rae+binary` A MIME (Multipurpose Internet Mail Extensions) type is a standardized way to describe the nature and format of a file's content. The assigned MIME type for the RAE file format indicates that it is a proprietary binary file created and managed by Rhopoint Instruments. Below is a breakdown of the MIME type components: 1. **`application`**:\ This denotes that the RAE file is an application-specific binary file rather than text or multimedia content. 2. **`vnd.rhopoint`**:\ The `vnd.` prefix specifies that this is a vendor-specific MIME type, followed by `rhopoint`, which identifies Rhopoint Instruments as the creator and maintainer of the format. 3. **`rae`**:\ This refers to the specific file format name, "RAE," associated with Rhopoint's measurement system. 4. **`+binary`**:\ The `+binary` suffix indicates that the file content is encoded in a binary format, as opposed to text-based formats like JSON or XML. ### Usage The MIME type `application/vnd.rhopoint.rae+binary` is critical for ensuring proper handling and identification of RAE files in various systems. It is used in the following scenarios: - **File Transfer**: To specify the file type during HTTP communication (e.g., as the `Content-Type` or `Accept` header in REST APIs). - **File Storage**: To associate the correct file type metadata with stored RAE files. - **File Parsing**: To ensure applications recognize and process RAE files with the appropriate decoders and parsers. By adhering to this MIME type, systems and software can reliably identify RAE files and process them correctly in accordance with Rhopoint's specifications. --- # MeasurementComponentMetaTuple > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. After decompression (see [Decompression and parsing](rhopoint-elements-hub-rae-file-format-decompression-and-parsing.md)), the payload of an RAE file is a **CBOR array**. Each element of the array is one `MeasurementComponentMetaTuple` — a pair of the searchable metadata (`Meta`) and the actual measurement tree (`Component`) of a single measurement. ```csharp public record MeasurementComponentMetaTuple { public required MeasurementMeta Meta { get; init; } public required MeasurementComponent Component { get; init; } } ``` In the CBOR encoding, each tuple is a map with two keys: | Key | Type | Description | |---|---|---| | `meta` | map | The `MeasurementMeta` record (see below) | | `component` | map | The root [MeasurementComponent](rhopoint-elements-hub-rae-file-format-measurementcomponent.md) of the measurement tree | ### Encoding rules These rules apply to the whole payload (tuples, components, and data records): - **Map keys** are the property names in **lowerCamelCase** (e.g. `MeasurementIdentifier` → `measurementIdentifier`). - **Unset properties** are written as CBOR `null`; parsers must treat `null` and a missing key the same way. - **Enum values** are written as lowerCamelCase strings (e.g. `Single` → `"single"`, `FileReference` → `"fileReference"`). - **Timestamps** are ISO 8601 round-trip strings in UTC (e.g. `"2026-07-07T12:34:56.7890123+00:00"`). - **Byte arrays** are CBOR byte strings. > [!note] > The [JSON Variant](rhopoint-elements-hub-rae-file-format-json-variant.md) of the RAE format uses different, abbreviated key names. The tables below list both. ### MeasurementMeta `MeasurementMeta` holds the descriptive, searchable metadata of a measurement. It is stored separately from the measurement tree so applications can list and filter measurements without loading the (potentially large) measurement data. | Property | CBOR key | JSON key | Type | Description | |---|---|---|---|---| | Identifier | `identifier` | `id` | string | Database identifier of the meta record. May be `null`; importers typically discard it and assign a new one. | | Version | `version` | `ver` | integer | Schema version of the record. Currently `2`. | | SourceIdentifier | `sourceIdentifier` | `srcId` | string | Identifier assigned by the system that produced the measurement (a GUID). Stable across export/import. | | MeasurementIdentifier | `measurementIdentifier` | `mId` | string | Identifier of the measurement component this meta record belongs to. | | ModuleIdentifier | `moduleIdentifier` | `modId` | string | Identifier of the software module that produced the measurement. | | MeasurementType | `measurementType` | `mType` | string (enum) | Type of the root component, see [MeasurementComponent](rhopoint-elements-hub-rae-file-format-measurementcomponent.md) for the list of values. | | Timestamp | `timestamp` | `time` | string (ISO 8601) | Time the measurement was stored. | | SourceTimestamp | `sourceTimestamp` | `srcTime` | string (ISO 8601) | Time the measurement was taken on the source device. | | Index | `index` | `index` | integer | Sequential index of the measurement. | | Name | `name` | `name` | string | Display name of the measurement. | | Project | `project` | `proj` | string | Project the measurement is assigned to. | | Batch | `batch` | `bat` | string | Batch the measurement is assigned to. | | Customer | `customer` | `cust` | string | Customer the measurement is assigned to. | | Comments | `comments` | `com` | string | Free-text comments. | | Tags | `tags` | `tag` | array of strings | User-defined tags. | | SearchableText | `searchableText` | `search` | string | Pre-built text used for full-text search. | ### Example (CBOR diagnostic notation) ``` [ { "meta": { "identifier": null, "version": 2, "sourceIdentifier": "0d9f4c1e-6a2b-4f43-9c1a-2f5e8d7b3a10", "measurementIdentifier": "0d9f4c1e-6a2b-4f43-9c1a-2f5e8d7b3a10", "moduleIdentifier": "surface-brilliance", "measurementType": "composite", "timestamp": "2026-07-07T12:34:56.7890123+00:00", "sourceTimestamp": "2026-07-07T12:34:55.1230000+00:00", "index": 42, "name": "Sample A", "project": "Door panels", "batch": "Batch 7", "customer": null, "comments": null, "tags": ["oem", "topcoat"], "searchableText": "Sample A Door panels Batch 7" }, "component": { ... } } ] ``` The structure of the `component` map is described in [MeasurementComponent](rhopoint-elements-hub-rae-file-format-measurementcomponent.md). --- # MeasurementComponent > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. A `MeasurementComponent` is a node in the measurement tree. A measurement is usually a **composite** root component that contains one child component per metric or image; child components can themselves contain further components, so arbitrary hierarchies are possible. ``` Measurement (composite) ├── Gloss 60° (single, data = 87.3) ├── DOI (single, data = 92.1) └── ledShadeImages (array) ├── ledShadeImages_0 (file, data = binary image) ├── ledShadeImages_1 (file, data = binary image) └── ... ``` ### Properties | Property | CBOR key | JSON key | Type | Description | |---|---|---|---|---| | Identifier | `identifier` | `id` | string | Database identifier. May be `null`; importers typically discard it and assign a new one. | | Version | `version` | `ver` | integer | Schema version of the record. Currently `2`. | | SourceIdentifier | `sourceIdentifier` | `srcId` | string | Identifier assigned by the system that produced the measurement. | | Type | `type` | `type` | string (enum) | **Required.** The component type, see below. | | Name | `name` | `name` | string | Name of the component (e.g. the metric name). | | Timestamp | `timestamp` | `time` | string (ISO 8601) | Time the component was stored. | | SourceTimestamp | `sourceTimestamp` | `srcTime` | string (ISO 8601) | Time the component was recorded on the source device. | | Data | `data` | `data` | map | The measurement value or file, see [Measurement Data Types](rhopoint-elements-hub-rae-file-format-measurement-data-types.md). `null` for pure grouping nodes. | | Metadata | `metadata` | `meta` | map | Additional metadata about the value, see below. | | Source | `source` | `src` | map | Information about where the measurement was taken, see below. | | Components | `components` | `comp` | array of maps | Child components (nested `MeasurementComponent` records). | | SourceSignature | `sourceSignature` | `srcSig` | string | Signature created by the source device (data authenticity). | | Signature | `signature` | `sig` | string | Signature of the record. | ### Component types The `type` value is one of the `MeasurementTypes` enum members (written in lowerCamelCase): | Value | Meaning | |---|---| | `single` | A single value; `data` holds the value. | | `composite` | A grouping node; the children live in `components`. | | `continuous` | A continuously recorded series of values. | | `graph` | Graph/curve data. | | `file` | A file embedded in the component; `data` is a `FileBinaryData` record. | | `fileReference` | A reference to a file stored elsewhere; `data` is a `FileReferenceData` record. | | `array` | An ordered collection of components of the same kind (e.g. an image stack). | ### Metadata The optional `metadata` map describes the value in `data`: | Property | CBOR key | JSON key | Type | Description | |---|---|---|---|---| | Unit | `unit` | `unit` | string | Unit of the value (e.g. `"GU"`). | | Accuracy | `accuracy` | `accuracy` | string | Accuracy of the value. | | Environment | `environment` | `env` | map | A nested `MeasurementComponent` describing environmental conditions (e.g. temperature) at the time of measurement. | ### Source The optional `source` map describes where the measurement was taken: | Property | CBOR key | JSON key | Type | Description | |---|---|---|---|---| | DeviceIdentifier | `deviceIdentifier` | `deviceId` | string | Identifier (e.g. serial number) of the measuring device. | | LocationName | `locationName` | `loc` | string | Human-readable location name. | | Latitude | `latitude` | `lat` | string | Geographic latitude. | | Longitude | `longitude` | `lon` | string | Geographic longitude. | ### Declaration ```csharp public record MeasurementComponent : Identifiable { public required MeasurementTypes Type { get; set; } public string? Name { get; set; } public DateTimeOffset? Timestamp { get; set; } public DateTimeOffset? SourceTimestamp { get; set; } public IMeasurementData? Data { get; set; } public IMetadata? Metadata { get; set; } public IMeasurementSource? Source { get; set; } public List? Components { get; set; } public string? SourceSignature { get; set; } public string? Signature { get; set; } } public enum MeasurementTypes { Single, Composite, Continuous, Graph, File, FileReference, Array } ``` --- # Measurement Data Types > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. The `data` map of a [MeasurementComponent](rhopoint-elements-hub-rae-file-format-measurementcomponent.md) is **polymorphic**: depending on the component type it holds a single value, an embedded file, or a file reference. All variants share a common set of base keys. ### Base keys (all data records) | Property | CBOR key | JSON key | Type | Description | |---|---|---|---|---| | — | `class` | — | string | Type discriminator, see below. Only present in the binary (CBOR) encoding. | | ClassType | `classType` | `class` | string | Logical type of the record: `"SingleMeasurement"` or `"FileReference"`. | | Version | `version` | `ver` | integer | Schema version of the data record. Currently `1`. | | ValueType | `valueType` | `valueType` | string | .NET type name of the value, e.g. `"System.Double"`, `"System.Int32"`. | | SerializedValue | `serializedValue` | `value` | string | The value serialized as a JSON string, e.g. `"42.42"`. | | Value | `value` | — | any | The value encoded natively (number, string, map, …). Only present in the binary (CBOR) encoding. | > [!warning] > The key `class` means different things in the two encodings, and so does `value`: > - In the **binary (CBOR)** encoding, `class` is the type discriminator (the internal record name, e.g. `"SingleMeasurementDataGenericDto"`, `"FileReferenceDataGenericDto"` or `"FileBinaryDataGenericDto"`) and the logical type lives in `classType`. The native value lives in `value`, and the JSON-serialized copy in `serializedValue`. > - In the **[JSON Variant](rhopoint-elements-hub-rae-file-format-json-variant.md)**, `class` holds the logical type (`"SingleMeasurement"` / `"FileReference"`) and `value` holds the JSON-serialized value string. There is no separate discriminator or native value. When reading a binary RAE file, use `class` to select the concrete record type. To read the value itself you can usually use the native `value` directly; the reference implementation reconstructs it from `serializedValue` + `valueType`. ### SingleMeasurementData Used with `single` components. It adds no keys beyond the base keys — the measurement value lives in `value` / `serializedValue`. The value is typically a number, but any JSON-serializable type is allowed (`valueType` tells you what it is). Example (CBOR diagnostic notation): ``` { "class": "SingleMeasurementDataGenericDto", "classType": "SingleMeasurement", "version": 1, "valueType": "System.Double", "serializedValue": "87.3", "value": 87.3 } ``` ### FileReferenceData Used with `fileReference` components. It describes a file without embedding its content: | Property | CBOR key | JSON key | Type | Description | |---|---|---|---|---| | FileIdentifier | `fileIdentifier` | `fileId` | string | Identifier of the file. | | Filename | `filename` | `name` | string | Original file name. | | MimeType | `mimeType` | `type` | string | MIME type of the file content (e.g. `"image/png"`, `"image/x-raw"`). | | HashData | `hashData` | `hash` | map | Hash of the file content, see below. | | Metadata | `metadata` | `meta` | map | File metadata, see below. | The **HashData** map: | Property | CBOR key | JSON key | Type | Description | |---|---|---|---|---| | Algorithm | `algorithm` | `alg` | string (enum) | Hash algorithm. Currently `"sha256"`. | | Hash | `hash` | `hash` | string | The hash value. | The **FileMetadata** map: | Property | CBOR key | JSON key | Type | Description | |---|---|---|---|---| | Author | `author` | `author` | string | Author of the file. | | Created | `created` | `created` | string (ISO 8601) | Creation time of the file. | ### FileBinaryData Used with `file` components. It extends `FileReferenceData` by embedding the file content: | Property | CBOR key | JSON key | Type | Description | |---|---|---|---|---| | BinaryData | `binaryData` | `bin` | byte string | The raw file content. In the binary encoding this is a CBOR byte string; in the [JSON Variant](rhopoint-elements-hub-rae-file-format-json-variant.md) it is Base64-encoded. | Device images returned by Elements Hub use this record — see [Measurement Image Formats](rhopoint-elements-hub-measurement-image-formats.md) for the image encodings and how to decode the `image/x-raw` payload. Example (CBOR diagnostic notation, binary data shortened): ``` { "class": "FileBinaryDataGenericDto", "classType": "FileReference", "version": 1, "valueType": null, "serializedValue": null, "value": null, "fileIdentifier": "surfaceImage", "filename": "surfaceImage.png", "mimeType": "image/png", "hashData": null, "metadata": null, "binaryData": h'89504E470D0A1A0A...' } ``` --- # JSON Variant > [!note] Diese Seite ist noch nicht auf Deutsch verfügbar und wird auf Englisch angezeigt. Besides the binary representation, the RAE format has a JSON representation with the MIME type:\ `application/vnd.rhopoint.rae+json` A JSON RAE document is a plain UTF-8 JSON text — it has **no magic string, no compression and no hash**. The root object is the `RaeJsonFile` record: | Key | Type | Description | |---|---|---| | `format` | string | Identifies the data format. Currently always `"MeasurementComponentMetaTuple[]"`. | | `version` | integer | Version of the file format. The current version is `1`. | | `dataContainer` | string | Encoding of the payload. Currently always `"json"`. | | `data` | array | The measurements, one object per [MeasurementComponentMetaTuple](rhopoint-elements-hub-rae-file-format-measurementcomponentmetatuple.md). | ### Differences to the binary encoding The payload objects have the same structure as in the binary encoding, but: - **Abbreviated keys** are used (e.g. `mId` instead of `measurementIdentifier`, `srcId` instead of `sourceIdentifier`). The tables in [MeasurementComponentMetaTuple](rhopoint-elements-hub-rae-file-format-measurementcomponentmetatuple.md), [MeasurementComponent](rhopoint-elements-hub-rae-file-format-measurementcomponent.md) and [Measurement Data Types](rhopoint-elements-hub-rae-file-format-measurement-data-types.md) list both key sets. - **Unset properties are omitted** instead of being written as `null`. - **Binary content** (e.g. embedded files) is Base64-encoded. - In data records, `class` holds the logical type (`"SingleMeasurement"` / `"FileReference"`) and `value` holds the JSON-serialized value string — see the note in [Measurement Data Types](rhopoint-elements-hub-rae-file-format-measurement-data-types.md). ### Example ```json { "format": "MeasurementComponentMetaTuple[]", "version": 1, "dataContainer": "json", "data": [ { "Meta": { "srcId": "0d9f4c1e-6a2b-4f43-9c1a-2f5e8d7b3a10", "mId": "0d9f4c1e-6a2b-4f43-9c1a-2f5e8d7b3a10", "mType": "composite", "time": "2026-07-07T12:34:56.7890123+00:00", "name": "Sample A", "ver": 2 }, "Component": { "type": "composite", "name": "Measurement", "comp": [ { "type": "single", "name": "Gloss 60°", "data": { "class": "SingleMeasurement", "ver": 1, "value": "87.3", "valueType": "System.Double" }, "meta": { "unit": "GU" }, "ver": 2 } ], "ver": 2 } } ] } ```