Sparkplug B C++ Library 1.0.0
Modern C++-23 implementation of Eclipse Sparkplug B 2.2 specification
Loading...
Searching...
No Matches
sparkplug::EdgeNode Class Reference

Sparkplug B Edge Node implementing the complete message lifecycle. More...

#include <edge_node.hpp>

Classes

struct  Config
 Configuration parameters for the Sparkplug B Edge Node. More...
 
struct  TlsOptions
 TLS/SSL configuration options for secure MQTT connections. More...
 

Public Member Functions

 EdgeNode (Config config)
 Constructs an EdgeNode with the given configuration.
 
 ~EdgeNode ()
 Destroys the EdgeNode and cleans up MQTT resources.
 
 EdgeNode (const EdgeNode &)=delete
 
EdgeNodeoperator= (const EdgeNode &)=delete
 
 EdgeNode (EdgeNode &&) noexcept
 
EdgeNodeoperator= (EdgeNode &&) noexcept
 
void set_credentials (std::optional< std::string > username, std::optional< std::string > password)
 Sets MQTT username and password for authentication.
 
void set_tls (std::optional< TlsOptions > tls)
 Configures TLS/SSL options for secure MQTT connections.
 
void set_log_callback (std::optional< LogCallback > callback)
 
stdx::expected< void, std::string > connect ()
 Connects to the MQTT broker and establishes a Sparkplug B session.
 
stdx::expected< void, std::string > disconnect ()
 Gracefully disconnects from the MQTT broker.
 
stdx::expected< void, std::string > publish_birth (PayloadBuilder &payload)
 Publishes an NBIRTH (Node Birth) message.
 
stdx::expected< void, std::string > publish_data (PayloadBuilder &payload)
 Publishes an NDATA (Node Data) message.
 
stdx::expected< void, std::string > publish_death ()
 Publishes an NDEATH (Node Death) message.
 
stdx::expected< void, std::string > rebirth ()
 Triggers a rebirth by publishing a new NBIRTH with incremented bdSeq.
 
uint64_t get_seq () const
 Gets the current message sequence number.
 
uint64_t get_bd_seq () const
 Gets the current birth/death sequence number.
 
bool is_primary_host_online () const
 Checks if the primary host application is online.
 
stdx::expected< void, std::string > publish_device_birth (std::string_view device_id, PayloadBuilder &payload)
 Publishes a DBIRTH (Device Birth) message.
 
stdx::expected< void, std::string > publish_device_data (std::string_view device_id, PayloadBuilder &payload)
 Publishes a DDATA (Device Data) message.
 
stdx::expected< void, std::string > publish_device_death (std::string_view device_id)
 Publishes a DDEATH (Device Death) message.
 
stdx::expected< void, std::string > publish_node_command (std::string_view target_edge_node_id, PayloadBuilder &payload)
 Publishes an NCMD (Node Command) message to another edge node.
 
stdx::expected< void, std::string > publish_device_command (std::string_view target_edge_node_id, std::string_view target_device_id, PayloadBuilder &payload)
 Publishes a DCMD (Device Command) message to a device on another edge node.
 
void log (LogLevel level, std::string_view message) const noexcept
 

Detailed Description

Sparkplug B Edge Node implementing the complete message lifecycle.

The EdgeNode class manages the full Sparkplug B protocol for an edge node:

  • NBIRTH: Initial birth certificate with all metrics and aliases
  • NDATA: Subsequent data updates using aliases for bandwidth efficiency
  • NDEATH: Death certificate (sent via MQTT Last Will Testament)
  • Automatic sequence number management (0-255, wraps at 256)
  • Birth/Death sequence (bdSeq) tracking for session management
Thread Safety
This class is fully thread-safe with coarse-grained locking:
  • All public methods use a single internal mutex to protect shared state
  • Methods can be safely called from any thread concurrently
  • Callbacks (e.g., command_callback) are invoked on MQTT thread WITHOUT holding mutex
  • Mutex is released before MQTT publish to prevent callback deadlocks
  • Performance: Suitable for typical IIoT applications; not optimized for ultra-high-frequency (>10kHz) publishing from multiple threads
Threading Model
  • Application threads: Call EdgeNode methods (connect, publish_*, disconnect)
  • MQTT client thread: Paho async library handles network I/O and invokes callbacks
  • Synchronization: Single std::mutex protects all mutable state (seq_num_, bd_seq_num_, device_states_, last_birth_payload_, etc.)
  • Lock acquisition: Methods acquire mutex, prepare data, release before MQTT operations
  • Callback safety: User callbacks invoked without mutex held (safe to call EdgeNode methods)
  • Blocking operations: connect() and disconnect() block until completion or timeout
Rust FFI Compatibility
  • Implements Send: Can transfer between threads safely (all state mutex-protected)
  • Implements Sync: Can access from multiple threads concurrently (mutex-guarded methods)
