5#include "sparkplug_b.pb.h"
24 std::same_as<std::remove_cvref_t<T>, int16_t> ||
25 std::same_as<std::remove_cvref_t<T>, int32_t> ||
26 std::same_as<std::remove_cvref_t<T>, int64_t>;
31 std::same_as<std::remove_cvref_t<T>, uint16_t> ||
32 std::same_as<std::remove_cvref_t<T>, uint32_t> ||
33 std::same_as<std::remove_cvref_t<T>, uint64_t>;
42 std::same_as<std::remove_cvref_t<T>,
double>;
64template <SparkplugMetricType T>
65consteval DataType get_datatype()
noexcept {
66 using BaseT = std::remove_cvref_t<T>;
67 if constexpr (std::is_same_v<BaseT, int8_t>)
68 return DataType::Int8;
69 else if constexpr (std::is_same_v<BaseT, int16_t>)
70 return DataType::Int16;
71 else if constexpr (std::is_same_v<BaseT, int32_t>)
72 return DataType::Int32;
73 else if constexpr (std::is_same_v<BaseT, int64_t>)
74 return DataType::Int64;
75 else if constexpr (std::is_same_v<BaseT, uint8_t>)
76 return DataType::UInt8;
77 else if constexpr (std::is_same_v<BaseT, uint16_t>)
78 return DataType::UInt16;
79 else if constexpr (std::is_same_v<BaseT, uint32_t>)
80 return DataType::UInt32;
81 else if constexpr (std::is_same_v<BaseT, uint64_t>)
82 return DataType::UInt64;
83 else if constexpr (std::is_same_v<BaseT, float>)
84 return DataType::Float;
85 else if constexpr (std::is_same_v<BaseT, double>)
86 return DataType::Double;
87 else if constexpr (std::is_same_v<BaseT, bool>)
88 return DataType::Boolean;
90 return DataType::String;
93template <SparkplugMetricType T>
94void set_metric_value(org::eclipse::tahu::protobuf::Payload::Metric* metric, T&& value) {
95 using BaseT = std::remove_cvref_t<T>;
97 if constexpr (std::is_same_v<BaseT, int8_t> || std::is_same_v<BaseT, int16_t> ||
98 std::is_same_v<BaseT, int32_t> || std::is_same_v<BaseT, uint8_t> ||
99 std::is_same_v<BaseT, uint16_t> || std::is_same_v<BaseT, uint32_t>) {
100 metric->set_int_value(value);
101 }
else if constexpr (std::is_same_v<BaseT, int64_t> ||
102 std::is_same_v<BaseT, uint64_t>) {
103 metric->set_long_value(value);
104 }
else if constexpr (std::is_same_v<BaseT, float>) {
105 metric->set_float_value(value);
106 }
else if constexpr (std::is_same_v<BaseT, double>) {
107 metric->set_double_value(value);
108 }
else if constexpr (std::is_same_v<BaseT, bool>) {
109 metric->set_boolean_value(value);
112 metric->set_string_value(std::string(value));
116template <SparkplugMetricType T>
117void add_metric_to_payload(org::eclipse::tahu::protobuf::Payload& payload,
118 std::string_view name,
120 std::optional<uint64_t> alias,
121 std::optional<uint64_t> timestamp_ms) {
122 auto* metric = payload.add_metrics();
125 metric->set_name(std::string(name));
127 if (alias.has_value()) {
128 metric->set_alias(*alias);
131 metric->set_datatype(std::to_underlying(get_datatype<T>()));
132 set_metric_value(metric, std::forward<T>(value));
136 if (timestamp_ms.has_value()) {
139 auto now = std::chrono::system_clock::now();
140 ts = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch())
143 metric->set_timestamp(ts);
205 template <SparkplugMetricType T>
207 detail::add_metric_to_payload(payload_, name, std::forward<T>(value), std::nullopt,
224 template <SparkplugMetricType T>
226 detail::add_metric_to_payload(payload_, name, std::forward<T>(value), std::nullopt,
243 template <SparkplugMetricType T>
246 detail::add_metric_to_payload(payload_, name, std::forward<T>(value), alias,
265 template <SparkplugMetricType T>
269 uint64_t timestamp_ms) {
270 detail::add_metric_to_payload(payload_, name, std::forward<T>(value), alias,
289 template <SparkplugMetricType T>
291 detail::add_metric_to_payload(payload_,
"", std::forward<T>(value), alias,
308 template <SparkplugMetricType T>
310 detail::add_metric_to_payload(payload_,
"", std::forward<T>(value), alias,
325 payload_.set_timestamp(ts);
326 timestamp_explicitly_set_ =
true;
340 payload_.set_seq(seq);
341 seq_explicitly_set_ =
true;
356 PayloadBuilder& add_node_control_next_server(
bool value =
false) {
357 add_metric(
"Node Control/Next Server", value);
367 [[nodiscard]]
bool has_seq() const noexcept {
368 return seq_explicitly_set_;
370 [[nodiscard]]
bool has_timestamp() const noexcept {
371 return timestamp_explicitly_set_;
375 [[nodiscard]] std::vector<uint8_t> build()
const;
376 [[nodiscard]]
const org::eclipse::tahu::protobuf::Payload& payload() const noexcept;
377 [[nodiscard]] org::eclipse::tahu::protobuf::Payload& mutable_payload() noexcept {
382 org::eclipse::tahu::protobuf::Payload payload_;
383 bool seq_explicitly_set_{
false};
384 bool timestamp_explicitly_set_{
false};
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 & set_seq(uint64_t seq)
Sets the sequence number manually.
PayloadBuilder & add_metric(std::string_view name, T &&value, uint64_t timestamp_ms)
Adds a metric by name with a custom timestamp.
PayloadBuilder & add_metric(std::string_view name, T &&value)
Adds a metric by name only (for NBIRTH without aliases).
PayloadBuilder & add_metric_by_alias(uint64_t alias, T &&value, uint64_t timestamp_ms)
Adds a metric by alias with a custom timestamp.
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).
PayloadBuilder & set_timestamp(uint64_t ts)
Sets the payload-level timestamp.
PayloadBuilder()
Constructs an empty payload.
PayloadBuilder & add_metric_with_alias(std::string_view name, uint64_t alias, T &&value, uint64_t timestamp_ms)
Adds a metric with name, alias, and custom timestamp (for NBIRTH with historical data).
Floating-point types supported by Sparkplug B.
Any integer type supported by Sparkplug B (signed or unsigned)
Any metric type supported by Sparkplug B.
Numeric types (integers and floats)
Signed integer types supported by Sparkplug B.
String-like types supported by Sparkplug B.
Unsigned integer types supported by Sparkplug B.