Covers Engine 2.0.0
DARTWIC Share
Exchange telemetry and commands in both directions, with recovery across engine, C++ client, and custom transport connections.
DARTWIC Share connects named nodes so they can keep exchanging state and commands across a link that may disconnect and recover. A node can do four things over one bidirectional connection:
| Direction | What it does | Flight-computer example |
|---|---|---|
| Send telemetry | Report locally owned state to the peer. | Flight computer sends battery voltage to the ground engine. |
| Receive telemetry | Consume the peer’s measurements and state. | Flight computer receives ground ambient temperature. |
| Send commands | Request a change to a channel owned by the peer. | Flight computer updates a ground-engine channel. |
| Receive commands | Apply requests to channels this node owns. | Ground operator changes the flight computer’s telemetry rate. |
The ground engine brings remote telemetry into its normal channel, interface, recording, and DCode workflows. An operator commands FLIGHT_COMPUTER:telemetry_rate_hz in the interface; Share delivers that command to the flight application’s handler. The application applies it and publishes its current state back. If the flight computer is behind a separate link adapter, that adapter implements the hardware link, forwards commands, and publishes the returned measurements.
Ground interface <--> DARTWIC ground engine <--> flight application / link adapter
commands -------> handler -------> hardware
telemetry <------ publisher <----- measurementsAutomatic recovery is central to Share: built-in TEMPEST connections detect loss, reconnect, and repeat the Hello exchange. A remembered engine connection is restored by the engine; the managed C++ client’s transport reconnects while the application stays running. With a custom link, your transport supplies failure detection and reconnect, and the protocol handles readiness when the link returns. Recovery resumes communication; it does not replay commands or guarantee delivery of telemetry sent during an outage.
Choose the connection you need:
| Setup | Engine side | Other side |
|---|---|---|
| 1. Engine to engine | Built-in TEMPEST Share transport | Another engine’s TEMPEST server |
| 2. External C++ node over TEMPEST | Existing TEMPEST server; no plugin required | DARTWICShareClient |
| 3. Custom transport | Engine plugin registers ShareTransport | DARTWICShareProtocol with a matching transport |
Each connection is bidirectional. Use distinct node names and avoid : and |, which delimit channel references. Open Cluster Map to inspect the resulting system.
1. Engine to engine over TEMPEST
Start both engines with distinct node names, then use the interface:
- Connect the interface to the first engine and open Cluster Map.
- Open Connect Remote Node and choose TEMPEST under Share Transport.
- Enter the second engine’s TEMPEST IP, TEMPEST Port (its base port), and Password, then choose Connect.
- Confirm the peer appears connected. Find a channel in Channel Search using
SECONDARY_NODE:channel_name, with the actual remote engine name.
One connection carries traffic in both directions. Use the base port, not the HTTP port; Share uses base port and base port + 1. Two engines on the same machine need non-overlapping port ranges, such as 7000�7002 and 7200�7202.
The interface connection is remembered by default in the active project’s edge_node_connections.json. Remembered peers can be offline; the engine retries saved connections. Disconnect and forget controls manage the target. To exercise both directions, change a channel on either engine and view it from the other; command a remote channel using its owning-node prefix. Commands remain subject to authority at the owning engine.
2. External C++ node over TEMPEST
The external application connects to the engine’s existing server using DARTWICShareClient. No engine transport plugin or separate engine-side connect operation is needed.
Follow the C++ Share Client guide to build a flight-computer example that demonstrates all four directions:
- Publish telemetry to the ground engine.
- Receive ground telemetry.
- Command a ground-engine channel.
- Accept a command from the ground interface.
Its query handler exposes current state for discovery and resynchronization. Its publication loop continues through a temporary outage, allowing the built-in transport to reconnect and resume telemetry. Your application still owns its measurements, command validation, and hardware behavior.
3. Custom transport and external application
Use this path for serial, radio, CAN, shared memory, or another connection the built-in client does not provide. Both ends implement the same ShareTransport interface.
Engine channels + ARGUS
|
engine Share runtime
|
plugin ShareTransport <--- your frame delivery ---> application ShareTransport
|
DARTWICShareProtocol
|
your application stateThe protocol SDK provides typed messages, Hello/readiness, session identity, and application APIs. Your transport owns delivery and request correlation. The engine supplies its own channel and ARGUS handlers; the external application supplies handlers for the state it owns.
Register the engine transport
Start with the example engine plugin. Its engine/include/example_share_transport.h and engine/src/example_share_transport.cpp implement a complete example transport; engine/src/example_device_plugin.cpp registers it during plugin initialization:
dartwic->registerShareTransport({
.id = "example_flight_link",
.name = "Example Flight Link",
.default_config = {
{"node_name", "FLIGHT_COMPUTER"},
{"receive_endpoint", "tcp://127.0.0.1:17600"},
{"send_endpoint", "tcp://127.0.0.1:17601"},
},
.create = [](const nlohmann::json& config) {
return std::make_shared<Example::ExampleShareTransport>(config);
},
});The callback receives the connection configuration and returns a fresh transport instance. Registration qualifies the local ID with the plugin ID. For example_device_plugin, the connect request therefore uses example_device_plugin.example_flight_link.
Build and install the plugin, then restart the engine. Open Connect Remote Node and confirm Example Flight Link appears in the Share Transport list. Use your own plugin and transport names if you renamed the starter.
Prepare a matching external transport
For a concrete first connection, copy those two example transport files into a separate application directory. Keep the filenames and Example::ExampleShareTransport class name. Make these changes in the application’s copy:
- In the header, replace
#include <sdk_api.h>with#include <nlohmann/json.hpp>and change the base class fromDARTWIC::API::ShareTransporttoDARTWIC::Share::ShareTransport. This lets the application use the standalone Protocol SDK. - In
start(), change both socket calls frombind(...)toconnect(...). - Reverse the endpoint configuration as shown below: the application’s PUSH socket connects to the engine’s PULL endpoint, and its PULL socket connects to the engine’s PUSH endpoint.
| Engine example binds | External application connects |
|---|---|
PULL receive_endpoint: tcp://127.0.0.1:17600 | PUSH send_endpoint: tcp://127.0.0.1:17600 |
PUSH send_endpoint: tcp://127.0.0.1:17601 | PULL receive_endpoint: tcp://127.0.0.1:17601 |
Supply both endpoints. The example deliberately uses a simulated peer when either endpoint is absent. This socket example is intended for a local integration test; a production transport must define its own link-failure detection, reconnect behavior, queue limits, and authentication as needed by the deployment.
Run the external application
Use the flight-computer application with the same incoming handlers and outgoing telemetry/commands. Replace its DARTWICShareClient construction with this protocol construction, and include example_share_transport.h instead of the Share Client header:
auto transport = std::make_shared<Example::ExampleShareTransport>(nlohmann::json{
{"receive_endpoint", "tcp://127.0.0.1:17601"},
{"send_endpoint", "tcp://127.0.0.1:17600"},
});
DARTWICShareProtocol share({.node_name = "FLIGHT_COMPUTER"}, transport);Remove the TEMPEST password lookup from this custom socket example. Keep the four channel workflows, query handler, and setHandlers call. Give waitUntilReady enough time to connect from the interface, for example 30 seconds. The application still owns FLIGHT_COMPUTER:battery_voltage and FLIGHT_COMPUTER:telemetry_rate_hz; only frame delivery changes.
Use installed Protocol SDK and cppzmq packages with this CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
project(custom_share_node LANGUAGES CXX)
find_package(DARTWICShareProtocol CONFIG REQUIRED)
find_package(cppzmq CONFIG REQUIRED)
add_executable(custom_share_node main.cpp example_share_transport.cpp)
target_compile_features(custom_share_node PRIVATE cxx_std_20)
target_link_libraries(custom_share_node PRIVATE DARTWIC::ShareProtocol cppzmq)Configure CMake with your dependency prefixes/toolchain, build, and start the application. While it waits for Hello, open Connect Remote Node in the ground interface:
| Field | Value for this example |
|---|---|
| Share Transport | Example Flight Link |
| Node Name | FLIGHT_COMPUTER |
| Receive Endpoint | tcp://127.0.0.1:17600 |
| Send Endpoint | tcp://127.0.0.1:17601 |
Choose Connect. Custom input fields come from the transport’s registered default_config; use your plugin’s labels if you changed them. The connection is remembered by default. The interface should show FLIGHT_COMPUTER, and Channel Search should find the queryable remote channels. Exercise the same four workflows from the C++ Share guide: publish flight state, receive ground state, command the ground node, and change the flight telemetry rate from the interface.
Replace the socket example with your transport
Keep the facade and engine registration. Replace socket delivery with your hardware or application link, preserving these contracts:
| Transport method or callback | Required behavior |
|---|---|
start(callbacks) | Retain callbacks, start workers, report state through on_state, and allow control frames to flow. A physical Connected state starts the Hello exchange. |
request(request, timeout) | Assign an ID, send the request, correlate its response, and return or throw within the timeout. Unblock pending requests on stop or disconnect. |
publish(telemetry) / control(control) | Deliver one-way frames independently of a pending request; the protocol can call these concurrently with request. |
on_request | Handle on a worker separate from receive/response processing. Preserve arrival order for non-response frames so Hello and updates cannot overtake one another. |
stop() | Stop workers and release callbacks before returning; no receive callback may outlive its owner. |
For a JSON wire format, use the JSON Codec. Send complete encoded frames with your own message framing. On receipt, use delivery(frame) to dispatch requests, responses, telemetry, and controls; use requestId(frame) for correlation. Reply with encodeResponse(response, original_request, original_id). Guard decoding against malformed fields, and handle unsupported frames explicitly. The codec does not provide sockets, length prefixes, retransmission, or a request worker.
Remote values, commands, and timing
| Action | What completion means |
|---|---|
waitUntilReady(timeout) | A valid Share Hello completed; throws on timeout or disconnection. transportConnected() alone is insufficient. |
| Channel upsert/remove/bulk commands and channel/ARGUS queries | Synchronous request/response calls. An acknowledgement confirms handler completion, not physical hardware feedback. |
| ARGUS respond/updateStatus/releaseHold/remove | Synchronous action request/response calls. |
| Channel telemetry and ARGUS message/warning/error/abort/hold/prompt publication | One-way publication. Returning does not confirm arrival or remote execution. Disconnected telemetry is dropped. |
| User state and telemetry callbacks | Run serially on a protocol-owned thread. They can issue synchronous Share requests; a slow callback delays later callbacks. |
Call stop() outside the user callback thread when you need it to drain already-dispatched callbacks before returning. Treat a request timeout as an unknown outcome: inspect state before repeating a command that must not execute twice.
A channel’s owning node determines where commands go. Use SECONDARY_NODE:tank_pressure as a remote channel key, and |SECONDARY_NODE:tank_pressure| in DCode or a graph reference. Remote commands still obey command authority.
Local reads see the most recently received remote state. An inline calculation runs when an update arrives; Share does not synchronize two engines’ execution clocks. See Execution and Timing for engine scheduling boundaries. RAPID sample timestamps and ARGUS event creation timestamps use Unix nanoseconds.
Channel filters use keys, not field paths. With rapid.share_all: false, list allowed keys in rapid.channels. Selected remote channels can be relayed; preserve owner/session/revision lineage and route metadata. ARGUS sharing covers direct-node event state and actions.
Diagnose a connection
Share exchanges snapshots and pushed updates. The legacy poll_interval_ms connection setting is not a control-loop period or a guaranteed telemetry interval. Per-peer connected, uplink, and downlink diagnostics update once per second. Mbps counts encoded Share payload, excluding transport framing; zero traffic can simply mean a quiet source.
| Symptom | Check |
|---|---|
| Unknown custom transport | Plugin loaded successfully; connect ID uses the plugin-qualified registration ID. |
| Physical connection without readiness | Both sides send/receive Hello, use compatible protocol versions, and process control frames independently of pending requests. |
| Custom example cannot exchange frames | Both endpoints supplied; engine binds, application connects, and PUSH/PULL endpoint roles are reversed. |
| Ready peer but no channel | Owner name, sharing filters, initial query handler, and whether the source actually published an update. |
| Commands time out while telemetry works | Request IDs and response correlation; request handling must not block the receive worker. |
| Channel stops changing after reconnect | Resume publication or supply a query handler for current state; unsent telemetry is not replayed. |
Use dartwic-share/list, transports, disconnect, and reconnect-remembered to inspect and manage peers.
The complete API is split into Share Client, Protocol, Transport, and engine transport registration.