Example Usage
.broker_url = "tcp://localhost:1883",
.client_id = "my_edge_node",
.group_id = "Energy",
.edge_node_id = "Gateway01"
};
sparkplug::EdgeNode edge_node(std::move(config));
edge_node.connect();
// Publish NBIRTH (required first message)
birth.add_metric_with_alias("Temperature", 1, 20.5);
edge_node.publish_birth(birth);
// Publish NDATA updates
data.add_metric_by_alias(1, 21.0); // Temperature changed
edge_node.publish_data(data);
edge_node.disconnect(); // Sends NDEATH automatically
Sparkplug B Edge Node implementing the complete message lifecycle.
Definition edge_node.hpp:96
Type-safe builder for Sparkplug B payloads with automatic type detection.
PayloadBuilder & add_metric_by_alias(uint64_t alias, T &&value)
Adds a metric by alias only (for NDATA messages).
PayloadBuilder & add_metric_with_alias(std::string_view name, uint64_t alias, T &&value)
Adds a metric with both name and alias (for NBIRTH messages).
Configuration parameters for the Sparkplug B Edge Node.
std::string broker_url
MQTT broker URL (e.g., "tcp://localhost:1883" or "ssl://localhost:8883")
See also
PayloadBuilder for constructing metric payloads
Subscriber for consuming Sparkplug B messages

Definition at line 96 of file edge_node.hpp.

Constructor & Destructor Documentation

◆ EdgeNode()

sparkplug::EdgeNode::EdgeNode ( Config  config)

Constructs an EdgeNode with the given configuration.

Parameters
configEdgeNode configuration (moved)
Note
The NDEATH payload is prepared during construction and will be sent automatically when the MQTT connection is lost.

Member Function Documentation

◆ connect()

stdx::expected< void, std::string > sparkplug::EdgeNode::connect ( )

Connects to the MQTT broker and establishes a Sparkplug B session.

Sets the NDEATH message as the MQTT Last Will Testament before connecting. The NDEATH will be sent automatically if the connection is lost unexpectedly.

Returns
void on success, error message on failure
Note
Must be called before publish_birth().
Warning
The EdgeNode must remain in scope while connected, or NDEATH may not be delivered properly.

◆ disconnect()

stdx::expected< void, std::string > sparkplug::EdgeNode::disconnect ( )

Gracefully disconnects from the MQTT broker.

Sends NDEATH via MQTT Last Will Testament and closes the connection.

Returns
void on success, error message on failure
Note
After disconnect, you can call connect() again to reconnect.

◆ get_bd_seq()

uint64_t sparkplug::EdgeNode::get_bd_seq ( ) const
inline

Gets the current birth/death sequence number.

Returns
Current bdSeq value (increments on each rebirth, never wraps)
Note
Used by SCADA to detect new sessions/rebirths.

Definition at line 298 of file edge_node.hpp.

◆ get_seq()

uint64_t sparkplug::EdgeNode::get_seq ( ) const
inline

Gets the current message sequence number.

Returns
Current sequence number (0-255, wraps at 256)
Note
Useful for monitoring and debugging.

Definition at line 286 of file edge_node.hpp.

◆ is_primary_host_online()

bool sparkplug::EdgeNode::is_primary_host_online ( ) const
inline

Checks if the primary host application is online.

Returns
true if primary host is online (or no primary host configured), false otherwise
Note
Returns true immediately if no primary_host_id was configured.
Used to determine if NBIRTH/DBIRTH can be published.

Definition at line 312 of file edge_node.hpp.

◆ publish_birth()

stdx::expected< void, std::string > sparkplug::EdgeNode::publish_birth ( PayloadBuilder payload)

Publishes an NBIRTH (Node Birth) message.

The NBIRTH message must be the first message published after connect(). It establishes the session and declares all available metrics with their aliases.

Parameters
payloadPayloadBuilder containing metrics with names and aliases
Returns
void on success, error message on failure
Note
The payload should include:
  • All metrics with both name and alias (for NDATA to use aliases)
  • bdSeq metric (automatically managed if using rebirth())
  • Any metadata or properties
Warning
Must be called after connect() and before any publish_data() calls.
See also
publish_data() for subsequent updates
rebirth() for publishing a new NBIRTH during runtime

◆ publish_data()

stdx::expected< void, std::string > sparkplug::EdgeNode::publish_data ( PayloadBuilder payload)

Publishes an NDATA (Node Data) message.

NDATA messages are used for Report by Exception updates. Your application is responsible for determining WHEN to call this method based on whether metrics have changed according to your domain-specific criteria (e.g., deadband thresholds, exact equality, percentage change). The payload should use aliases for bandwidth efficiency (60-80% reduction vs. full names).

Parameters
payloadPayloadBuilder containing changed metrics (by alias only)
Returns
void on success, error message on failure
Note
Sequence number is automatically incremented (0-255, wraps at 256).
Timestamp is automatically added if not explicitly set.
The library provides the transport mechanism; you provide the RBE logic.
Warning
Must call publish_birth() before the first publish_data().
See also
publish_birth() for establishing aliases

◆ publish_death()

stdx::expected< void, std::string > sparkplug::EdgeNode::publish_death ( )

Publishes an NDEATH (Node Death) message.

Explicitly sends the NDEATH message. Usually not needed as NDEATH is sent automatically via MQTT Last Will Testament on disconnect or connection loss.

