MaximaLabsOpen app

Edge Telemetry Collector Daemon

`flowsim/twin/collector.py` — a Dockerized standalone service for a plant gateway VM: OPC-UA polling with reconnect + store-and-forward disk buffering, ingest-key auth, one outbound TLS connection back to the cloud digital twin.

edgeopc-uadigital-twin
Concept

A Dockerized, independently deployable service for a plant gateway VM. It polls mapped tags over the configured field protocol, authenticates to the MaximaLabs ingestion API with a revocable ingest key, and is built for unattended field deployment — reconnect with capped backoff, and store-and-forward so a network outage never loses a reading.

Execution

Protocols & durability

  • OPC-UA, Modbus TCP, EtherNet/IP, MQTT, or Sparkplug B — swapping the source swaps only the protocol driver; the durable pipeline underneath is identical.
  • MQTT / Sparkplug B — publish-subscribe rather than poll: the collector subscribes once and caches whatever arrives, so a brownfield IIoT gateway feeds the same durable pipeline as an OPC-UA site.
  • Store-and-forward — unreachable API → append to a capped local JSONL disk buffer, oldest dropped; flushed oldest-first on the next success.
  • One outbound connection — TLS + X-API-Key to the cloud twin; nothing inbound to the plant network.
python -m flowsim.twin.collector           # run continuously
python -m flowsim.twin.collector --once     # single poll cycle
How to Setup (GUI + Docker)

Deploy end to end

  1. Mint an ingest key — in the app, open a simulation's Twin panel → Ingest keysCreate. Copy the fsk_live_… key shown once; it authorizes writes to that simulation only and can be revoked anytime.
  2. Configure — copy flowsim/twin/.env.example to .env and set FLOWSIM_API, FLOWSIM_SIM_ID, FLOWSIM_INGEST_KEY, and a TAG_MAP (comma-separated FlowSimTag=protocol-address pairs — e.g. for OPC-UA: TI-101=ns=2;s=Feed.Temperature). Set SOURCE to opcua (default), modbus, ethernetip, mqtt, or sparkplug to pick the protocol.
  3. MQTT / Sparkplug B only — set MQTT_HOST (+ optional MQTT_PORT, MQTT_TLS, MQTT_USERNAME/ MQTT_PASSWORD). Plain mqtt maps each FlowSimTag to a topic (TI-101=plant/unit1/ti101); each topic carries a bare number or JSON with a "value" key. sparkplug instead maps each tag to a Sparkplug metric name (TI-101=Temperature) and needs SPARKPLUG_GROUP_ID + SPARKPLUG_EDGE_NODE_ID (optional SPARKPLUG_DEVICE_ID) — one Sparkplug message carries many named metrics on a single topic, unlike plain MQTT's one-topic-per-tag.
  4. Run it:
    docker compose -f flowsim/twin/docker-compose.collector.yml up -d --build
    docker compose -f flowsim/twin/docker-compose.collector.yml logs -f
    The buffer is a named volume, so it survives restarts. No published registry image — the compose file builds it locally from the bundled Dockerfile.
  5. Map the ingested tags — back in the Twin panel, map each FlowSimTag from your TAG_MAP to the flowsheet location (stream field or unit metric) it represents, via the labeled dropdown of converged variables — this is the "telemetry mapping" step, done in-app rather than a separate config file.
Troubleshooting & Common Errors
401 on ingestThe ingest key was revoked or belongs to a different simulation — mint a fresh one from that simulation's Twin panel.
buffer.jsonl growingThe collector can't reach the API (network outage, wrong FLOWSIM_API, or DNS) — readings are safely buffered, not lost; check connectivity, they flush oldest-first on the next success.
no data arriving despite a healthy connectionA TAG_MAP entry that doesn't match the source's actual address format for the chosen SOURCE (OPC-UA node id vs. Modbus kind:address:dtype vs. EtherNet/IP symbolic path vs. MQTT topic vs. Sparkplug metric name) silently reads nothing for that tag — double check the format for the protocol in use.
sparkplug values never updatetag_map for SOURCE=sparkplug must map to the Sparkplug metric name published on that node, not a topic — double check SPARKPLUG_GROUP_ID/SPARKPLUG_EDGE_NODE_ID match the publishing device exactly (Sparkplug topics are case-sensitive).