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-Keyto the cloud twin; nothing inbound to the plant network.
python -m flowsim.twin.collector # run continuously
python -m flowsim.twin.collector --once # single poll cycleHow to Setup (GUI + Docker)
Deploy end to end
- Mint an ingest key — in the app, open a simulation's Twin panel → Ingest keys → Create. Copy the
fsk_live_…key shown once; it authorizes writes to that simulation only and can be revoked anytime. - Configure — copy
flowsim/twin/.env.exampleto.envand setFLOWSIM_API,FLOWSIM_SIM_ID,FLOWSIM_INGEST_KEY, and aTAG_MAP(comma-separatedFlowSimTag=protocol-addresspairs — e.g. for OPC-UA:TI-101=ns=2;s=Feed.Temperature). SetSOURCEtoopcua(default),modbus,ethernetip,mqtt, orsparkplugto pick the protocol. - MQTT / Sparkplug B only — set
MQTT_HOST(+ optionalMQTT_PORT,MQTT_TLS,MQTT_USERNAME/MQTT_PASSWORD). Plainmqttmaps eachFlowSimTagto a topic (TI-101=plant/unit1/ti101); each topic carries a bare number or JSON with a"value"key.sparkpluginstead maps each tag to a Sparkplug metric name (TI-101=Temperature) and needsSPARKPLUG_GROUP_ID+SPARKPLUG_EDGE_NODE_ID(optionalSPARKPLUG_DEVICE_ID) — one Sparkplug message carries many named metrics on a single topic, unlike plain MQTT's one-topic-per-tag. - Run it:The buffer is a named volume, so it survives restarts. No published registry image — the compose file builds it locally from the bundled Dockerfile.
docker compose -f flowsim/twin/docker-compose.collector.yml up -d --build docker compose -f flowsim/twin/docker-compose.collector.yml logs -f - Map the ingested tags — back in the Twin panel, map each
FlowSimTagfrom yourTAG_MAPto 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).