Returns
void on success, error message on failure
Note
Prefer using disconnect() which handles NDEATH automatically.

◆ publish_device_birth()

stdx::expected< void, std::string > sparkplug::EdgeNode::publish_device_birth ( std::string_view  device_id,
PayloadBuilder payload 
)

Publishes a DBIRTH (Device Birth) message.

The DBIRTH message declares a device attached to this edge node. It must be published after NBIRTH and declares all device metrics with aliases.

Parameters
device_idThe device identifier (e.g., "Sensor01", "Motor02")
payloadPayloadBuilder containing device metrics with names and aliases
Returns
void on success, error message on failure
Note
Device messages share the node's sequence counter. DBIRTH increments the sequence number from where NBIRTH left it (NBIRTH=0, DBIRTH=1, etc.).
Must call publish_birth() before publishing any device births.
See also
publish_device_data() for subsequent device updates
publish_device_death() for device disconnection

◆ publish_device_command()

stdx::expected< void, std::string > sparkplug::EdgeNode::publish_device_command ( std::string_view  target_edge_node_id,
std::string_view  target_device_id,
PayloadBuilder payload 
)

Publishes a DCMD (Device Command) message to a device on another edge node.

DCMD messages are commands sent to devices attached to edge nodes.

Parameters
target_edge_node_idThe target edge node identifier
target_device_idThe target device identifier
payloadPayloadBuilder containing command metrics
Returns
void on success, error message on failure
Example Usage
cmd.add_metric("SetPoint", 75.0);
edge_node.publish_device_command("Gateway01", "Motor01", cmd);
PayloadBuilder & add_metric(std::string_view name, T &&value)
Adds a metric by name only (for NBIRTH without aliases).

◆ publish_device_data()

stdx::expected< void, std::string > sparkplug::EdgeNode::publish_device_data ( std::string_view  device_id,
PayloadBuilder payload 
)

Publishes a DDATA (Device Data) message.

DDATA messages are used for Report by Exception updates. Your application is responsible for determining WHEN to call this method based on whether device metrics have changed according to your domain-specific criteria. The payload should use aliases for bandwidth efficiency.

Parameters
device_idThe device identifier
payloadPayloadBuilder containing changed metrics (by alias only)
Returns
void on success, error message on failure
Note
Sequence number is automatically incremented per device (0-255, wraps at 256).
Must call publish_device_birth() before the first publish_device_data().
The library provides the transport mechanism; you provide the RBE logic.
See also
publish_device_birth() for establishing aliases

◆ publish_device_death()

stdx::expected< void, std::string > sparkplug::EdgeNode::publish_device_death ( std::string_view  device_id)

Publishes a DDEATH (Device Death) message.

Explicitly sends a device death message to indicate device disconnection.

Parameters
device_idThe device identifier
Returns
void on success, error message on failure
Note
After DDEATH, publish_device_birth() must be called again before DDATA.

◆ publish_node_command()

stdx::expected< void, std::string > sparkplug::EdgeNode::publish_node_command ( std::string_view  target_edge_node_id,
PayloadBuilder payload 
)

Publishes an NCMD (Node Command) message to another edge node.

NCMD messages are commands sent from SCADA/Primary Applications or other edge nodes to request actions like rebirth, reboot, or custom operations.

Parameters
target_edge_node_idThe target edge node identifier
payloadPayloadBuilder containing command metrics (e.g., "Node Control/Rebirth")
Returns
void on success, error message on failure
Note
Common Node Control commands:
  • "Node Control/Rebirth" (bool): Request node to republish NBIRTH
  • "Node Control/Reboot" (bool): Request node to reboot
  • "Node Control/Next Server" (bool): Switch to backup server
  • "Node Control/Scan Rate" (int64): Change data acquisition rate
Example Usage
cmd.add_metric("Node Control/Rebirth", true);
edge_node.publish_node_command("Gateway01", cmd);

◆ rebirth()

stdx::expected< void, std::string > sparkplug::EdgeNode::rebirth ( )

Triggers a rebirth by publishing a new NBIRTH with incremented bdSeq.

Rebirth is used when:

  • SCADA/Primary Application requests it via NCMD/Rebirth
  • New metrics need to be added to the metric inventory
  • Edge node configuration changes
Returns
void on success, error message on failure
Note
Automatically increments bdSeq and resets sequence number to 0.
Republishes the last NBIRTH payload with updated bdSeq.
Warning
The new NBIRTH should contain ALL metrics (old + new), not just additions.

◆ set_credentials()

void sparkplug::EdgeNode::set_credentials ( std::optional< std::string >  username,
std::optional< std::string >  password 
)

Sets MQTT username and password for authentication.

Parameters
usernameMQTT username (empty string or std::nullopt to unset)
passwordMQTT password (empty string or std::nullopt to unset)
Note
Must be called before connect().

◆ set_tls()

void sparkplug::EdgeNode::set_tls ( std::optional< TlsOptions tls)

Configures TLS/SSL options for secure MQTT connections.

Parameters
tlsTLS configuration options
Note
Must be called before connect().
broker_url must use ssl:// prefix for TLS connections.

The documentation for this class was generated from the following file: