From 0eec736bafab0f02c8f6e7908f8cbd23f5235d4b Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 14 Jan 2026 13:20:03 +0100 Subject: [PATCH 01/49] mqtt: renaming --- .../custom-mqtt-sub/src/custom-mqtt-sub.cpp | 8 +-- examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp | 6 +-- .../ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp | 8 +-- .../include/mqtt_streaming_module/constants.h | 54 +++++++++---------- .../src/mqtt_json_receiver_fb_impl.cpp | 5 +- .../src/mqtt_publisher_fb_impl.cpp | 21 ++++---- .../src/mqtt_raw_receiver_fb_impl.cpp | 5 +- 7 files changed, 49 insertions(+), 58 deletions(-) diff --git a/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp b/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp index 4b13367..19dd00c 100644 --- a/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp +++ b/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp @@ -123,17 +123,17 @@ int main(int argc, char* argv[]) // Create OpenDAQ instance and add MQTT broker FB const InstancePtr instance = InstanceBuilder().addModulePath(MODULE_PATH).build(); - const std::string rootFbName = "RootMqttFb"; + const std::string rootFbName = "MQTTClientFB"; auto rootFbConfig = instance.getAvailableFunctionBlockTypes().get(rootFbName).createDefaultConfig(); - rootFbConfig.setPropertyValue("MqttBrokerAddress", appConfig.brokerAddress); + rootFbConfig.setPropertyValue("MQTTBrokerAddress", appConfig.brokerAddress); auto brokerFB = instance.addFunctionBlock(rootFbName, rootFbConfig); auto availableFbs = brokerFB.getAvailableFunctionBlockTypes(); - const std::string jsonFbName = "JsonSubscriberMqttFb"; + const std::string jsonFbName = "JSONSubscriberMQTTFB"; std::cout << "Try to add the " << jsonFbName << std::endl; auto config = availableFbs.get(jsonFbName).createDefaultConfig(); - config.setPropertyValue("JsonConfigFile", appConfig.configFilePath); + config.setPropertyValue("JSONConfigFile", appConfig.configFilePath); // Add the JSON function block to the broker FB daq::FunctionBlockPtr jsonFb = brokerFB.addFunctionBlock(jsonFbName, config); diff --git a/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp b/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp index cb6946b..28160a6 100644 --- a/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp +++ b/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp @@ -55,13 +55,13 @@ int main(int argc, char* argv[]) // Create OpenDAQ instance and add MQTT broker FB const InstancePtr instance = InstanceBuilder().addModulePath(MODULE_PATH).build(); - const std::string rootFbName = "RootMqttFb"; + const std::string rootFbName = "MQTTClientFB"; auto rootFbConfig = instance.getAvailableFunctionBlockTypes().get(rootFbName).createDefaultConfig(); - rootFbConfig.setPropertyValue("MqttBrokerAddress", appConfig.brokerAddress); + rootFbConfig.setPropertyValue("MQTTBrokerAddress", appConfig.brokerAddress); auto brokerFB = instance.addFunctionBlock(rootFbName, rootFbConfig); auto availableFbs = brokerFB.getAvailableFunctionBlockTypes(); - const std::string fbName = "RawSubscriberMqttFb"; + const std::string fbName = "RawSubscriberMQTTFB"; std::cout << "Try to add the " << fbName << std::endl; // Create RAW function block configuration diff --git a/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp b/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp index e6baf9a..4b9fc52 100644 --- a/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp +++ b/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp @@ -89,16 +89,16 @@ int main(int argc, char* argv[]) channels[3].setPropertyValue("Frequency", 20); // Create and configure MQTT server - const std::string rootFbName = "RootMqttFb"; + const std::string rootFbName = "MQTTClientFB"; auto rootFbConfig = instance.getAvailableFunctionBlockTypes().get(rootFbName).createDefaultConfig(); - rootFbConfig.setPropertyValue("MqttBrokerAddress", appConfig.brokerAddress); + rootFbConfig.setPropertyValue("MQTTBrokerAddress", appConfig.brokerAddress); auto brokerFB = instance.addFunctionBlock(rootFbName, rootFbConfig); auto availableFbs = brokerFB.getAvailableFunctionBlockTypes(); - const std::string fbName = "PublisherMqttFb"; + const std::string fbName = "PublisherMQTTFB"; std::cout << "Try to add the " << fbName << std::endl; auto config = availableFbs.get(fbName).createDefaultConfig(); - config.setPropertyValue("MqttQoS", 1); + config.setPropertyValue("QoS", 1); config.setPropertyValue("ReaderPeriod", 20); config.setPropertyValue("UseSignalNames", True); switch (appConfig.mode) { diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index 779f290..fb25ec7 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -4,9 +4,9 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -static const char* MODULE_NAME = "OpenDaqMqttModule"; -static const char* MODULE_ID = "OpenDaqMqttModule"; -static const char* SHORT_MODULE_NAME = "MqttModule"; +static const char* MODULE_NAME = "OpenDAQMQTTModule"; +static const char* MODULE_ID = "OpenDAQMQTTModule"; +static const char* SHORT_MODULE_NAME = "MQTTModule"; static constexpr const char* DEFAULT_BROKER_ADDRESS = "127.0.0.1"; static constexpr uint16_t DEFAULT_PORT = 1883; @@ -19,16 +19,16 @@ static constexpr uint32_t DEFAULT_PUB_QOS = 1; static constexpr uint32_t DEFAULT_SUB_QOS = 1; static constexpr uint32_t DEFAULT_PUB_PACK_SIZE = 1; -static constexpr const char* DEFAULT_SIGNAL_NAME = "mqttValueSignal"; +static constexpr const char* DEFAULT_SIGNAL_NAME = "MQTTValueSignal"; -static constexpr const char* PROPERTY_NAME_MQTT_BROKER_ADDRESS = "MqttBrokerAddress"; -static constexpr const char* PROPERTY_NAME_MQTT_BROKER_PORT = "MqttBrokerPort"; -static constexpr const char* PROPERTY_NAME_MQTT_USERNAME = "MqttUsername"; -static constexpr const char* PROPERTY_NAME_MQTT_PASSWORD = "MqttPassword"; +static constexpr const char* PROPERTY_NAME_MQTT_BROKER_ADDRESS = "MQTTBrokerAddress"; +static constexpr const char* PROPERTY_NAME_MQTT_BROKER_PORT = "MQTTBrokerPort"; +static constexpr const char* PROPERTY_NAME_MQTT_USERNAME = "MQTTUsername"; +static constexpr const char* PROPERTY_NAME_MQTT_PASSWORD = "MQTTPassword"; static constexpr const char* PROPERTY_NAME_CONNECT_TIMEOUT = "ConnectTimeout"; static constexpr const char* PROPERTY_NAME_SIGNAL_LIST = "SignalList"; -static constexpr const char* PROPERTY_NAME_JSON_CONFIG = "JsonConfig"; -static constexpr const char* PROPERTY_NAME_JSON_CONFIG_FILE = "JsonConfigFile"; +static constexpr const char* PROPERTY_NAME_JSON_CONFIG = "JSONConfig"; +static constexpr const char* PROPERTY_NAME_JSON_CONFIG_FILE = "JSONConfigFile"; static constexpr const char* PROPERTY_NAME_TOPIC = "Topic"; static constexpr const char* PROPERTY_NAME_VALUE_NAME = "ValueName"; static constexpr const char* PROPERTY_NAME_TS_NAME = "TimestampName"; @@ -42,29 +42,29 @@ static constexpr const char* PROPERTY_NAME_PUB_SHARED_TS = "SharedTimestamp"; static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES = "GroupValues"; static constexpr const char* PROPERTY_NAME_PUB_USE_SIGNAL_NAMES = "UseSignalNames"; static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE = "GroupValuesPackSize"; -static constexpr const char* PROPERTY_NAME_PUB_QOS = "MqttQoS"; -static constexpr const char* PROPERTY_NAME_SUB_QOS = "MqttQoS"; +static constexpr const char* PROPERTY_NAME_PUB_QOS = "QoS"; +static constexpr const char* PROPERTY_NAME_SUB_QOS = "QoS"; static constexpr const char* PROPERTY_NAME_PUB_READ_PERIOD = "ReaderPeriod"; -static constexpr const char* RAW_FB_NAME = "RawSubscriberMqttFb"; -static constexpr const char* JSON_FB_NAME = "JsonSubscriberMqttFb"; -static constexpr const char* PUB_FB_NAME = "PublisherMqttFb"; -static constexpr const char* ROOT_FB_NAME = "RootMqttFb"; -static constexpr const char* JSON_DECODER_FB_NAME = "JsonDecoderMqttFb"; +static constexpr const char* RAW_FB_NAME = "RawSubscriberMQTTFB"; +static constexpr const char* JSON_FB_NAME = "JSONSubscriberMQTTFB"; +static constexpr const char* PUB_FB_NAME = "PublisherMQTTFB"; +static constexpr const char* ROOT_FB_NAME = "MQTTClientFB"; +static constexpr const char* JSON_DECODER_FB_NAME = "JSONDecoderMQTTTFB"; -static const char* MQTT_LOCAL_ROOT_FB_ID_PREFIX = "RootMqttFb"; -static const char* MQTT_LOCAL_PUB_FB_ID_PREFIX = "PublisherMqttFb"; -static const char* MQTT_LOCAL_RAW_FB_ID_PREFIX = "RawSubscriberMqttFb"; -static const char* MQTT_LOCAL_JSON_FB_ID_PREFIX = "JsonSubscriberMqttFb"; -static const char* MQTT_LOCAL_JSON_DECODER_FB_ID_PREFIX = "JsonDecoderMqttFb"; +static const char* MQTT_LOCAL_ROOT_FB_ID_PREFIX = "MQTTClientFB"; +static const char* MQTT_LOCAL_PUB_FB_ID_PREFIX = "PublisherMQTTFB"; +static const char* MQTT_LOCAL_RAW_FB_ID_PREFIX = "RawSubscriberMQTTFB"; +static const char* MQTT_LOCAL_JSON_FB_ID_PREFIX = "JSONSubscriberMQTTFB"; +static const char* MQTT_LOCAL_JSON_DECODER_FB_ID_PREFIX = "JSONDecoderMQTTTFB"; static const char* MQTT_ROOT_FB_CON_STATUS_TYPE = "BrokerConnectionStatusType"; -static const char* MQTT_FB_SUB_STATUS_TYPE = "MqttSubscriptionStatusType"; -static const char* MQTT_PUB_FB_SIG_STATUS_TYPE = "MqttSignalStatusType"; -static const char* MQTT_PUB_FB_PUB_STATUS_TYPE = "MqttPublishingStatusType"; -static const char* MQTT_FB_PARSING_STATUS_TYPE = "MqttParsingStatusType"; -static const char* MQTT_PUB_FB_SET_STATUS_TYPE = "MqttSettingStatusType"; +static const char* MQTT_FB_SUB_STATUS_TYPE = "MQTTSubscriptionStatusType"; +static const char* MQTT_PUB_FB_SIG_STATUS_TYPE = "MQTTSignalStatusType"; +static const char* MQTT_PUB_FB_PUB_STATUS_TYPE = "MQTTPublishingStatusType"; +static const char* MQTT_FB_PARSING_STATUS_TYPE = "MQTTParsingStatusType"; +static const char* MQTT_PUB_FB_SET_STATUS_TYPE = "MQTTSettingStatusType"; static const char* MQTT_ROOT_FB_CON_STATUS_NAME = "ConnectionStatus"; diff --git a/mqtt_streaming_module/src/mqtt_json_receiver_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_json_receiver_fb_impl.cpp index 341a2ac..d8cfb28 100644 --- a/mqtt_streaming_module/src/mqtt_json_receiver_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_json_receiver_fb_impl.cpp @@ -86,10 +86,7 @@ FunctionBlockTypePtr MqttJsonReceiverFbImpl::CreateType() } { auto builder = - IntPropertyBuilder(PROPERTY_NAME_SUB_QOS, DEFAULT_SUB_QOS) - .setMinValue(0) - .setMaxValue(2) - .setSuggestedValues(List(0, 1, 2)) + SelectionPropertyBuilder(PROPERTY_NAME_PUB_QOS, List(0, 1, 2), DEFAULT_PUB_QOS) .setDescription( fmt::format("MQTT Quality of Service level for subscribing. It can be 0 (at most once), 1 (at least once), or 2 " "(exactly once). By default it is set to {}.", diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index ab5a0bf..c6b2f96 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -66,25 +66,25 @@ FunctionBlockTypePtr MqttPublisherFbImpl::CreateType() auto defaultConfig = PropertyObject(); { auto builder = - SelectionPropertyBuilder(PROPERTY_NAME_PUB_TOPIC_MODE, List("single-topic", "multiple-topic"), 0) + SelectionPropertyBuilder(PROPERTY_NAME_PUB_TOPIC_MODE, List("TopicPerSignal", "SingleTopic"), 0) .setDescription( - "Selects whether to publish all signals to separate MQTT topics (one per signal, single-topic mode) or to a single " - "topic (multiple-topic mode), one for all signals. Choose 0 for single-topic mode, 1 for multiple-topic mode. By " - "default it is set to single-topic mode."); + "Selects whether to publish all signals to separate MQTT topics (one per signal, TopicPerSignal mode) or to a single " + "topic (SingleTopic mode), one for all signals. Choose 0 for TopicPerSignal mode, 1 for SingleTopic mode. By " + "default it is set to TopicPerSignal mode."); defaultConfig.addProperty(builder.build()); } { auto builder = StringPropertyBuilder(PROPERTY_NAME_PUB_TOPIC_NAME, "") .setDescription( - "Topic name for publishing in multiple-topic mode. If left empty, the Publisher's Global ID is used as the topic name.") + "Topic name for publishing in SingleTopic mode. If left empty, the Publisher's Global ID is used as the topic name.") .setVisible(EvalValue(std::string("$") + PROPERTY_NAME_PUB_TOPIC_MODE + " == 1")); defaultConfig.addProperty(builder.build()); } { auto builder = BoolPropertyBuilder(PROPERTY_NAME_PUB_SHARED_TS, False) .setVisible(EvalValue(std::string("$") + PROPERTY_NAME_PUB_TOPIC_MODE + " == 1")) - .setDescription("Enables the use of a shared timestamp for all signals when publishing in multiple-topic mode. " + .setDescription("Enables the use of a shared timestamp for all signals when publishing in SingleTopic mode. " "By default it is set to false."); defaultConfig.addProperty(builder.build()); } @@ -93,7 +93,7 @@ FunctionBlockTypePtr MqttPublisherFbImpl::CreateType() BoolPropertyBuilder(PROPERTY_NAME_PUB_GROUP_VALUES, False) .setVisible(EvalValue(std::string("$") + PROPERTY_NAME_PUB_TOPIC_MODE + " == 0")) .setDescription( - "Enables the use of a sample pack for a signal when publishing in single-topic mode. By default it is set to false."); + "Enables the use of a sample pack for a signal when publishing in TopicPerSignal mode. By default it is set to false."); defaultConfig.addProperty(builder.build()); } { @@ -107,17 +107,14 @@ FunctionBlockTypePtr MqttPublisherFbImpl::CreateType() .setMinValue(1) .setVisible(EvalValue(std::string("($") + PROPERTY_NAME_PUB_TOPIC_MODE + " == 0) && " + std::string("($") + PROPERTY_NAME_PUB_GROUP_VALUES + ")")) - .setDescription(fmt::format("Set the size of the sample pack when publishing grouped values in single-topic mode. " + .setDescription(fmt::format("Set the size of the sample pack when publishing grouped values in TopicPerSignal mode. " "By default it is set to {}.", DEFAULT_PUB_PACK_SIZE)); defaultConfig.addProperty(builder.build()); } { auto builder = - IntPropertyBuilder(PROPERTY_NAME_PUB_QOS, DEFAULT_PUB_QOS) - .setMinValue(0) - .setMaxValue(2) - .setSuggestedValues(List(0, 1, 2)) + SelectionPropertyBuilder(PROPERTY_NAME_PUB_QOS, List(0, 1, 2), DEFAULT_PUB_QOS) .setDescription( fmt::format("MQTT Quality of Service level for published messages. It can be 0 (at most once), 1 (at least once), or 2 " "(exactly once). By default it is set to {}.", diff --git a/mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp index bfa6a38..f4e657f 100644 --- a/mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp @@ -37,10 +37,7 @@ FunctionBlockTypePtr MqttRawReceiverFbImpl::CreateType() auto defaultConfig = PropertyObject(); { auto builder = - IntPropertyBuilder(PROPERTY_NAME_SUB_QOS, DEFAULT_SUB_QOS) - .setMinValue(0) - .setMaxValue(2) - .setSuggestedValues(List(0, 1, 2)) + SelectionPropertyBuilder(PROPERTY_NAME_PUB_QOS, List(0, 1, 2), DEFAULT_PUB_QOS) .setDescription( fmt::format("MQTT Quality of Service level for subscribing. It can be 0 (at most once), 1 (at least once), or 2 " "(exactly once). By default it is set to {}.", From 2260aeb16589caa24a637b7b358875f8788f4ed8 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 14 Jan 2026 15:28:58 +0100 Subject: [PATCH 02/49] mqtt: componentStatus error for PublisherFB if connection lost --- .../include/mqtt_streaming_module/mqtt_root_fb_impl.h | 1 + mqtt_streaming_module/src/mqtt_root_fb_impl.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_root_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_root_fb_impl.h index cc4940a..2b07928 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_root_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_root_fb_impl.h @@ -71,6 +71,7 @@ class MqttRootFbImpl : public FunctionBlock std::future connectedFuture; std::atomic connectedDone{false}; std::unordered_map deviceMap; // device name -> signal list JSON + std::mutex componentStatusSync; }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp index acc63f0..81ac486 100644 --- a/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp @@ -54,7 +54,6 @@ void MqttRootFbImpl::removed() else { LOG_I("MQTT: disconnection was successful"); - connectionStatus.setStatus(ConnectionStatus::Disconnected); } } @@ -98,6 +97,8 @@ void MqttRootFbImpl::initMqttSubscriber() { connectionStatus.setStatus(ConnectionStatus::Connected); connectedPromise.set_value(true); + std::scoped_lock lock(componentStatusSync); + setComponentStatus(ComponentStatus::Ok); } }); @@ -111,6 +112,8 @@ void MqttRootFbImpl::initConnectionStatus() [this](std::string msg) { connectionStatus.setStatus(ConnectionStatus::Reconnecting, msg); + std::scoped_lock lock(componentStatusSync); + setComponentStatusWithMessage(ComponentStatus::Error, "Connection lost"); }); } @@ -170,6 +173,8 @@ bool MqttRootFbImpl::waitForConnection(const int timeoutMs) [this] { connectionStatus.setStatus(ConnectionStatus::Connected); + std::scoped_lock lock(componentStatusSync); + setComponentStatus(ComponentStatus::Ok); }); return res; } From 55dae62dea6573755ad9a0741ce08bd55971f043 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 14 Jan 2026 15:54:34 +0100 Subject: [PATCH 03/49] mqtt: warning status for a publisher if there are no connected input ports --- mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index c6b2f96..db84b1f 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -227,6 +227,10 @@ void MqttPublisherFbImpl::updateStatuses() { setComponentStatusWithMessage(ComponentStatus::Error, "Some property has wrong value!"); } + else if (signalContexts.size() == 1) // no one input port is connected + { + setComponentStatusWithMessage(ComponentStatus::Warning, "No input ports are connected!"); + } else if (skippedMsgCnt != 0) { setComponentStatusWithMessage(ComponentStatus::Warning, "Some messages were not published!"); From 3d599bda5e67f34a6c526401e922e715a20138ca Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 14 Jan 2026 16:43:05 +0100 Subject: [PATCH 04/49] mqtt: warning for a publisher FB if the topic property is empty --- .../mqtt_streaming_module/handler_factory.h | 4 +-- .../mqtt_publisher_fb_impl.h | 1 + .../include/mqtt_streaming_module/types.h | 4 +-- .../src/mqtt_publisher_fb_impl.cpp | 36 ++++++++++++------- .../tests/test_mqtt_publisher_fb.cpp | 2 +- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h b/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h index 8a3b276..49f7b51 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h @@ -34,14 +34,14 @@ class HandlerFactory return std::make_unique(config.useSignalNames, config.topicName.empty() ? publisherFbGlobalId : config.topicName); } - else if (config.topicMode == TopicMode::Single) + else if (config.topicMode == TopicMode::PerSignal) { if (config.groupValues) return std::make_unique(config.useSignalNames, config.groupValuesPackSize); else return std::make_unique(config.useSignalNames); } - else if (config.topicMode == TopicMode::Multi) + else if (config.topicMode == TopicMode::Single) { return std::make_unique(config.useSignalNames, config.topicName.empty() ? publisherFbGlobalId : config.topicName); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h index fd52186..a687dae 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h @@ -83,6 +83,7 @@ class MqttPublisherFbImpl final : public FunctionBlock std::atomic running; std::atomic hasSignalError; std::atomic hasSettingError; + std::atomic hasEmptyTopic; std::vector signalErrors; std::vector settingErrors; std::unique_ptr handler; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/types.h b/mqtt_streaming_module/include/mqtt_streaming_module/types.h index 0f758ff..9879c35 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/types.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/types.h @@ -12,8 +12,8 @@ using MqttDataSample = std::pair; using MqttData = std::vector; enum class TopicMode { - Single = 0, - Multi, + PerSignal = 0, + Single, _count }; diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index db84b1f..f693d1d 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -231,6 +231,10 @@ void MqttPublisherFbImpl::updateStatuses() { setComponentStatusWithMessage(ComponentStatus::Warning, "No input ports are connected!"); } + else if (hasEmptyTopic) + { + setComponentStatusWithMessage(ComponentStatus::Warning, "Topic property is empty! Using FB Global ID as topic name."); + } else if (skippedMsgCnt != 0) { setComponentStatusWithMessage(ComponentStatus::Warning, "Some messages were not published!"); @@ -295,12 +299,31 @@ void MqttPublisherFbImpl::readProperties() config.qos = readProperty(PROPERTY_NAME_PUB_QOS, DEFAULT_PUB_QOS); config.periodMs = readProperty(PROPERTY_NAME_PUB_READ_PERIOD, DEFAULT_PUB_READ_PERIOD); config.topicName = readProperty(PROPERTY_NAME_PUB_TOPIC_NAME, globalId.toStdString()); + + if (tmpTopicMode < static_cast(TopicMode::_count) && tmpTopicMode >= 0) + { + config.topicMode = static_cast(tmpTopicMode); + } + else + { + config.topicMode = TopicMode::PerSignal; + hasSettingError = true; + settingErrors.push_back("Topic mode has invalid value."); + } + if (config.topicName.empty()) + { config.topicName = globalId.toStdString(); + hasEmptyTopic = (config.topicMode == TopicMode::Single); + } + else + { + hasEmptyTopic = false; + } settingErrors.clear(); hasSettingError = false; - if (config.topicMode == TopicMode::Multi || config.sharedTs) + if (config.topicMode == TopicMode::Single || config.sharedTs) { auto result = mqtt::MqttDataWrapper::validateTopic(config.topicName, loggerComponent); hasSettingError = !result.success; @@ -318,17 +341,6 @@ void MqttPublisherFbImpl::readProperties() hasSettingError = true; settingErrors.push_back("Reader period must be non-negative."); } - if (tmpTopicMode < static_cast(TopicMode::_count) && tmpTopicMode >= 0) - { - config.topicMode = static_cast(tmpTopicMode); - } - else - { - config.topicMode = TopicMode::Single; - hasSettingError = true; - settingErrors.push_back("Topic mode has invalid value."); - } - } void MqttPublisherFbImpl::propertyChanged() diff --git a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp index c407bbd..184c62d 100644 --- a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp @@ -619,7 +619,7 @@ TEST_F(MqttPublisherFbTest, Config) } MqttPublisherFbImpl* ptr = reinterpret_cast(fb.getObject()); ASSERT_TRUE(ptr != nullptr); - EXPECT_EQ(ptr->getFbConfig().topicMode, TopicMode::Multi); + EXPECT_EQ(ptr->getFbConfig().topicMode, TopicMode::Single); EXPECT_TRUE(ptr->getFbConfig().sharedTs); EXPECT_TRUE(ptr->getFbConfig().groupValues); EXPECT_TRUE(ptr->getFbConfig().useSignalNames); From da6eff227d176768555131a683a96655fbed25f9 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 14 Jan 2026 18:09:28 +0100 Subject: [PATCH 05/49] mqtt: removing SignalName property from a Publisher FB --- .../include/mqtt_streaming_module/constants.h | 4 +-- .../mqtt_json_decoder_fb_impl.h | 1 - .../src/mqtt_json_decoder_fb_impl.cpp | 17 ++++------- .../src/mqtt_json_receiver_fb_impl.cpp | 1 - .../src/mqtt_raw_receiver_fb_impl.cpp | 2 +- .../tests/test_mqtt_json_decoder_fb.cpp | 14 +++------ .../tests/test_mqtt_json_fb.cpp | 30 +++++++++---------- .../tests/test_mqtt_publisher_fb.cpp | 20 ++++++++++++- 8 files changed, 46 insertions(+), 43 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index fb25ec7..662d998 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -19,7 +19,8 @@ static constexpr uint32_t DEFAULT_PUB_QOS = 1; static constexpr uint32_t DEFAULT_SUB_QOS = 1; static constexpr uint32_t DEFAULT_PUB_PACK_SIZE = 1; -static constexpr const char* DEFAULT_SIGNAL_NAME = "MQTTValueSignal"; +static constexpr const char* DEFAULT_VALUE_SIGNAL_LOCAL_ID = "MQTTValueSignal"; +static constexpr const char* DEFAULT_TS_SIGNAL_LOCAL_ID = "MQTTTimestampSignal"; static constexpr const char* PROPERTY_NAME_MQTT_BROKER_ADDRESS = "MQTTBrokerAddress"; static constexpr const char* PROPERTY_NAME_MQTT_BROKER_PORT = "MQTTBrokerPort"; @@ -33,7 +34,6 @@ static constexpr const char* PROPERTY_NAME_TOPIC = "Topic"; static constexpr const char* PROPERTY_NAME_VALUE_NAME = "ValueName"; static constexpr const char* PROPERTY_NAME_TS_NAME = "TimestampName"; static constexpr const char* PROPERTY_NAME_UNIT = "Unit"; -static constexpr const char* PROPERTY_NAME_SIGNAL_NAME = "SignalName"; static constexpr const char* PROPERTY_NAME_PUB_TOPIC_MODE = "TopicMode"; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h index acefcf2..492f099 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h @@ -47,7 +47,6 @@ class MqttJsonDecoderFbImpl final : public FunctionBlock std::string valueFieldName; std::string tsFieldName; std::string unitSymbol; - std::string signalName; }; struct ConfigStatus { bool configValid; diff --git a/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp index 0399a1c..e57fd19 100644 --- a/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp @@ -48,11 +48,6 @@ FunctionBlockTypePtr MqttJsonDecoderFbImpl::CreateType() defaultConfig.addProperty(builder.build()); } - { - auto builder = StringPropertyBuilder(PROPERTY_NAME_SIGNAL_NAME, String(DEFAULT_SIGNAL_NAME)).setDescription(""); - defaultConfig.addProperty(builder.build()); - } - { auto builder = StringPropertyBuilder(PROPERTY_NAME_UNIT, String("")).setDescription(""); defaultConfig.addProperty(builder.build()); @@ -106,9 +101,6 @@ void MqttJsonDecoderFbImpl::readProperties() } config.tsFieldName = readProperty(PROPERTY_NAME_TS_NAME, ""); config.unitSymbol = readProperty(PROPERTY_NAME_UNIT, ""); - config.signalName = readProperty(PROPERTY_NAME_SIGNAL_NAME, DEFAULT_SIGNAL_NAME); - if (config.signalName.empty()) - config.signalName = DEFAULT_SIGNAL_NAME; jsonDataWorker.setValueFieldName(config.valueFieldName); jsonDataWorker.setTimestampFieldName(config.tsFieldName); @@ -199,7 +191,8 @@ void MqttJsonDecoderFbImpl::createSignal() if (config.unitSymbol != "") dataDescBdr.setUnit(Unit(config.unitSymbol)); - outputSignal = createAndAddSignal(config.signalName, dataDescBdr.build()); + outputSignal = createAndAddSignal(DEFAULT_VALUE_SIGNAL_LOCAL_ID, dataDescBdr.build()); + outputSignal.setName(config.valueFieldName); if (config.tsFieldName != "") { outputSignal.setDomainSignal(createDomainSignal()); @@ -211,9 +204,9 @@ void MqttJsonDecoderFbImpl::createSignal() void MqttJsonDecoderFbImpl::reconfigureSignal(const FbConfig& prevConfig) { auto lock = this->getRecursiveConfigLock(); - if (prevConfig.signalName != config.signalName) + if (prevConfig.valueFieldName != config.valueFieldName) { - outputSignal.setName(config.signalName); + outputSignal.setName(config.valueFieldName); } if (prevConfig.valueFieldName != config.valueFieldName || prevConfig.unitSymbol != config.unitSymbol) @@ -260,7 +253,7 @@ SignalConfigPtr MqttJsonDecoderFbImpl::createDomainSignal() .setOrigin(getEpoch()) .setName("Time") .build(); - outputDomainSignal = createAndAddSignal("mqttTimestampSignal", domainSignalDsc, false); + outputDomainSignal = createAndAddSignal(DEFAULT_TS_SIGNAL_LOCAL_ID, domainSignalDsc, false); return outputDomainSignal; } diff --git a/mqtt_streaming_module/src/mqtt_json_receiver_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_json_receiver_fb_impl.cpp index d8cfb28..a6bca9e 100644 --- a/mqtt_streaming_module/src/mqtt_json_receiver_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_json_receiver_fb_impl.cpp @@ -232,7 +232,6 @@ void MqttJsonReceiverFbImpl::setJsonConfig(const std::string config) LOG_I("Creating a decoder FB for the signal \"{}\":", signalName); fbConfig.setPropertyValue(PROPERTY_NAME_VALUE_NAME, descriptor.valueFieldName); fbConfig.setPropertyValue(PROPERTY_NAME_TS_NAME, descriptor.tsFieldName); - fbConfig.setPropertyValue(PROPERTY_NAME_SIGNAL_NAME, signalName); if (descriptor.unit.assigned()) fbConfig.setPropertyValue(PROPERTY_NAME_UNIT, descriptor.unit.getSymbol()); MqttJsonReceiverFbImpl::onAddFunctionBlock(JSON_DECODER_FB_NAME, fbConfig); diff --git a/mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp index f4e657f..ab51851 100644 --- a/mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp @@ -145,7 +145,7 @@ void MqttRawReceiverFbImpl::createSignals() { auto lock = std::lock_guard(sync); const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::Binary).build(); - outputSignal = createAndAddSignal(DEFAULT_SIGNAL_NAME, signalDsc); + outputSignal = createAndAddSignal(DEFAULT_VALUE_SIGNAL_LOCAL_ID, signalDsc); } std::string MqttRawReceiverFbImpl::getSubscribedTopic() const diff --git a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp index 290a73b..052af22 100644 --- a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp @@ -66,20 +66,19 @@ class MqttJsonDecoderFbHelper : public DaqTestHelper jsonMqttFb = new MqttJsonReceiverFbImpl(NullContext(), nullptr, fbType, nullptr, config); } - void CreateDecoderFB(std::string topic, std::string valueF, std::string tsF, std::string sigName = "", std::string unitSymbol = "") + void CreateDecoderFB(std::string topic, std::string valueF, std::string tsF, std::string unitSymbol = "") { CreateJsonFb(topic); - AddDecoderFb(valueF, tsF, sigName, unitSymbol); + AddDecoderFb(valueF, tsF, unitSymbol); } - daq::FunctionBlockPtr AddDecoderFb(std::string valueF, std::string tsF, std::string sigName = "", std::string unitSymbol = "") + daq::FunctionBlockPtr AddDecoderFb(std::string valueF, std::string tsF, std::string unitSymbol = "") { daq::StringPtr typeId = daq::String(JSON_DECODER_FB_NAME); auto config = jsonMqttFb.getAvailableFunctionBlockTypes().get(JSON_DECODER_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_VALUE_NAME, valueF); config.setPropertyValue(PROPERTY_NAME_TS_NAME, tsF); - config.setPropertyValue(PROPERTY_NAME_SIGNAL_NAME, sigName); config.setPropertyValue(PROPERTY_NAME_UNIT, unitSymbol); decoderObj = jsonMqttFb.addFunctionBlock(typeId, config); return decoderObj; @@ -466,7 +465,7 @@ TEST_F(MqttJsonDecoderFbTest, DefaultConfig) ASSERT_TRUE(defaultConfig.assigned()); - ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 4u); + ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 3u); ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_VALUE_NAME)); ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_VALUE_NAME).getValueType(), CoreType::ctString); @@ -476,10 +475,6 @@ TEST_F(MqttJsonDecoderFbTest, DefaultConfig) ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_TS_NAME).getValueType(), CoreType::ctString); ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_TS_NAME).asPtr().getLength(), 0u); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SIGNAL_NAME)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SIGNAL_NAME).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_SIGNAL_NAME).asPtr().toStdString(), std::string(DEFAULT_SIGNAL_NAME)); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_UNIT)); ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_UNIT).getValueType(), CoreType::ctString); ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_UNIT).asPtr().getLength(), 0u); @@ -493,7 +488,6 @@ TEST_F(MqttJsonDecoderFbTest, Config) config.setPropertyValue(PROPERTY_NAME_VALUE_NAME, "value"); config.setPropertyValue(PROPERTY_NAME_TS_NAME, "timestamp"); - config.setPropertyValue(PROPERTY_NAME_SIGNAL_NAME, "signalName_0"); config.setPropertyValue(PROPERTY_NAME_UNIT, "ppm"); daq::FunctionBlockPtr fb; ASSERT_NO_THROW(fb = jsonMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); diff --git a/mqtt_streaming_module/tests/test_mqtt_json_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_json_fb.cpp index 817be45..8caa9a9 100644 --- a/mqtt_streaming_module/tests/test_mqtt_json_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_json_fb.cpp @@ -261,9 +261,9 @@ TEST_F(MqttJsonFbTest, JsonInit0) ASSERT_EQ(jsonFb.getFunctionBlocks().getCount(), 3u); ASSERT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - auto lambda = [&](FunctionBlockPtr nestedFb, std::string name, std::string value, std::string ts, std::string symbol) + auto lambda = [&](FunctionBlockPtr nestedFb, std::string value, std::string ts, std::string symbol) { - EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), name); + EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); if (!symbol.empty()) EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); @@ -271,9 +271,9 @@ TEST_F(MqttJsonFbTest, JsonInit0) }; EXPECT_EQ(jsonFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "openDAQ/RefDev0/IO/AI/RefCh0/Sig/AI0"); - lambda(jsonFb.getFunctionBlocks()[0], "AI0", "value", "timestamp", "V"); - lambda(jsonFb.getFunctionBlocks()[1], "AI1", "value1", "", ""); - lambda(jsonFb.getFunctionBlocks()[2], "AI2", "value2", "", "W"); + lambda(jsonFb.getFunctionBlocks()[0], "value", "timestamp", "V"); + lambda(jsonFb.getFunctionBlocks()[1], "value1", "", ""); + lambda(jsonFb.getFunctionBlocks()[2], "value2", "", "W"); } @@ -287,9 +287,9 @@ TEST_F(MqttJsonFbTest, JsonInit1) ASSERT_EQ(jsonFb.getFunctionBlocks().getCount(), 3u); ASSERT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - auto lambda = [&](FunctionBlockPtr nestedFb, std::string name, std::string value, std::string ts, std::string symbol) + auto lambda = [&](FunctionBlockPtr nestedFb, std::string value, std::string ts, std::string symbol) { - EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), name); + EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); if (!symbol.empty()) EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); @@ -297,9 +297,9 @@ TEST_F(MqttJsonFbTest, JsonInit1) }; EXPECT_EQ(jsonFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "/mirip/UNet3AC2/sensor/data"); - lambda(jsonFb.getFunctionBlocks()[0], "temp", "temp", "ts", "°C"); - lambda(jsonFb.getFunctionBlocks()[1], "humidity", "humi", "ts", "%"); - lambda(jsonFb.getFunctionBlocks()[2], "tds", "tds_value", "ts", "ppm"); + lambda(jsonFb.getFunctionBlocks()[0], "temp", "ts", "°C"); + lambda(jsonFb.getFunctionBlocks()[1], "humi", "ts", "%"); + lambda(jsonFb.getFunctionBlocks()[2], "tds_value", "ts", "ppm"); } @@ -356,9 +356,9 @@ TEST_F(MqttJsonFbTest, JsonInitFromFileWithChecking) ASSERT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); ASSERT_EQ(jsonFb.getFunctionBlocks().getCount(), 3u); - auto lambda = [&](FunctionBlockPtr nestedFb, std::string name, std::string value, std::string ts, std::string symbol) + auto lambda = [&](FunctionBlockPtr nestedFb, std::string value, std::string ts, std::string symbol) { - EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), name); + EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); if (!symbol.empty()) EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); @@ -366,9 +366,9 @@ TEST_F(MqttJsonFbTest, JsonInitFromFileWithChecking) }; EXPECT_EQ(jsonFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "/mirip/UNet3AC2/sensor/data"); - lambda(jsonFb.getFunctionBlocks()[0], "temp", "temp", "ts", "°C"); - lambda(jsonFb.getFunctionBlocks()[1], "humidity", "humi", "ts", "%"); - lambda(jsonFb.getFunctionBlocks()[2], "tds", "tds_value", "ts", "ppm"); + lambda(jsonFb.getFunctionBlocks()[0], "temp", "ts", "°C"); + lambda(jsonFb.getFunctionBlocks()[1], "humi", "ts", "%"); + lambda(jsonFb.getFunctionBlocks()[2], "tds_value", "ts", "ppm"); } diff --git a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp index 184c62d..d1c9e44 100644 --- a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp @@ -602,6 +602,10 @@ TEST_F(MqttPublisherFbTest, Config) config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME, buildTopicName()); daq::FunctionBlockPtr fb; ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Warning", daqInstance.getContext().getTypeManager())); + SignalHelper helper; + fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SET_STATUS_NAME), @@ -633,6 +637,8 @@ TEST_F(MqttPublisherFbTest, Creation) StartUp(); daq::FunctionBlockPtr fb; ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME)); + SignalHelper helper; + fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SET_STATUS_NAME), @@ -644,9 +650,11 @@ TEST_F(MqttPublisherFbTest, Creation) TEST_F(MqttPublisherFbTest, TwoFbCreation) { StartUp(); + SignalHelper helper; { daq::FunctionBlockPtr fb; ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME)); + fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SET_STATUS_NAME), @@ -657,6 +665,7 @@ TEST_F(MqttPublisherFbTest, TwoFbCreation) { daq::FunctionBlockPtr fb; ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME)); + fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SET_STATUS_NAME), @@ -675,6 +684,10 @@ TEST_F(MqttPublisherFbTest, CreationWithDefaultConfig) ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME)); auto signals = fb.getSignals(); ASSERT_EQ(signals.getCount(), 0u); + ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Warning", daqInstance.getContext().getTypeManager())); + SignalHelper helper; + fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SET_STATUS_NAME), @@ -690,6 +703,8 @@ TEST_F(MqttPublisherFbTest, CreationWithPartialConfig) auto config = PropertyObject(); config.addProperty(BoolProperty(PROPERTY_NAME_PUB_USE_SIGNAL_NAMES, True)); ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + SignalHelper helper; + fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SET_STATUS_NAME), @@ -720,6 +735,7 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) daqInstance.getContext().getTypeManager()); const auto comStOk = Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager()); const auto comStError = Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager()); + const auto comStWarn = Enumeration("ComponentStatusType", "Warning", daqInstance.getContext().getTypeManager()); { daq::FunctionBlockPtr fb; @@ -753,7 +769,7 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStOk); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStValid); fb.getInputPorts()[0].disconnect(); - ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStOk); + ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStWarn); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStNotConnected); } @@ -829,6 +845,8 @@ TEST_F(MqttPublisherFbTest, WrongConfig) daqInstance.getContext().getTypeManager())); fb.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, False); + SignalHelper helper; + fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SET_STATUS_NAME), From 835c248abbe9918a1928a85fdbe302cad80bafe6 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Thu, 15 Jan 2026 13:04:07 +0100 Subject: [PATCH 06/49] mqtt: merging two FB - Raw and JSON to MQTTSubscriberFb --- .../custom-mqtt-sub/src/custom-mqtt-sub.cpp | 20 +- examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp | 18 +- .../include/mqtt_streaming_module/constants.h | 6 +- .../mqtt_streaming_module/mqtt_base_fb.h | 86 --- .../mqtt_raw_receiver_fb_impl.h | 56 -- ...er_fb_impl.h => mqtt_subscriber_fb_impl.h} | 59 +- mqtt_streaming_module/src/CMakeLists.txt | 18 +- mqtt_streaming_module/src/mqtt_base_fb.cpp | 148 ----- .../src/mqtt_raw_receiver_fb_impl.cpp | 161 ----- .../src/mqtt_root_fb_impl.cpp | 20 +- .../src/mqtt_streaming_module_impl.cpp | 2 - ...b_impl.cpp => mqtt_subscriber_fb_impl.cpp} | 198 ++++-- mqtt_streaming_module/tests/CMakeLists.txt | 3 +- .../tests/test_daq_test_helper.h | 10 +- .../tests/test_mqtt_json_decoder_fb.cpp | 62 +- .../tests/test_mqtt_json_fb.cpp | 386 ------------ .../tests/test_mqtt_raw_fb.cpp | 392 ------------ .../tests/test_mqtt_root_fb.cpp | 5 +- .../tests/test_mqtt_subscriber_fb.cpp | 591 ++++++++++++++++++ 19 files changed, 865 insertions(+), 1376 deletions(-) delete mode 100644 mqtt_streaming_module/include/mqtt_streaming_module/mqtt_base_fb.h delete mode 100644 mqtt_streaming_module/include/mqtt_streaming_module/mqtt_raw_receiver_fb_impl.h rename mqtt_streaming_module/include/mqtt_streaming_module/{mqtt_json_receiver_fb_impl.h => mqtt_subscriber_fb_impl.h} (59%) delete mode 100644 mqtt_streaming_module/src/mqtt_base_fb.cpp delete mode 100644 mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp rename mqtt_streaming_module/src/{mqtt_json_receiver_fb_impl.cpp => mqtt_subscriber_fb_impl.cpp} (58%) delete mode 100644 mqtt_streaming_module/tests/test_mqtt_json_fb.cpp delete mode 100644 mqtt_streaming_module/tests/test_mqtt_raw_fb.cpp create mode 100644 mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp diff --git a/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp b/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp index 19dd00c..b98bb44 100644 --- a/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp +++ b/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp @@ -123,24 +123,24 @@ int main(int argc, char* argv[]) // Create OpenDAQ instance and add MQTT broker FB const InstancePtr instance = InstanceBuilder().addModulePath(MODULE_PATH).build(); - const std::string rootFbName = "MQTTClientFB"; - auto rootFbConfig = instance.getAvailableFunctionBlockTypes().get(rootFbName).createDefaultConfig(); - rootFbConfig.setPropertyValue("MQTTBrokerAddress", appConfig.brokerAddress); - auto brokerFB = instance.addFunctionBlock(rootFbName, rootFbConfig); + const std::string clientFbName = "MQTTClientFB"; + auto clientFbConfig = instance.getAvailableFunctionBlockTypes().get(clientFbName).createDefaultConfig(); + clientFbConfig.setPropertyValue("MQTTBrokerAddress", appConfig.brokerAddress); + auto brokerFB = instance.addFunctionBlock(clientFbName, clientFbConfig); auto availableFbs = brokerFB.getAvailableFunctionBlockTypes(); - const std::string jsonFbName = "JSONSubscriberMQTTFB"; - std::cout << "Try to add the " << jsonFbName << std::endl; + const std::string subFbName = "MQTTSubscriberFB"; + std::cout << "Try to add the " << subFbName << std::endl; - auto config = availableFbs.get(jsonFbName).createDefaultConfig(); + auto config = availableFbs.get(subFbName).createDefaultConfig(); config.setPropertyValue("JSONConfigFile", appConfig.configFilePath); - // Add the JSON function block to the broker FB - daq::FunctionBlockPtr jsonFb = brokerFB.addFunctionBlock(jsonFbName, config); + // Add the subscriber function block to the broker FB + daq::FunctionBlockPtr subFb = brokerFB.addFunctionBlock(subFbName, config); // Create packet readers for all signals auto signals = List(); - const auto fbs = jsonFb.getFunctionBlocks(); + const auto fbs = subFb.getFunctionBlocks(); for (const auto& fb : fbs) { const auto sig = fb.getSignals(); diff --git a/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp b/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp index 28160a6..d7452b9 100644 --- a/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp +++ b/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp @@ -55,24 +55,24 @@ int main(int argc, char* argv[]) // Create OpenDAQ instance and add MQTT broker FB const InstancePtr instance = InstanceBuilder().addModulePath(MODULE_PATH).build(); - const std::string rootFbName = "MQTTClientFB"; - auto rootFbConfig = instance.getAvailableFunctionBlockTypes().get(rootFbName).createDefaultConfig(); - rootFbConfig.setPropertyValue("MQTTBrokerAddress", appConfig.brokerAddress); - auto brokerFB = instance.addFunctionBlock(rootFbName, rootFbConfig); + const std::string clientFbName = "MQTTClientFB"; + auto clientFbConfig = instance.getAvailableFunctionBlockTypes().get(clientFbName).createDefaultConfig(); + clientFbConfig.setPropertyValue("MQTTBrokerAddress", appConfig.brokerAddress); + auto brokerFB = instance.addFunctionBlock(clientFbName, clientFbConfig); auto availableFbs = brokerFB.getAvailableFunctionBlockTypes(); - const std::string fbName = "RawSubscriberMQTTFB"; + const std::string fbName = "MQTTSubscriberFB"; std::cout << "Try to add the " << fbName << std::endl; - // Create RAW function block configuration + // Create subscriber function block configuration auto config = availableFbs.get(fbName).createDefaultConfig(); config.setPropertyValue("Topic", appConfig.topic); - // Add the RAW function block to the broker FB - daq::FunctionBlockPtr rawFb = brokerFB.addFunctionBlock(fbName, config); + // Add the subscriber function block to the broker FB + daq::FunctionBlockPtr subFb = brokerFB.addFunctionBlock(fbName, config); // Create packet readers for a signal - const auto signal = rawFb.getSignals()[0]; + const auto signal = subFb.getSignals()[0]; PacketReaderPtr reader = daq::PacketReader(signal); // Start a thread to read packets from the reader diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index 662d998..0a4b34f 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -46,16 +46,14 @@ static constexpr const char* PROPERTY_NAME_PUB_QOS = "QoS"; static constexpr const char* PROPERTY_NAME_SUB_QOS = "QoS"; static constexpr const char* PROPERTY_NAME_PUB_READ_PERIOD = "ReaderPeriod"; -static constexpr const char* RAW_FB_NAME = "RawSubscriberMQTTFB"; -static constexpr const char* JSON_FB_NAME = "JSONSubscriberMQTTFB"; +static constexpr const char* SUB_FB_NAME = "MQTTSubscriberFB"; static constexpr const char* PUB_FB_NAME = "PublisherMQTTFB"; static constexpr const char* ROOT_FB_NAME = "MQTTClientFB"; static constexpr const char* JSON_DECODER_FB_NAME = "JSONDecoderMQTTTFB"; static const char* MQTT_LOCAL_ROOT_FB_ID_PREFIX = "MQTTClientFB"; static const char* MQTT_LOCAL_PUB_FB_ID_PREFIX = "PublisherMQTTFB"; -static const char* MQTT_LOCAL_RAW_FB_ID_PREFIX = "RawSubscriberMQTTFB"; -static const char* MQTT_LOCAL_JSON_FB_ID_PREFIX = "JSONSubscriberMQTTFB"; +static const char* MQTT_LOCAL_SUB_FB_ID_PREFIX = "MQTTSubscriberFB"; static const char* MQTT_LOCAL_JSON_DECODER_FB_ID_PREFIX = "JSONDecoderMQTTTFB"; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_base_fb.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_base_fb.h deleted file mode 100644 index 25cf036..0000000 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_base_fb.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2022-2025 openDAQ d.o.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include -#include - -#include "MqttAsyncClient.h" -#include "mqtt_streaming_module/constants.h" -#include "mqtt_streaming_module/status_helper.h" - -BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE - -class MqttBaseFb : public FunctionBlock -{ -public: - enum class SubscriptionStatus : EnumType - { - InvalidTopicName = 0, - SubscribingError, - WaitingForData, - HasData - }; - - struct CmdResult - { - bool success = false; - std::string msg; - int token = 0; - - CmdResult(bool success = false, const std::string& msg = "", int token = 0) - : success(success), - msg(msg), - token(token) - { - } - }; - - - explicit DAQ_MQTT_STREAM_MODULE_API MqttBaseFb(const ContextPtr& ctx, - const ComponentPtr& parent, - const FunctionBlockTypePtr& type, - const StringPtr& localId, - std::shared_ptr subscriber, - const PropertyObjectPtr& config = nullptr); - virtual ~MqttBaseFb() = default; - - virtual std::string getSubscribedTopic() const = 0; - -protected: - static std::vector> subscriptionStatusMap; - - std::shared_ptr subscriber; - StatusHelper subscriptionStatus; - int qos = DEFAULT_SUB_QOS; - - virtual void processMessage(const mqtt::MqttMessage& msg) = 0; - - void initProperties(const PropertyObjectPtr& config); - virtual void readProperties() = 0; - - DAQ_MQTT_STREAM_MODULE_API void onSignalsMessage(const mqtt::MqttAsyncClient& subscriber, const mqtt::MqttMessage& msg); - - virtual void clearSubscribedTopic() = 0; - CmdResult subscribeToTopic(); - CmdResult unsubscribeFromTopic(); - - virtual void propertyChanged() = 0; - - void removed() override; -}; - -END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_raw_receiver_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_raw_receiver_fb_impl.h deleted file mode 100644 index 4ed7b91..0000000 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_raw_receiver_fb_impl.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2022-2025 openDAQ d.o.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include -#include -#include -#include - -BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE - -class MqttRawReceiverFbImpl final : public MqttBaseFb -{ - friend class MqttRawFbTest; - -public: - explicit DAQ_MQTT_STREAM_MODULE_API MqttRawReceiverFbImpl(const ContextPtr& ctx, - const ComponentPtr& parent, - const FunctionBlockTypePtr& type, - std::shared_ptr subscriber, - const PropertyObjectPtr& config = nullptr); - DAQ_MQTT_STREAM_MODULE_API ~MqttRawReceiverFbImpl() override; - - static FunctionBlockTypePtr CreateType(); - - std::string getSubscribedTopic() const override; -private: - std::mutex sync; - SignalConfigPtr outputSignal; - std::string topicForSubscribing; - static std::atomic localIndex; - - static std::string generateLocalId(); - - void createSignals(); - void clearSubscribedTopic() override; - - void processMessage(const mqtt::MqttMessage& msg) override; - void readProperties() override; - void propertyChanged() override; -}; - -END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_receiver_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h similarity index 59% rename from mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_receiver_fb_impl.h rename to mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h index 114ea32..55ec4fa 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_receiver_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h @@ -18,54 +18,87 @@ #include "MqttAsyncClient.h" #include "MqttDataWrapper.h" #include -#include +#include +#include "mqtt_streaming_module/constants.h" +#include "mqtt_streaming_module/status_helper.h" #include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -class MqttJsonReceiverFbImpl final : public MqttBaseFb +class MqttSubscriberFbImpl final : public FunctionBlock { - friend class MqttJsonFbHelper; + friend class MqttSubscriberFbHelper; friend class MqttJsonDecoderFbHelper; public: - explicit DAQ_MQTT_STREAM_MODULE_API MqttJsonReceiverFbImpl(const ContextPtr& ctx, + enum class SubscriptionStatus : EnumType + { + InvalidTopicName = 0, + SubscribingError, + WaitingForData, + HasData + }; + + struct CmdResult + { + bool success = false; + std::string msg; + int token = 0; + + CmdResult(bool success = false, const std::string& msg = "", int token = 0) + : success(success), + msg(msg), + token(token) + { + } + }; + + explicit DAQ_MQTT_STREAM_MODULE_API MqttSubscriberFbImpl(const ContextPtr& ctx, const ComponentPtr& parent, const FunctionBlockTypePtr& type, std::shared_ptr subscriber, const PropertyObjectPtr& config = nullptr); - DAQ_MQTT_STREAM_MODULE_API ~MqttJsonReceiverFbImpl() override; + DAQ_MQTT_STREAM_MODULE_API ~MqttSubscriberFbImpl() override; DAQ_MQTT_STREAM_MODULE_API static FunctionBlockTypePtr CreateType(); - DAQ_MQTT_STREAM_MODULE_API std::string getSubscribedTopic() const override; + DAQ_MQTT_STREAM_MODULE_API std::string getSubscribedTopic() const; protected: - mqtt::MqttDataWrapper jsonDataWorker; - std::unordered_map outputSignals; - std::string topicForSubscribing; + static std::vector> subscriptionStatusMap; static std::atomic localIndex; + std::shared_ptr subscriber; + StatusHelper subscriptionStatus; + int qos = DEFAULT_SUB_QOS; + mqtt::MqttDataWrapper jsonDataWorker; + std::string topicForSubscribing; DictObjectPtr nestedFbTypes; std::vector nestedFunctionBlocks; + SignalConfigPtr outputSignal; + + DAQ_MQTT_STREAM_MODULE_API void onSignalsMessage(const mqtt::MqttAsyncClient& subscriber, const mqtt::MqttMessage& msg); static std::string generateLocalId(); void initNestedFbTypes(); void createSignals(); - void clearSubscribedTopic() override; + void clearSubscribedTopic(); - void processMessage(const mqtt::MqttMessage& msg) override; + void processMessage(const mqtt::MqttMessage& msg); void initProperties(const PropertyObjectPtr& config); - void readProperties() override; + void readProperties(); void readJsonConfig(); std::pair readFileToString(const std::string& filePath); void setJsonConfig(const std::string config); - void propertyChanged() override; + void propertyChanged(); bool setTopic(std::string topic); + CmdResult subscribeToTopic(); + CmdResult unsubscribeFromTopic(); + void removed() override; DictPtr onGetAvailableFunctionBlockTypes() override; FunctionBlockPtr onAddFunctionBlock(const StringPtr& typeId, const PropertyObjectPtr& config) override; void onRemoveFunctionBlock(const FunctionBlockPtr& functionBlock) override; diff --git a/mqtt_streaming_module/src/CMakeLists.txt b/mqtt_streaming_module/src/CMakeLists.txt index 136ccd1..ed3d39f 100644 --- a/mqtt_streaming_module/src/CMakeLists.txt +++ b/mqtt_streaming_module/src/CMakeLists.txt @@ -5,10 +5,8 @@ set(SRC_Include common.h module_dll.h mqtt_streaming_module_impl.h mqtt_root_fb_impl.h - mqtt_base_fb.h - mqtt_json_receiver_fb_impl.h mqtt_json_decoder_fb_impl.h - mqtt_raw_receiver_fb_impl.h + mqtt_subscriber_fb_impl.h mqtt_publisher_fb_impl.h constants.h helper.h @@ -25,10 +23,8 @@ set(SRC_Include common.h set(SRC_Srcs module_dll.cpp mqtt_streaming_module_impl.cpp mqtt_root_fb_impl.cpp - mqtt_base_fb.cpp - mqtt_json_receiver_fb_impl.cpp mqtt_json_decoder_fb_impl.cpp - mqtt_raw_receiver_fb_impl.cpp + mqtt_subscriber_fb_impl.cpp mqtt_publisher_fb_impl.cpp helper.cpp atomic_signal_atomic_sample_handler.cpp @@ -53,16 +49,12 @@ source_group("module" FILES ${MODULE_HEADERS_DIR}/mqtt_streaming_module_impl.h mqtt_streaming_module_impl.cpp ) -source_group("functionalBlock" FILES ${MODULE_HEADERS_DIR}/mqtt_json_receiver_fb_impl.h - mqtt_json_receiver_fb_impl.cpp - ${MODULE_HEADERS_DIR}/mqtt_json_decoder_fb_impl.h +source_group("functionalBlock" FILES ${MODULE_HEADERS_DIR}/mqtt_json_decoder_fb_impl.h mqtt_json_decoder_fb_impl.cpp - ${MODULE_HEADERS_DIR}/mqtt_raw_receiver_fb_impl.h - mqtt_raw_receiver_fb_impl.cpp + ${MODULE_HEADERS_DIR}/mqtt_subscriber_fb_impl.h + mqtt_subscriber_fb_impl.cpp ${MODULE_HEADERS_DIR}/mqtt_publisher_fb_impl.h mqtt_publisher_fb_impl.cpp - ${MODULE_HEADERS_DIR}/mqtt_base_fb.h - mqtt_base_fb.cpp ${MODULE_HEADERS_DIR}/mqtt_root_fb_impl.h mqtt_root_fb_impl.cpp ) diff --git a/mqtt_streaming_module/src/mqtt_base_fb.cpp b/mqtt_streaming_module/src/mqtt_base_fb.cpp deleted file mode 100644 index d735c17..0000000 --- a/mqtt_streaming_module/src/mqtt_base_fb.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include "mqtt_streaming_module/constants.h" -#include - -BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE - -constexpr int MQTT_FB_UNSUBSCRIBE_TOUT = 3000; - -std::vector> MqttBaseFb::subscriptionStatusMap = - {{SubscriptionStatus::InvalidTopicName, "InvalidTopicName"}, - {SubscriptionStatus::SubscribingError, "SubscribingError"}, - {SubscriptionStatus::WaitingForData, "WaitingForData"}, - {SubscriptionStatus::HasData, "HasData"}}; - -MqttBaseFb::MqttBaseFb(const ContextPtr& ctx, - const ComponentPtr& parent, - const FunctionBlockTypePtr& type, - const StringPtr& localId, - std::shared_ptr subscriber, - const PropertyObjectPtr& config) - : FunctionBlock(type, ctx, parent, localId), - subscriber(subscriber), - subscriptionStatus(MQTT_FB_SUB_STATUS_TYPE, - MQTT_FB_SUB_STATUS_NAME, - statusContainer, - subscriptionStatusMap, - SubscriptionStatus::InvalidTopicName, - context.getTypeManager()) -{ - initComponentStatus(); -} - -void MqttBaseFb::removed() -{ - FunctionBlock::removed(); - unsubscribeFromTopic(); -} - -void MqttBaseFb::onSignalsMessage(const mqtt::MqttAsyncClient& subscriber, const mqtt::MqttMessage& msg) -{ - processMessage(msg); -} - -void MqttBaseFb::initProperties(const PropertyObjectPtr& config) -{ - for (const auto& prop : config.getAllProperties()) - { - const auto propName = prop.getName(); - if (!objPtr.hasProperty(propName)) - { - if (const auto internalProp = prop.asPtrOrNull(true); internalProp.assigned()) - { - objPtr.addProperty(internalProp.clone()); - objPtr.setPropertyValue(propName, prop.getValue()); - objPtr.getOnPropertyValueWrite(prop.getName()) += - [this](PropertyObjectPtr& obj, PropertyValueEventArgsPtr& args) { propertyChanged(); }; - } - } - else - { - objPtr.setPropertyValue(propName, prop.getValue()); - } - } - readProperties(); -} - -MqttBaseFb::CmdResult MqttBaseFb::subscribeToTopic() -{ - MqttBaseFb::CmdResult result{false}; - if (subscriber) - { - auto lambda = [this](const mqtt::MqttAsyncClient &client, mqtt::MqttMessage &msg){this->onSignalsMessage(client, msg);}; - const auto topic = getSubscribedTopic(); - if (!topic.empty()) - { - LOG_I("Trying to subscribe to the topic : {}", topic); - subscriber->setMessageArrivedCb(topic, lambda); - if (auto subRes = subscriber->subscribe(topic, qos); subRes.success == false) - { - LOG_W("Failed to subscribe to the topic: \"{}\"; reason: {}", topic, subRes.msg); - setComponentStatusWithMessage(ComponentStatus::Warning, "Some topics failed to subscribe!"); - subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, "The reason: " + subRes.msg); - result = {false, "Failed to subscribe to the topic: \"" + topic + "\"; reason: " + subRes.msg}; - } - else - { - // subscriber->subscribe(...) is asynchronous. It puts command in queue and returns immediately. - LOG_D("Trying to subscribe to the topic: {}", topic); - setComponentStatus(ComponentStatus::Ok); - subscriptionStatus.setStatus(SubscriptionStatus::WaitingForData, "Topic: " + topic); - result = {true, "", result.token}; - } - } - else - { - result = {false, "Couldn't subscribe to an empty topic"}; - LOG_W("{}", result.msg); - } - } - else - { - const std::string msg = "MQTT subscriber client is not set!"; - setComponentStatusWithMessage(ComponentStatus::Error, msg); - subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, msg); - result = {false, msg}; - } - return result; -} - -MqttBaseFb::CmdResult MqttBaseFb::unsubscribeFromTopic() -{ - MqttBaseFb::CmdResult result{true}; - if (subscriber) - { - const auto topic = getSubscribedTopic(); - if (!topic.empty()) - { - subscriber->setMessageArrivedCb(topic, nullptr); - mqtt::CmdResult unsubRes = subscriber->unsubscribe(topic); - if (unsubRes.success) - unsubRes = subscriber->waitForCompletion(unsubRes.token, MQTT_FB_UNSUBSCRIBE_TOUT); - - if (unsubRes.success) - { - clearSubscribedTopic(); - LOG_I("The topic \'{}\' has been unsubscribed successfully", topic); - result = {true}; - } - else - { - const auto msg = fmt::format("Failed to unsubscribe from the topic \'{}\'; reason: {}", topic, unsubRes.msg); - LOG_W("{}", msg); - setComponentStatus(ComponentStatus::Warning); - subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, msg); - result = {false, msg}; - } - } - } - else - { - const std::string msg = "MQTT subscriber client is not set!"; - setComponentStatusWithMessage(ComponentStatus::Error, msg); - subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, msg); - result = {false, msg}; - } - return result; -} - -END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp deleted file mode 100644 index ab51851..0000000 --- a/mqtt_streaming_module/src/mqtt_raw_receiver_fb_impl.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "MqttDataWrapper.h" -#include "mqtt_streaming_module/constants.h" -#include "mqtt_streaming_module/helper.h" -#include -#include -#include - -BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE - -constexpr int MQTT_RAW_FB_UNSUBSCRIBE_TOUT = 3000; - -std::atomic MqttRawReceiverFbImpl::localIndex = 0; - -MqttRawReceiverFbImpl::MqttRawReceiverFbImpl(const ContextPtr& ctx, - const ComponentPtr& parent, - const FunctionBlockTypePtr& type, - std::shared_ptr subscriber, - const PropertyObjectPtr& config) - : MqttBaseFb(ctx, parent, type, generateLocalId(), subscriber, config) -{ - if (config.assigned()) - initProperties(populateDefaultConfig(type.createDefaultConfig(), config)); - else - initProperties(type.createDefaultConfig()); - - createSignals(); - subscribeToTopic(); -} - -MqttRawReceiverFbImpl::~MqttRawReceiverFbImpl() -{ - unsubscribeFromTopic(); -} - -FunctionBlockTypePtr MqttRawReceiverFbImpl::CreateType() -{ - auto defaultConfig = PropertyObject(); - { - auto builder = - SelectionPropertyBuilder(PROPERTY_NAME_PUB_QOS, List(0, 1, 2), DEFAULT_PUB_QOS) - .setDescription( - fmt::format("MQTT Quality of Service level for subscribing. It can be 0 (at most once), 1 (at least once), or 2 " - "(exactly once). By default it is set to {}.", - DEFAULT_SUB_QOS)); - defaultConfig.addProperty(builder.build()); - } - { - auto builder = - StringPropertyBuilder(PROPERTY_NAME_TOPIC, "").setDescription("An MQTT topic to subscribe to for receiving raw binary data."); - defaultConfig.addProperty(builder.build()); - } - const auto fbType = - FunctionBlockType(RAW_FB_NAME, - RAW_FB_NAME, - "The raw MQTT function block allows subscribing to an MQTT topic and converting MQTT payloads into " - "openDAQ signal binary data samples.", - defaultConfig); - return fbType; -} - -std::string MqttRawReceiverFbImpl::generateLocalId() -{ - return std::string(MQTT_LOCAL_RAW_FB_ID_PREFIX + std::to_string(localIndex++)); -} - -void MqttRawReceiverFbImpl::readProperties() -{ - auto lock = std::lock_guard(sync); - topicForSubscribing.clear(); - bool isPresent = false; - if (objPtr.hasProperty(PROPERTY_NAME_TOPIC)) - { - auto topicStr = objPtr.getPropertyValue(PROPERTY_NAME_TOPIC).asPtrOrNull(); - if (topicStr.assigned()) - { - isPresent = true; - const auto validationStatus = mqtt::MqttDataWrapper::validateTopic(topicStr, loggerComponent); - if (validationStatus.success) - { - LOG_I("An MQTT topic: {}", topicStr.toStdString()); - topicForSubscribing = topicStr.toStdString(); - setComponentStatus(ComponentStatus::Ok); - subscriptionStatus.setStatus(SubscriptionStatus::WaitingForData, "Subscribing to topic: " + topicForSubscribing); - } - else - { - setComponentStatus(ComponentStatus::Warning); - subscriptionStatus.setStatus(SubscriptionStatus::InvalidTopicName, validationStatus.msg); - } - } - } - - if (objPtr.hasProperty(PROPERTY_NAME_SUB_QOS)) - { - auto qosProp = objPtr.getPropertyValue(PROPERTY_NAME_SUB_QOS).asPtrOrNull(); - if (qosProp.assigned()) - { - const auto qos = qosProp.getValue(DEFAULT_SUB_QOS); - this->qos = (qos < 0 || qos > 2) ? DEFAULT_SUB_QOS : qos; - } - } - - if (!isPresent) - { - LOG_W("\'{}\' property is missing!", PROPERTY_NAME_TOPIC); - setComponentStatus(ComponentStatus::Warning); - subscriptionStatus.setStatus(SubscriptionStatus::InvalidTopicName, "The topic property is not set!"); - } - if (topicForSubscribing.empty()) - { - LOG_W("No topic to subscribe to!"); - } -} - -void MqttRawReceiverFbImpl::propertyChanged() -{ - auto result = unsubscribeFromTopic(); - if (result.success == false) - { - LOG_W("Failed to unsubscribe from the previous topic before subscribing to a new one; reason: {}", result.msg); - return; - } - readProperties(); - result = subscribeToTopic(); -} - -void MqttRawReceiverFbImpl::processMessage(const mqtt::MqttMessage& msg) -{ - const std::string topic(msg.getTopic()); - - auto lock = std::lock_guard(sync); - if (topicForSubscribing == topic) - { - if (subscriptionStatus.getStatus() == SubscriptionStatus::WaitingForData) - { - subscriptionStatus.setStatus(SubscriptionStatus::HasData); - } - const auto outputPacket = BinaryDataPacket(nullptr, outputSignal.getDescriptor(), msg.getData().size()); - memcpy(outputPacket.getData(), msg.getData().data(), msg.getData().size()); - outputSignal.sendPacket(outputPacket); - } -} - -void MqttRawReceiverFbImpl::createSignals() -{ - auto lock = std::lock_guard(sync); - const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::Binary).build(); - outputSignal = createAndAddSignal(DEFAULT_VALUE_SIGNAL_LOCAL_ID, signalDsc); -} - -std::string MqttRawReceiverFbImpl::getSubscribedTopic() const -{ - return topicForSubscribing; -} - -void MqttRawReceiverFbImpl::clearSubscribedTopic() -{ - topicForSubscribing.clear(); -} - -END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp index 81ac486..71c691c 100644 --- a/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp @@ -1,7 +1,6 @@ #include "mqtt_streaming_module/constants.h" -#include "mqtt_streaming_module/mqtt_json_receiver_fb_impl.h" +#include "mqtt_streaming_module/mqtt_subscriber_fb_impl.h" #include "mqtt_streaming_module/mqtt_publisher_fb_impl.h" -#include "mqtt_streaming_module/mqtt_raw_receiver_fb_impl.h" #include #include #include @@ -60,14 +59,9 @@ void MqttRootFbImpl::removed() void MqttRootFbImpl::initNestedFbTypes() { nestedFbTypes = Dict(); - // Add a function block type for manual JSON configuration + // Add a MQTT subscriber function block type { - const auto fbType = MqttJsonReceiverFbImpl::CreateType(); - nestedFbTypes.set(fbType.getId(), fbType); - } - // Add a function block type for raw MQTT messages - { - const auto fbType = MqttRawReceiverFbImpl::CreateType(); + const auto fbType = MqttSubscriberFbImpl::CreateType(); nestedFbTypes.set(fbType.getId(), fbType); } @@ -191,13 +185,9 @@ FunctionBlockPtr MqttRootFbImpl::onAddFunctionBlock(const StringPtr& typeId, con if (nestedFbTypes.hasKey(typeId)) { auto fbTypePtr = nestedFbTypes.getOrDefault(typeId); - if (fbTypePtr.getName() == RAW_FB_NAME) - { - nestedFunctionBlock = createWithImplementation(context, functionBlocks, fbTypePtr, subscriber, config); - } - else if (fbTypePtr.getName() == JSON_FB_NAME) + if (fbTypePtr.getName() == SUB_FB_NAME) { - nestedFunctionBlock = createWithImplementation(context, functionBlocks, fbTypePtr, subscriber, config); + nestedFunctionBlock = createWithImplementation(context, functionBlocks, fbTypePtr, subscriber, config); } else if (fbTypePtr.getName() == PUB_FB_NAME) { diff --git a/mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp b/mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp index e73122c..9802c15 100644 --- a/mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp @@ -13,8 +13,6 @@ #include #include -#include - BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE MqttStreamingModule::MqttStreamingModule(ContextPtr context) diff --git a/mqtt_streaming_module/src/mqtt_json_receiver_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp similarity index 58% rename from mqtt_streaming_module/src/mqtt_json_receiver_fb_impl.cpp rename to mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp index a6bca9e..e3bebf5 100644 --- a/mqtt_streaming_module/src/mqtt_json_receiver_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp @@ -1,23 +1,39 @@ #include "mqtt_streaming_module/constants.h" #include +#include #include -#include #include -#include +#include +#include #include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -std::atomic MqttJsonReceiverFbImpl::localIndex = 0; +constexpr int MQTT_FB_UNSUBSCRIBE_TOUT = 3000; + +std::atomic MqttSubscriberFbImpl::localIndex = 0; +std::vector> MqttSubscriberFbImpl::subscriptionStatusMap = + {{SubscriptionStatus::InvalidTopicName, "InvalidTopicName"}, + {SubscriptionStatus::SubscribingError, "SubscribingError"}, + {SubscriptionStatus::WaitingForData, "WaitingForData"}, + {SubscriptionStatus::HasData, "HasData"}}; -MqttJsonReceiverFbImpl::MqttJsonReceiverFbImpl(const ContextPtr& ctx, - const ComponentPtr& parent, - const FunctionBlockTypePtr& type, - std::shared_ptr subscriber, - const PropertyObjectPtr& config) - : MqttBaseFb(ctx, parent, type, generateLocalId(), subscriber, config), +MqttSubscriberFbImpl::MqttSubscriberFbImpl(const ContextPtr& ctx, + const ComponentPtr& parent, + const FunctionBlockTypePtr& type, + std::shared_ptr subscriber, + const PropertyObjectPtr& config) + : FunctionBlock(type, ctx, parent, generateLocalId()), + subscriber(subscriber), + subscriptionStatus(MQTT_FB_SUB_STATUS_TYPE, + MQTT_FB_SUB_STATUS_NAME, + statusContainer, + subscriptionStatusMap, + SubscriptionStatus::InvalidTopicName, + context.getTypeManager()), jsonDataWorker(loggerComponent) { + initComponentStatus(); initNestedFbTypes(); if (config.assigned()) initProperties(populateDefaultConfig(type.createDefaultConfig(), config)); @@ -31,12 +47,23 @@ MqttJsonReceiverFbImpl::MqttJsonReceiverFbImpl(const ContextPtr& ctx, subscribeToTopic(); } -MqttJsonReceiverFbImpl::~MqttJsonReceiverFbImpl() +MqttSubscriberFbImpl::~MqttSubscriberFbImpl() +{ + unsubscribeFromTopic(); +} + +void MqttSubscriberFbImpl::removed() { + FunctionBlock::removed(); unsubscribeFromTopic(); } -void MqttJsonReceiverFbImpl::initProperties(const PropertyObjectPtr& config) +void MqttSubscriberFbImpl::onSignalsMessage(const mqtt::MqttAsyncClient& subscriber, const mqtt::MqttMessage& msg) +{ + processMessage(msg); +} + +void MqttSubscriberFbImpl::initProperties(const PropertyObjectPtr& config) { for (const auto& prop : config.getAllProperties()) { @@ -76,12 +103,12 @@ void MqttJsonReceiverFbImpl::initProperties(const PropertyObjectPtr& config) readProperties(); } -FunctionBlockTypePtr MqttJsonReceiverFbImpl::CreateType() +FunctionBlockTypePtr MqttSubscriberFbImpl::CreateType() { auto defaultConfig = PropertyObject(); { auto builder = - StringPropertyBuilder(PROPERTY_NAME_TOPIC, String("")).setDescription("An MQTT topic to subscribe to for receiving JSON data."); + StringPropertyBuilder(PROPERTY_NAME_TOPIC, String("")).setDescription("An MQTT topic to subscribe to for receiving data."); defaultConfig.addProperty(builder.build()); } { @@ -94,9 +121,9 @@ FunctionBlockTypePtr MqttJsonReceiverFbImpl::CreateType() defaultConfig.addProperty(builder.build()); } { - auto builder = StringPropertyBuilder(PROPERTY_NAME_JSON_CONFIG, String("")) - .setDescription( - "JSON configuration string that defines an MQTT topic and corresponding signals to subscribe to."); + auto builder = + StringPropertyBuilder(PROPERTY_NAME_JSON_CONFIG, String("")) + .setDescription("JSON configuration string that defines an MQTT topic and corresponding signals to subscribe to."); defaultConfig.addProperty(builder.build()); } { @@ -104,20 +131,21 @@ FunctionBlockTypePtr MqttJsonReceiverFbImpl::CreateType() .setDescription("Path to file where the JSON configuration string is stored."); defaultConfig.addProperty(builder.build()); } - const auto fbType = FunctionBlockType(JSON_FB_NAME, - JSON_FB_NAME, - "The JSON MQTT function block allows subscribing to MQTT topics, extracting values and " - "timestamps from MQTT JSON messages, and converting them into openDAQ signal data samples.", - defaultConfig); + const auto fbType = + FunctionBlockType(SUB_FB_NAME, + SUB_FB_NAME, + "The subscriber MQTT function block allows subscribing to an MQTT topic and converting MQTT payloads into " + "openDAQ signal binary data samples.", + defaultConfig); return fbType; } -std::string MqttJsonReceiverFbImpl::generateLocalId() +std::string MqttSubscriberFbImpl::generateLocalId() { - return std::string(MQTT_LOCAL_JSON_FB_ID_PREFIX + std::to_string(localIndex++)); + return std::string(MQTT_LOCAL_SUB_FB_ID_PREFIX + std::to_string(localIndex++)); } -void MqttJsonReceiverFbImpl::initNestedFbTypes() +void MqttSubscriberFbImpl::initNestedFbTypes() { nestedFbTypes = Dict(); // Add a function block type for manual JSON configuration @@ -127,7 +155,7 @@ void MqttJsonReceiverFbImpl::initNestedFbTypes() } } -void MqttJsonReceiverFbImpl::readProperties() +void MqttSubscriberFbImpl::readProperties() { auto lock = this->getRecursiveConfigLock(); topicForSubscribing.clear(); @@ -158,7 +186,7 @@ void MqttJsonReceiverFbImpl::readProperties() } } -void MqttJsonReceiverFbImpl::readJsonConfig() +void MqttSubscriberFbImpl::readJsonConfig() { bool hasJsonConfig = false; if (objPtr.hasProperty(PROPERTY_NAME_JSON_CONFIG)) @@ -197,7 +225,7 @@ void MqttJsonReceiverFbImpl::readJsonConfig() } } -std::pair MqttJsonReceiverFbImpl::readFileToString(const std::string& filePath) +std::pair MqttSubscriberFbImpl::readFileToString(const std::string& filePath) { std::ifstream file(filePath); if (!file) @@ -208,7 +236,7 @@ std::pair MqttJsonReceiverFbImpl::readFileToString(const std: return std::pair(true, buffer.str()); } -void MqttJsonReceiverFbImpl::setJsonConfig(const std::string config) +void MqttSubscriberFbImpl::setJsonConfig(const std::string config) { jsonDataWorker.setConfig(config); auto result = jsonDataWorker.isJsonValid(); @@ -234,7 +262,7 @@ void MqttJsonReceiverFbImpl::setJsonConfig(const std::string config) fbConfig.setPropertyValue(PROPERTY_NAME_TS_NAME, descriptor.tsFieldName); if (descriptor.unit.assigned()) fbConfig.setPropertyValue(PROPERTY_NAME_UNIT, descriptor.unit.getSymbol()); - MqttJsonReceiverFbImpl::onAddFunctionBlock(JSON_DECODER_FB_NAME, fbConfig); + MqttSubscriberFbImpl::onAddFunctionBlock(JSON_DECODER_FB_NAME, fbConfig); } } } @@ -251,7 +279,7 @@ void MqttJsonReceiverFbImpl::setJsonConfig(const std::string config) } } -void MqttJsonReceiverFbImpl::propertyChanged() +void MqttSubscriberFbImpl::propertyChanged() { auto lock = this->getRecursiveConfigLock(); auto result = unsubscribeFromTopic(); @@ -264,7 +292,7 @@ void MqttJsonReceiverFbImpl::propertyChanged() result = subscribeToTopic(); } -bool MqttJsonReceiverFbImpl::setTopic(std::string topic) +bool MqttSubscriberFbImpl::setTopic(std::string topic) { const auto validationStatus = mqtt::MqttDataWrapper::validateTopic(topic, loggerComponent); if (validationStatus.success) @@ -282,12 +310,12 @@ bool MqttJsonReceiverFbImpl::setTopic(std::string topic) return validationStatus.success; } -DictPtr MqttJsonReceiverFbImpl::onGetAvailableFunctionBlockTypes() +DictPtr MqttSubscriberFbImpl::onGetAvailableFunctionBlockTypes() { return nestedFbTypes; } -FunctionBlockPtr MqttJsonReceiverFbImpl::onAddFunctionBlock(const StringPtr& typeId, const PropertyObjectPtr& config) +FunctionBlockPtr MqttSubscriberFbImpl::onAddFunctionBlock(const StringPtr& typeId, const PropertyObjectPtr& config) { FunctionBlockPtr nestedFunctionBlock; @@ -296,7 +324,8 @@ FunctionBlockPtr MqttJsonReceiverFbImpl::onAddFunctionBlock(const StringPtr& typ auto fbTypePtr = nestedFbTypes.getOrDefault(typeId); if (fbTypePtr.getName() == JSON_DECODER_FB_NAME) { - nestedFunctionBlock = createWithImplementation(context, functionBlocks, fbTypePtr, config); + nestedFunctionBlock = + createWithImplementation(context, functionBlocks, fbTypePtr, config); } } if (nestedFunctionBlock.assigned()) @@ -315,7 +344,7 @@ FunctionBlockPtr MqttJsonReceiverFbImpl::onAddFunctionBlock(const StringPtr& typ return nestedFunctionBlock; } -void MqttJsonReceiverFbImpl::onRemoveFunctionBlock(const FunctionBlockPtr& functionBlock) +void MqttSubscriberFbImpl::onRemoveFunctionBlock(const FunctionBlockPtr& functionBlock) { { auto lock = this->getAcquisitionLock2(); @@ -331,7 +360,7 @@ void MqttJsonReceiverFbImpl::onRemoveFunctionBlock(const FunctionBlockPtr& funct FunctionBlockImpl::onRemoveFunctionBlock(functionBlock); } -void MqttJsonReceiverFbImpl::processMessage(const mqtt::MqttMessage& msg) +void MqttSubscriberFbImpl::processMessage(const mqtt::MqttMessage& msg) { if (topicForSubscribing == msg.getTopic()) { @@ -342,6 +371,11 @@ void MqttJsonReceiverFbImpl::processMessage(const mqtt::MqttMessage& msg) std::string jsonObjStr(msg.getData().begin(), msg.getData().end()); auto acqlock = this->getAcquisitionLock2(); + + const auto outputPacket = BinaryDataPacket(nullptr, outputSignal.getDescriptor(), msg.getData().size()); + memcpy(outputPacket.getData(), msg.getData().data(), msg.getData().size()); + outputSignal.sendPacket(outputPacket); + for (const auto& fb : nestedFunctionBlocks) { if (fb.assigned()) @@ -353,19 +387,103 @@ void MqttJsonReceiverFbImpl::processMessage(const mqtt::MqttMessage& msg) } } -void MqttJsonReceiverFbImpl::createSignals() +void MqttSubscriberFbImpl::createSignals() { - + auto lock = this->getRecursiveConfigLock(); // ??? + const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::Binary).build(); + outputSignal = createAndAddSignal(DEFAULT_VALUE_SIGNAL_LOCAL_ID, signalDsc); } -std::string MqttJsonReceiverFbImpl::getSubscribedTopic() const +std::string MqttSubscriberFbImpl::getSubscribedTopic() const { return topicForSubscribing; } -void MqttJsonReceiverFbImpl::clearSubscribedTopic() +void MqttSubscriberFbImpl::clearSubscribedTopic() { topicForSubscribing.clear(); } +MqttSubscriberFbImpl::CmdResult MqttSubscriberFbImpl::subscribeToTopic() +{ + CmdResult result{false}; + if (subscriber) + { + auto lambda = [this](const mqtt::MqttAsyncClient& client, mqtt::MqttMessage& msg) { this->onSignalsMessage(client, msg); }; + const auto topic = getSubscribedTopic(); + if (!topic.empty()) + { + LOG_I("Trying to subscribe to the topic : {}", topic); + subscriber->setMessageArrivedCb(topic, lambda); + if (auto subRes = subscriber->subscribe(topic, qos); subRes.success == false) + { + LOG_W("Failed to subscribe to the topic: \"{}\"; reason: {}", topic, subRes.msg); + setComponentStatusWithMessage(ComponentStatus::Warning, "Some topics failed to subscribe!"); + subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, "The reason: " + subRes.msg); + result = {false, "Failed to subscribe to the topic: \"" + topic + "\"; reason: " + subRes.msg}; + } + else + { + // subscriber->subscribe(...) is asynchronous. It puts command in queue and returns immediately. + LOG_D("Trying to subscribe to the topic: {}", topic); + setComponentStatus(ComponentStatus::Ok); + subscriptionStatus.setStatus(SubscriptionStatus::WaitingForData, "Topic: " + topic); + result = {true, "", result.token}; + } + } + else + { + result = {false, "Couldn't subscribe to an empty topic"}; + LOG_W("{}", result.msg); + } + } + else + { + const std::string msg = "MQTT subscriber client is not set!"; + setComponentStatusWithMessage(ComponentStatus::Error, msg); + subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, msg); + result = {false, msg}; + } + return result; +} + +MqttSubscriberFbImpl::CmdResult MqttSubscriberFbImpl::unsubscribeFromTopic() +{ + CmdResult result{true}; + if (subscriber) + { + const auto topic = getSubscribedTopic(); + if (!topic.empty()) + { + subscriber->setMessageArrivedCb(topic, nullptr); + mqtt::CmdResult unsubRes = subscriber->unsubscribe(topic); + if (unsubRes.success) + unsubRes = subscriber->waitForCompletion(unsubRes.token, MQTT_FB_UNSUBSCRIBE_TOUT); + + if (unsubRes.success) + { + clearSubscribedTopic(); + LOG_I("The topic \'{}\' has been unsubscribed successfully", topic); + result = {true}; + } + else + { + const auto msg = fmt::format("Failed to unsubscribe from the topic \'{}\'; reason: {}", topic, unsubRes.msg); + LOG_W("{}", msg); + setComponentStatus(ComponentStatus::Warning); + subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, msg); + result = {false, msg}; + } + } + } + else + { + const std::string msg = "MQTT subscriber client is not set!"; + setComponentStatusWithMessage(ComponentStatus::Error, msg); + subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, msg); + result = {false, msg}; + } + return result; +} + END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/tests/CMakeLists.txt b/mqtt_streaming_module/tests/CMakeLists.txt index 85c143d..7c3ddeb 100644 --- a/mqtt_streaming_module/tests/CMakeLists.txt +++ b/mqtt_streaming_module/tests/CMakeLists.txt @@ -3,8 +3,7 @@ set(TEST_APP test_${MODULE_NAME}) set(TEST_SOURCES test_mqtt_streaming_module.cpp test_mqtt_root_fb.cpp - test_mqtt_raw_fb.cpp - test_mqtt_json_fb.cpp + test_mqtt_subscriber_fb.cpp test_mqtt_json_decoder_fb.cpp test_mqtt_publisher_fb.cpp test_daq_test_helper.h diff --git a/mqtt_streaming_module/tests/test_daq_test_helper.h b/mqtt_streaming_module/tests/test_daq_test_helper.h index d6e2f07..0b31b41 100644 --- a/mqtt_streaming_module/tests/test_daq_test_helper.h +++ b/mqtt_streaming_module/tests/test_daq_test_helper.h @@ -10,7 +10,7 @@ class DaqTestHelper public: daq::InstancePtr daqInstance; daq::FunctionBlockPtr rootMqttFb; - daq::FunctionBlockPtr jsonMqttFb; + daq::FunctionBlockPtr subMqttFb; void StartUp(std::string url = DEFAULT_BROKER_ADDRESS, uint16_t port = DEFAULT_PORT) { @@ -60,11 +60,11 @@ class DaqTestHelper return module; } - daq::FunctionBlockPtr AddJsonFb(std::string topic = "") + daq::FunctionBlockPtr AddSubFb(std::string topic = "") { - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(JSON_FB_NAME).createDefaultConfig(); + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_TOPIC, daq::String(topic)); - jsonMqttFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config); - return jsonMqttFb; + subMqttFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config); + return subMqttFb; } }; diff --git a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp index 052af22..8e459cb 100644 --- a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp @@ -1,6 +1,6 @@ #include "MqttAsyncClientWrapper.h" #include "mqtt_streaming_helper/timer.h" -#include "mqtt_streaming_module/mqtt_json_receiver_fb_impl.h" +#include "mqtt_streaming_module/mqtt_subscriber_fb_impl.h" #include "mqtt_streaming_module/mqtt_json_decoder_fb_impl.h" #include "test_daq_test_helper.h" #include "test_data.h" @@ -44,7 +44,7 @@ class MqttJsonDecoderFbHelper : public DaqTestHelper void onSignalsMessage(const mqtt::MqttMessage& msg) { mqtt::MqttAsyncClient unused; - auto fb = reinterpret_cast(*jsonMqttFb); + auto fb = reinterpret_cast(*subMqttFb); fb->onSignalsMessage(unused, msg); } @@ -52,18 +52,18 @@ class MqttJsonDecoderFbHelper : public DaqTestHelper { auto config = PropertyObject(); config.addProperty(StringProperty(PROPERTY_NAME_SIGNAL_LIST, String(""))); - const auto fbType = FunctionBlockType(JSON_FB_NAME, JSON_FB_NAME, "", config); + const auto fbType = FunctionBlockType(SUB_FB_NAME, SUB_FB_NAME, "", config); config.setPropertyValue(PROPERTY_NAME_SIGNAL_LIST, jsonConfig); - jsonMqttFb = new MqttJsonReceiverFbImpl(NullContext(), nullptr, fbType, nullptr, config); + subMqttFb = new MqttSubscriberFbImpl(NullContext(), nullptr, fbType, nullptr, config); } void CreateJsonFb(const std::string& topic) { auto config = PropertyObject(); config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, String(""))); - const auto fbType = FunctionBlockType(JSON_FB_NAME, JSON_FB_NAME, "", config); + const auto fbType = FunctionBlockType(SUB_FB_NAME, SUB_FB_NAME, "", config); config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); - jsonMqttFb = new MqttJsonReceiverFbImpl(NullContext(), nullptr, fbType, nullptr, config); + subMqttFb = new MqttSubscriberFbImpl(NullContext(), nullptr, fbType, nullptr, config); } void CreateDecoderFB(std::string topic, std::string valueF, std::string tsF, std::string unitSymbol = "") @@ -75,12 +75,12 @@ class MqttJsonDecoderFbHelper : public DaqTestHelper daq::FunctionBlockPtr AddDecoderFb(std::string valueF, std::string tsF, std::string unitSymbol = "") { daq::StringPtr typeId = daq::String(JSON_DECODER_FB_NAME); - auto config = jsonMqttFb.getAvailableFunctionBlockTypes().get(JSON_DECODER_FB_NAME).createDefaultConfig(); + auto config = subMqttFb.getAvailableFunctionBlockTypes().get(JSON_DECODER_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_VALUE_NAME, valueF); config.setPropertyValue(PROPERTY_NAME_TS_NAME, tsF); config.setPropertyValue(PROPERTY_NAME_UNIT, unitSymbol); - decoderObj = jsonMqttFb.addFunctionBlock(typeId, config); + decoderObj = subMqttFb.addFunctionBlock(typeId, config); return decoderObj; } @@ -455,11 +455,11 @@ class MqttJsonFbUnitPTest : public ::testing::TestWithParam fbTypes; daq::FunctionBlockTypePtr fbt; daq::PropertyObjectPtr defaultConfig; - ASSERT_NO_THROW(fbTypes = jsonMqttFb.getAvailableFunctionBlockTypes()); + ASSERT_NO_THROW(fbTypes = subMqttFb.getAvailableFunctionBlockTypes()); ASSERT_NO_THROW(fbt = fbTypes.get(JSON_DECODER_FB_NAME)); ASSERT_NO_THROW(defaultConfig = fbt.createDefaultConfig()); @@ -483,14 +483,14 @@ TEST_F(MqttJsonDecoderFbTest, DefaultConfig) TEST_F(MqttJsonDecoderFbTest, Config) { StartUp(); - AddJsonFb(buildTopicName()); - auto config = jsonMqttFb.getAvailableFunctionBlockTypes().get(JSON_DECODER_FB_NAME).createDefaultConfig(); + AddSubFb(buildTopicName()); + auto config = subMqttFb.getAvailableFunctionBlockTypes().get(JSON_DECODER_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_VALUE_NAME, "value"); config.setPropertyValue(PROPERTY_NAME_TS_NAME, "timestamp"); config.setPropertyValue(PROPERTY_NAME_UNIT, "ppm"); daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = jsonMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); + ASSERT_NO_THROW(fb = subMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); EXPECT_EQ(fb.getSignals().getCount(), 1u); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -512,9 +512,9 @@ TEST_F(MqttJsonDecoderFbTest, Config) TEST_F(MqttJsonDecoderFbTest, CreationWithDefaultConfig) { StartUp(); - AddJsonFb(buildTopicName()); + AddSubFb(buildTopicName()); daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = jsonMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME)); + ASSERT_NO_THROW(fb = subMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME)); EXPECT_EQ(fb.getSignals().getCount(), 1u); EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); @@ -527,12 +527,12 @@ TEST_F(MqttJsonDecoderFbTest, CreationWithDefaultConfig) TEST_F(MqttJsonDecoderFbTest, CreationWithPartialConfig) { StartUp(); - AddJsonFb(buildTopicName()); + AddSubFb(buildTopicName()); { daq::FunctionBlockPtr fb; auto config = PropertyObject(); config.addProperty(StringProperty(PROPERTY_NAME_VALUE_NAME, String("value"))); - ASSERT_NO_THROW(fb = jsonMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); + ASSERT_NO_THROW(fb = subMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); EXPECT_EQ(fb.getSignals().getCount(), 1u); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -540,13 +540,13 @@ TEST_F(MqttJsonDecoderFbTest, CreationWithPartialConfig) EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, static_cast(MqttJsonDecoderFbImpl::ParsingStatus::WaitingForData), daqInstance.getContext().getTypeManager())); - jsonMqttFb.removeFunctionBlock(fb); + subMqttFb.removeFunctionBlock(fb); } { daq::FunctionBlockPtr fb; auto config = PropertyObject(); config.addProperty(StringProperty(PROPERTY_NAME_TS_NAME, String("ts"))); - ASSERT_NO_THROW(fb = jsonMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); + ASSERT_NO_THROW(fb = subMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); EXPECT_EQ(fb.getSignals().getCount(), 1u); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); @@ -554,7 +554,7 @@ TEST_F(MqttJsonDecoderFbTest, CreationWithPartialConfig) EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, static_cast(MqttJsonDecoderFbImpl::ParsingStatus::InvalidParamaters), daqInstance.getContext().getTypeManager())); - jsonMqttFb.removeFunctionBlock(fb); + subMqttFb.removeFunctionBlock(fb); } } @@ -683,7 +683,7 @@ TEST_F(MqttJsonDecoderFbTest, DataTransferSeveralSignals) DaqInstanceInit(); auto rootFb0 = DaqAddRootMqttFb("127.0.0.1", DEFAULT_PORT); - auto jsonFb0 = AddJsonFb(topic); + auto jsonFb0 = AddSubFb(topic); auto decoderFb0 = AddDecoderFb(valueF0, tsF); auto decoderFb1 = AddDecoderFb(valueF1, tsF); auto decoderFb2 = AddDecoderFb(valueF2, ""); @@ -782,7 +782,7 @@ TEST_F(MqttJsonDecoderFbTest, DataTransferMissingFieldSeveralSignals) DaqInstanceInit(); auto rootFb0 = DaqAddRootMqttFb("127.0.0.1", DEFAULT_PORT); - auto jsonFb0 = AddJsonFb(topic); + auto jsonFb0 = AddSubFb(topic); auto decoderFb0 = AddDecoderFb(valueF0, tsF); auto decoderFb1 = AddDecoderFb(valueF1, tsF); auto decoderFb2 = AddDecoderFb(valueF2, ""); @@ -848,7 +848,7 @@ TEST_F(MqttJsonFbCommunicationTest, FullDataTransfer) const auto msgTemplate = VALID_JSON_DATA_0; const std::string valueF = extractFieldName(msgTemplate, ""); const std::string tsF = extractFieldName(msgTemplate, ""); - AddJsonFb(topic); + AddSubFb(topic); AddDecoderFb(valueF, tsF); const auto result = processTransfer("127.0.0.1", DEFAULT_PORT, topic, DATA_DOUBLE_INT_0, decoderObj.getSignals()[0]); @@ -875,11 +875,11 @@ TEST_F(MqttJsonFbCommunicationTest, FullDataTransferFor2MqttFbs) DaqInstanceInit(); auto rootFb0 = DaqAddRootMqttFb("127.0.0.1", 1883); - auto jsonFb0 = AddJsonFb(topic0); + auto jsonFb0 = AddSubFb(topic0); auto decoderFb0 = AddDecoderFb(valueF, tsF); auto rootFb1 = DaqAddRootMqttFb("127.0.0.1", 1884); - auto jsonFb1 = AddJsonFb(topic1); + auto jsonFb1 = AddSubFb(topic1); auto decoderFb1 = AddDecoderFb(valueF, tsF); const auto result0 = processTransfer("127.0.0.1", 1883, topic0, DATA_DOUBLE_INT_0, decoderFb0.getSignals()[0]); @@ -911,17 +911,17 @@ TEST_F(MqttJsonFbCommunicationTest, FullDataTransferFor2MqttFbs) TEST_F(MqttJsonDecoderFbTest, RemovingNestedFunctionBlock) { StartUp(); - AddJsonFb(buildTopicName()); + AddSubFb(buildTopicName()); daq::FunctionBlockPtr jsonDecoderFb; { auto config = PropertyObject(); config.addProperty(StringProperty(PROPERTY_NAME_VALUE_NAME, String("temp"))); - ASSERT_NO_THROW(jsonDecoderFb = jsonMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); + ASSERT_NO_THROW(jsonDecoderFb = subMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); } - ASSERT_EQ(jsonMqttFb.getFunctionBlocks().getCount(), 1u); + ASSERT_EQ(subMqttFb.getFunctionBlocks().getCount(), 1u); - ASSERT_NO_THROW(jsonMqttFb.removeFunctionBlock(jsonDecoderFb)); - ASSERT_EQ(jsonMqttFb.getFunctionBlocks().getCount(), 0u); - ASSERT_EQ(jsonMqttFb.getStatusContainer().getStatus("ComponentStatus"), + ASSERT_NO_THROW(subMqttFb.removeFunctionBlock(jsonDecoderFb)); + ASSERT_EQ(subMqttFb.getFunctionBlocks().getCount(), 0u); + ASSERT_EQ(subMqttFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); } diff --git a/mqtt_streaming_module/tests/test_mqtt_json_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_json_fb.cpp deleted file mode 100644 index 8caa9a9..0000000 --- a/mqtt_streaming_module/tests/test_mqtt_json_fb.cpp +++ /dev/null @@ -1,386 +0,0 @@ -#include "mqtt_streaming_module/mqtt_json_receiver_fb_impl.h" -#include "test_daq_test_helper.h" -#include "test_data.h" -#include -#include -#include -#include -#include -#include - -using namespace daq; -using namespace daq::modules::mqtt_streaming_module; - -namespace daq::modules::mqtt_streaming_module -{ -class MqttJsonFbHelper -{ -public: - std::unique_ptr obj; - - auto getSignals() - { - auto signalList = List(); - obj->getSignals(&signalList); - return signalList; - } - - std::string buildTopicName(const std::string& postfix = "") - { - return std::string("test/topic/") + std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + postfix; - } - - std::string buildClientId() - { - return std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + "_ClientId"; - } -}; - -class MqttJsonFbTest : public testing::Test, public DaqTestHelper, public MqttJsonFbHelper -{ -}; - -class MqttJsonFbTopicPTest : public ::testing::TestWithParam>, - public DaqTestHelper, - public MqttJsonFbHelper -{ -}; - -class MqttJsonFbConfigPTest : public ::testing::TestWithParam, - public DaqTestHelper, - public MqttJsonFbHelper -{ -}; - -class MqttJsonFbConfigFilePTest : public ::testing::TestWithParam, - public DaqTestHelper, - public MqttJsonFbHelper -{ -}; -} // namespace daq::modules::mqtt_streaming_module - -TEST_F(MqttJsonFbTest, DefaultConfig) -{ - StartUp(); - daq::DictPtr fbTypes; - daq::FunctionBlockTypePtr fbt; - daq::PropertyObjectPtr defaultConfig; - ASSERT_NO_THROW(fbTypes = rootMqttFb.getAvailableFunctionBlockTypes()); - ASSERT_NO_THROW(fbt = fbTypes.get(JSON_FB_NAME)); - ASSERT_NO_THROW(defaultConfig = fbt.createDefaultConfig()); - - ASSERT_TRUE(defaultConfig.assigned()); - - ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 4u); - - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_JSON_CONFIG)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_JSON_CONFIG).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_JSON_CONFIG).asPtr().getLength(), 0u); - - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_JSON_CONFIG_FILE)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_JSON_CONFIG_FILE).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE).asPtr().getLength(), 0u); - - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_TOPIC)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_TOPIC).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().getLength(), 0u); - - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_QOS)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_QOS).getValueType(), CoreType::ctInt); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_SUB_QOS).asPtr().getValue(DEFAULT_SUB_QOS), DEFAULT_SUB_QOS); -} - -TEST_F(MqttJsonFbTest, Config) -{ - StartUp(); - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(JSON_FB_NAME).createDefaultConfig(); - - config.setPropertyValue(PROPERTY_NAME_TOPIC, buildTopicName()); - daq::FunctionBlockPtr jsonFb; - ASSERT_NO_THROW(jsonFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - - const auto allProperties = jsonFb.getAllProperties(); - ASSERT_EQ(allProperties.getCount(), config.getAllProperties().getCount()); - - for (const auto& pror : config.getAllProperties()) - { - const auto propName = pror.getName(); - ASSERT_TRUE(jsonFb.hasProperty(propName)); - ASSERT_EQ(jsonFb.getPropertyValue(propName), config.getPropertyValue(propName)); - } -} - -TEST_F(MqttJsonFbTest, CreationWithDefaultConfig) -{ - StartUp(); - daq::FunctionBlockPtr jsonFb; - ASSERT_NO_THROW(jsonFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME)); - EXPECT_EQ(jsonFb.getSignals().getCount(), 0u); - ASSERT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Warning", daqInstance.getContext().getTypeManager())); -} - -TEST_F(MqttJsonFbTest, CreationWithPartialConfig) -{ - // If FB has only one property, partial config is equivalent to custom config - StartUp(); - daq::FunctionBlockPtr jsonFb; - auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, String(buildTopicName()))); - ASSERT_NO_THROW(jsonFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - EXPECT_EQ(jsonFb.getSignals().getCount(), 0u); - ASSERT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); -} - -TEST_F(MqttJsonFbTest, CreationWithCustomConfig) -{ - // If FB has only one property, partial config is equivalent to custom config - StartUp(); - daq::FunctionBlockPtr jsonFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(JSON_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_TOPIC, String(buildTopicName())); - ASSERT_NO_THROW(jsonFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - EXPECT_EQ(jsonFb.getSignals().getCount(), 0u); - ASSERT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); -} - -TEST_P(MqttJsonFbTopicPTest, CheckJsonFbTopic) -{ - auto [topic, result] = GetParam(); - StartUp(); - - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(JSON_FB_NAME).createDefaultConfig(); - - config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); - daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - auto signals = fb.getSignals(); - ASSERT_EQ(signals.getCount(), 0); - const auto expectedComponentStatus = result ? "Ok" : "Warning"; - EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", expectedComponentStatus, daqInstance.getContext().getTypeManager())); - if (result) - { - EXPECT_NE(fb.getStatusContainer().getStatus("SubscriptionStatus"), - EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttBaseFb::SubscriptionStatus::InvalidTopicName), - daqInstance.getContext().getTypeManager())); - } - else - { - EXPECT_EQ(fb.getStatusContainer().getStatus("SubscriptionStatus"), - EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttBaseFb::SubscriptionStatus::InvalidTopicName), - daqInstance.getContext().getTypeManager())); - } -} - -INSTANTIATE_TEST_SUITE_P(TopicTest, - MqttJsonFbTopicPTest, - ::testing::Values(std::make_pair("", false), - std::make_pair("goodTopic/test", true), - std::make_pair("/goodTopic/test0", true), - std::make_pair("badTopic/+/test/topic", false), - std::make_pair("badTopic/+/+/topic", false), - std::make_pair("badTopic/#", false))); - -TEST_F(MqttJsonFbTest, RemovingNestedFunctionBlock) -{ - StartUp(); - daq::FunctionBlockPtr jsonFb; - { - auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, String(buildTopicName()))); - ASSERT_NO_THROW(jsonFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - ASSERT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - } - daq::FunctionBlockPtr jsonDecoderFb; - { - auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_VALUE_NAME, String("temp"))); - ASSERT_NO_THROW(jsonDecoderFb = jsonFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); - } - ASSERT_EQ(jsonFb.getFunctionBlocks().getCount(), 1u); - - ASSERT_NO_THROW(jsonFb.removeFunctionBlock(jsonDecoderFb)); - ASSERT_EQ(jsonFb.getFunctionBlocks().getCount(), 0u); -} - -TEST_F(MqttJsonFbTest, TwoFbCreation) -{ - StartUp(); - { - daq::FunctionBlockPtr fb; - auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, buildTopicName("0"))); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - } - { - daq::FunctionBlockPtr fb; - auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, buildTopicName("1"))); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - } - auto fbs = rootMqttFb.getFunctionBlocks(); - ASSERT_EQ(fbs.getCount(), 2u); -} - -TEST_F(MqttJsonFbTest, PropertyChanged) -{ - StartUp(); - - daq::FunctionBlockPtr fb; - auto config = PropertyObject(); - auto topic = buildTopicName("0"); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, topic)); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - auto jsonFb = reinterpret_cast(*fb); - - ASSERT_EQ(topic, jsonFb->getSubscribedTopic()); - topic = buildTopicName("1"); - fb.setPropertyValue(PROPERTY_NAME_TOPIC, topic); - ASSERT_EQ(topic, jsonFb->getSubscribedTopic()); -} - -TEST_F(MqttJsonFbTest, JsonInit0) -{ - StartUp(); - daq::FunctionBlockPtr jsonFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(JSON_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG, String(VALID_JSON_1_TOPIC_0)); - ASSERT_NO_THROW(jsonFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - ASSERT_EQ(jsonFb.getFunctionBlocks().getCount(), 3u); - ASSERT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - auto lambda = [&](FunctionBlockPtr nestedFb, std::string value, std::string ts, std::string symbol) - { - EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); - if (!symbol.empty()) - EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_TS_NAME).asPtr().toStdString(), ts); - }; - EXPECT_EQ(jsonFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "openDAQ/RefDev0/IO/AI/RefCh0/Sig/AI0"); - - lambda(jsonFb.getFunctionBlocks()[0], "value", "timestamp", "V"); - lambda(jsonFb.getFunctionBlocks()[1], "value1", "", ""); - lambda(jsonFb.getFunctionBlocks()[2], "value2", "", "W"); - -} - -TEST_F(MqttJsonFbTest, JsonInit1) -{ - StartUp(); - daq::FunctionBlockPtr jsonFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(JSON_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG, String(VALID_JSON_1_TOPIC_1)); - ASSERT_NO_THROW(jsonFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - ASSERT_EQ(jsonFb.getFunctionBlocks().getCount(), 3u); - ASSERT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - auto lambda = [&](FunctionBlockPtr nestedFb, std::string value, std::string ts, std::string symbol) - { - EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); - if (!symbol.empty()) - EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_TS_NAME).asPtr().toStdString(), ts); - }; - EXPECT_EQ(jsonFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "/mirip/UNet3AC2/sensor/data"); - - lambda(jsonFb.getFunctionBlocks()[0], "temp", "ts", "°C"); - lambda(jsonFb.getFunctionBlocks()[1], "humi", "ts", "%"); - lambda(jsonFb.getFunctionBlocks()[2], "tds_value", "ts", "ppm"); - -} - -TEST_P(MqttJsonFbConfigPTest, JsonWrongInit) -{ - const auto configJson = GetParam(); - StartUp(); - daq::FunctionBlockPtr jsonFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(JSON_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG, String(configJson)); - ASSERT_NO_THROW(jsonFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - EXPECT_EQ(jsonFb.getFunctionBlocks().getCount(), 0u); - EXPECT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); - EXPECT_EQ(jsonFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), ""); -} - -INSTANTIATE_TEST_SUITE_P( - JsonConfigTest, - MqttJsonFbConfigPTest, - ::testing::Values( - VALID_JSON_3_TOPIC_2, - WILDCARD_JSON_0, - WILDCARD_JSON_1, - INVALID_JSON_1, - INVALID_JSON_3)); - -TEST_P(MqttJsonFbConfigFilePTest, JsonInitFromFile) -{ - const auto configJson = GetParam(); - StartUp(); - daq::FunctionBlockPtr jsonFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(JSON_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE, String(configJson)); - ASSERT_NO_THROW(jsonFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - ASSERT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); -} - -INSTANTIATE_TEST_SUITE_P(JsonConfigTest, - MqttJsonFbConfigFilePTest, - ::testing::Values("data/public-example0.json", - "data/public-example1.json", - "data/public-example2.json", - "data/public-example3.json")); - -TEST_F(MqttJsonFbTest, JsonInitFromFileWithChecking) -{ - StartUp(); - daq::FunctionBlockPtr jsonFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(JSON_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE, String("data/public-example0.json")); - ASSERT_NO_THROW(jsonFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - ASSERT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(jsonFb.getFunctionBlocks().getCount(), 3u); - auto lambda = [&](FunctionBlockPtr nestedFb, std::string value, std::string ts, std::string symbol) - { - EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); - if (!symbol.empty()) - EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_TS_NAME).asPtr().toStdString(), ts); - }; - EXPECT_EQ(jsonFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "/mirip/UNet3AC2/sensor/data"); - - lambda(jsonFb.getFunctionBlocks()[0], "temp", "ts", "°C"); - lambda(jsonFb.getFunctionBlocks()[1], "humi", "ts", "%"); - lambda(jsonFb.getFunctionBlocks()[2], "tds_value", "ts", "ppm"); - -} - -TEST_F(MqttJsonFbTest, JsonInitFromFileWrongPath) -{ - StartUp(); - daq::FunctionBlockPtr jsonFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(JSON_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE, String("/justWrongPath/wrongFile.txt")); - ASSERT_NO_THROW(jsonFb = rootMqttFb.addFunctionBlock(JSON_FB_NAME, config)); - EXPECT_EQ(jsonFb.getFunctionBlocks().getCount(), 0u); - EXPECT_EQ(jsonFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); - EXPECT_EQ(jsonFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), ""); -} diff --git a/mqtt_streaming_module/tests/test_mqtt_raw_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_raw_fb.cpp deleted file mode 100644 index a0ce92b..0000000 --- a/mqtt_streaming_module/tests/test_mqtt_raw_fb.cpp +++ /dev/null @@ -1,392 +0,0 @@ -#include "MqttAsyncClientWrapper.h" -#include "mqtt_streaming_module/mqtt_raw_receiver_fb_impl.h" -#include "test_daq_test_helper.h" -#include -#include -#include -#include -#include -#include -#include - -using namespace daq; -using namespace daq::modules::mqtt_streaming_module; - -namespace daq::modules::mqtt_streaming_module -{ -class MqttRawFbTest : public testing::Test, public DaqTestHelper -{ -public: - std::unique_ptr obj; - - void onSignalsMessage(mqtt::MqttMessage& msg) - { - mqtt::MqttAsyncClient unused; - obj->onSignalsMessage(unused, msg); - } - - void CreateRawFB(std::string topic) - { - auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, "")); - const auto fbType = FunctionBlockType(RAW_FB_NAME, RAW_FB_NAME, "", config); - config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); - obj = std::make_unique(NullContext(), nullptr, fbType, nullptr, config); - } - - std::string buildTopicName(const std::string& postfix = "") - { - return std::string("test/topic/") + std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + postfix; - } -}; - -class MqttRawFbPTest : public ::testing::TestWithParam>, - public DaqTestHelper -{ -}; -} // namespace daq::modules::mqtt_streaming_module - -TEST_F(MqttRawFbTest, DefaultRawFbConfig) -{ - StartUp(); - daq::DictPtr fbTypes; - daq::FunctionBlockTypePtr fbt; - daq::PropertyObjectPtr defaultConfig; - ASSERT_NO_THROW(fbTypes = rootMqttFb.getAvailableFunctionBlockTypes()); - ASSERT_NO_THROW(fbt = fbTypes.get(RAW_FB_NAME)); - ASSERT_NO_THROW(defaultConfig = fbt.createDefaultConfig()); - - ASSERT_TRUE(defaultConfig.assigned()); - - ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 2u); - - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_TOPIC)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_TOPIC).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().getLength(), 0u); - - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_QOS)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_QOS).getValueType(), CoreType::ctInt); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_SUB_QOS).asPtr().getValue(DEFAULT_SUB_QOS), DEFAULT_SUB_QOS); -} - -TEST_F(MqttRawFbTest, Creation) -{ - StartUp(); - daq::FunctionBlockPtr rawFb; - auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, buildTopicName())); - ASSERT_NO_THROW(rawFb = rootMqttFb.addFunctionBlock(RAW_FB_NAME, config)); - ASSERT_EQ(rawFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); -} - -TEST_F(MqttRawFbTest, CheckRawFbWithEmptyConfig) -{ - StartUp(); - daq::FunctionBlockPtr rawFb; - auto config = PropertyObject(); - ASSERT_NO_THROW(rawFb = rootMqttFb.addFunctionBlock(RAW_FB_NAME, config)); - auto signals = rawFb.getSignals(); - ASSERT_EQ(signals.getCount(), 1u); -} - -TEST_F(MqttRawFbTest, TwoFbCreation) -{ - StartUp(); - { - daq::FunctionBlockPtr fb; - auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, buildTopicName("0"))); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(RAW_FB_NAME, config)); - EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - } - { - daq::FunctionBlockPtr fb; - auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, buildTopicName("1"))); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(RAW_FB_NAME, config)); - EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - } - auto fbs = rootMqttFb.getFunctionBlocks(); - ASSERT_EQ(fbs.getCount(), 2u); -} - -TEST_F(MqttRawFbTest, CheckRawFbWithDefaultConfig) -{ - StartUp(); - daq::FunctionBlockPtr rawFb; - ASSERT_NO_THROW(rawFb = rootMqttFb.addFunctionBlock(RAW_FB_NAME)); - auto signals = rawFb.getSignals(); - ASSERT_EQ(signals.getCount(), 1u); -} - -TEST_F(MqttRawFbTest, CheckRawFbWithPartialConfig) -{ - // If FB has only one property, partial config is equivalent to custom config - StartUp(); - daq::FunctionBlockPtr rawFb; - auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, "")); - ASSERT_NO_THROW(rawFb = rootMqttFb.addFunctionBlock(RAW_FB_NAME, config)); -} - -TEST_F(MqttRawFbTest, CheckRawFbWithCustomConfig) -{ - // If FB has only one property, partial config is equivalent to custom config - StartUp(); - daq::FunctionBlockPtr rawFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(RAW_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_TOPIC, buildTopicName()); - ASSERT_NO_THROW(rawFb = rootMqttFb.addFunctionBlock(RAW_FB_NAME, config)); -} - -TEST_F(MqttRawFbTest, CheckRawFbConfig) -{ - StartUp(); - - const auto topic = buildTopicName(); - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(RAW_FB_NAME).createDefaultConfig(); - - config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); - daq::FunctionBlockPtr rawFb; - ASSERT_NO_THROW(rawFb = rootMqttFb.addFunctionBlock(RAW_FB_NAME, config)); - - const auto allProperties = rawFb.getAllProperties(); - ASSERT_EQ(allProperties.getCount(), config.getAllProperties().getCount()); - - for (const auto& pror : config.getAllProperties()) - { - const auto propName = pror.getName(); - ASSERT_TRUE(rawFb.hasProperty(propName)); - ASSERT_EQ(rawFb.getPropertyValue(propName), config.getPropertyValue(propName)); - } -} - -TEST_F(MqttRawFbTest, CheckRawFbSubscriptionStatusWaitingForData) -{ - StartUp(); - - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(RAW_FB_NAME).createDefaultConfig(); - - config.setPropertyValue(PROPERTY_NAME_TOPIC, buildTopicName()); - daq::FunctionBlockPtr rawFb; - ASSERT_NO_THROW(rawFb = rootMqttFb.addFunctionBlock(RAW_FB_NAME, config)); - EXPECT_EQ(rawFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - - EXPECT_EQ(rawFb.getStatusContainer().getStatus("SubscriptionStatus"), - EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttBaseFb::SubscriptionStatus::WaitingForData), - daqInstance.getContext().getTypeManager())); -} - -TEST_P(MqttRawFbPTest, CheckRawFbTopic) -{ - auto [topic, result] = GetParam(); - StartUp(); - - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(RAW_FB_NAME).createDefaultConfig(); - - config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); - daq::FunctionBlockPtr rawFb; - ASSERT_NO_THROW(rawFb = rootMqttFb.addFunctionBlock(RAW_FB_NAME, config)); - auto signals = rawFb.getSignals(); - ASSERT_EQ(signals.getCount(), 1); - const auto expectedComponentStatus = result ? "Ok" : "Warning"; - EXPECT_EQ(rawFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", expectedComponentStatus, daqInstance.getContext().getTypeManager())); - if (result) - { - EXPECT_NE(rawFb.getStatusContainer().getStatus("SubscriptionStatus"), - EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttBaseFb::SubscriptionStatus::InvalidTopicName), - daqInstance.getContext().getTypeManager())); - } - else - { - EXPECT_EQ(rawFb.getStatusContainer().getStatus("SubscriptionStatus"), - EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttBaseFb::SubscriptionStatus::InvalidTopicName), - daqInstance.getContext().getTypeManager())); - } -} - -INSTANTIATE_TEST_SUITE_P(TopicTest, - MqttRawFbPTest, - ::testing::Values(std::make_pair("", false), - std::make_pair("goodTopic/test", true), - std::make_pair("/goodTopic/test0", true), - std::make_pair("badTopic/+/test/topic", false), - std::make_pair("badTopic/+/+/topic", false), - std::make_pair("badTopic/#", false))); - -TEST_F(MqttRawFbTest, CheckRawFbDataTransfer) -{ - const auto topic = buildTopicName(); - const auto dataToSend = std::vector>{std::vector{0x01, 0x02, 0x03, 0x04, 0x05}, - std::vector{0x11, 0x12, 0x13, 0x14}, - std::vector{0x21, 0x22, 0x23, 0x24, 0x25, 0x26}, - std::vector{0x31}, - std::vector{0x41, 0x42, 0x43, 0x44, 0x45}}; - std::vector> dataToReceive; - - CreateRawFB({topic}); - - auto signalList = List(); - obj->getSignals(&signalList); - auto reader = daq::PacketReader(signalList[0]); - - for (const auto& data : dataToSend) - { - mqtt::MqttMessage msg = {topic, data, 1, 0}; - onSignalsMessage(msg); - } - - while (!reader.getEmpty()) - { - auto packet = reader.read(); - if (const auto eventPacket = packet.asPtrOrNull(); eventPacket.assigned()) - { - continue; - } - if (const auto dataPacket = packet.asPtrOrNull(); dataPacket.assigned()) - { - std::vector readData(dataPacket.getDataSize()); - memcpy(readData.data(), dataPacket.getData(), dataPacket.getDataSize()); - dataToReceive.push_back(std::move(readData)); - } - } - ASSERT_EQ(dataToSend.size(), dataToReceive.size()); - ASSERT_EQ(dataToSend, dataToReceive); -} - -TEST_F(MqttRawFbTest, CheckRawFbFullDataTransfer) -{ - const std::string topic = buildTopicName(); - - StartUp(); - - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(RAW_FB_NAME).createDefaultConfig(); - - config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); - auto singal = rootMqttFb.addFunctionBlock(RAW_FB_NAME, config).getSignals()[0]; - auto reader = daq::PacketReader(singal); - - MqttAsyncClientWrapper publisher("testPublisherId"); - ASSERT_TRUE(publisher.connect("127.0.0.1")); - - const auto dataToSend = std::vector>{std::vector{0x01, 0x02, 0x03, 0x04, 0x05}, - std::vector{0x11, 0x12, 0x13, 0x14}, - std::vector{0x21, 0x22, 0x23, 0x24, 0x25, 0x26}, - std::vector{0x31}, - std::vector{0x41, 0x42, 0x43, 0x44, 0x45}}; - std::vector> dataToReceive; - - for (const auto& data : dataToSend) - { - mqtt::MqttMessage msg = {topic, data, 1, 0}; - ASSERT_TRUE(publisher.publishMsg(msg)); - } - helper::utils::Timer tmr(3000, true); - while ((!reader.getEmpty() || !tmr.expired()) && dataToReceive.size() != dataToSend.size()) - { - auto packet = reader.read(); - if (const auto eventPacket = packet.asPtrOrNull(); eventPacket.assigned()) - { - continue; - } - if (const auto dataPacket = packet.asPtrOrNull(); dataPacket.assigned()) - { - std::vector readData(dataPacket.getDataSize()); - memcpy(readData.data(), dataPacket.getData(), dataPacket.getDataSize()); - dataToReceive.push_back(std::move(readData)); - } - } - - ASSERT_EQ(dataToSend.size(), dataToReceive.size()); - ASSERT_EQ(dataToSend, dataToReceive); -} - -TEST_F(MqttRawFbTest, CheckRawFbFullDataTransferWithReconfiguring) -{ - const std::string topic0 = buildTopicName("0"); - const std::string topic1 = buildTopicName("1"); - const auto dataToSend = std::vector>{std::vector{0x01, 0x02, 0x03, 0x04, 0x05}, - std::vector{0x11, 0x12, 0x13, 0x14}}; - std::vector> dataToReceive; - - StartUp(); - - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(RAW_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_TOPIC, topic0); - auto rawFB = rootMqttFb.addFunctionBlock(RAW_FB_NAME, config); - auto singal = rawFB.getSignals()[0]; - auto reader = daq::PacketReader(singal); - - const auto stHasData = EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttBaseFb::SubscriptionStatus::HasData), - daqInstance.getContext().getTypeManager()); - - const auto stWaitData = EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttBaseFb::SubscriptionStatus::WaitingForData), - daqInstance.getContext().getTypeManager()); - - MqttAsyncClientWrapper publisher("testPublisherId"); - ASSERT_TRUE(publisher.connect("127.0.0.1")); - EXPECT_EQ(rawFB.getStatusContainer().getStatus("SubscriptionStatus"), stWaitData); - - mqtt::MqttMessage msg = {topic0, dataToSend[0], 2, 0}; - ASSERT_TRUE(publisher.publishMsg(msg)); - - auto readerLambda = [&reader, &dataToReceive]() - { - while (!reader.getEmpty()) - { - auto packet = reader.read(); - if (const auto eventPacket = packet.asPtrOrNull(); eventPacket.assigned()) - { - continue; - } - if (const auto dataPacket = packet.asPtrOrNull(); dataPacket.assigned()) - { - std::vector readData(dataPacket.getDataSize()); - memcpy(readData.data(), dataPacket.getData(), dataPacket.getDataSize()); - dataToReceive.push_back(std::move(readData)); - } - } - }; - helper::utils::Timer tmr(1000, true); - - bool hasData = false; - while (tmr.expired() == false && hasData == false) - hasData = rawFB.getStatusContainer().getStatus("SubscriptionStatus") == stHasData; - - EXPECT_TRUE(hasData); - - readerLambda(); - ASSERT_EQ(dataToReceive.size(), 1u); - ASSERT_EQ(dataToSend[0], dataToReceive[0]); - - dataToReceive.clear(); - - ASSERT_NO_THROW(rawFB.setPropertyValue(PROPERTY_NAME_TOPIC, topic1)); - EXPECT_EQ(rawFB.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - EXPECT_EQ(rawFB.getStatusContainer().getStatus("SubscriptionStatus"), stWaitData); - - msg = {topic1, dataToSend[1], 2, 0}; - ASSERT_TRUE(publisher.publishMsg(msg)); - tmr.restart(); - - hasData = false; - while (tmr.expired() == false && hasData == false) - hasData = rawFB.getStatusContainer().getStatus("SubscriptionStatus") == stHasData; - - EXPECT_TRUE(hasData); - - readerLambda(); - ASSERT_EQ(dataToReceive.size(), 1u); - ASSERT_EQ(dataToSend[1], dataToReceive[0]); -} diff --git a/mqtt_streaming_module/tests/test_mqtt_root_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_root_fb.cpp index ef51020..418711b 100644 --- a/mqtt_streaming_module/tests/test_mqtt_root_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_root_fb.cpp @@ -131,8 +131,7 @@ TEST_F(MqttFbTest, CheckMqttFbFunctionalBlocks) StartUp(); daq::DictPtr fbTypes; ASSERT_NO_THROW(fbTypes = rootMqttFb.getAvailableFunctionBlockTypes()); - ASSERT_GE(fbTypes.getCount(), 3); - ASSERT_TRUE(fbTypes.hasKey(RAW_FB_NAME)); - ASSERT_TRUE(fbTypes.hasKey(JSON_FB_NAME)); + ASSERT_GE(fbTypes.getCount(), 2); + ASSERT_TRUE(fbTypes.hasKey(SUB_FB_NAME)); ASSERT_TRUE(fbTypes.hasKey(PUB_FB_NAME)); } diff --git a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp new file mode 100644 index 0000000..94f2364 --- /dev/null +++ b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp @@ -0,0 +1,591 @@ +#include "mqtt_streaming_module/mqtt_subscriber_fb_impl.h" +#include "test_daq_test_helper.h" +#include "test_data.h" +#include +#include +#include +#include +#include +#include +#include "MqttAsyncClientWrapper.h" +#include + +using namespace daq; +using namespace daq::modules::mqtt_streaming_module; + +namespace daq::modules::mqtt_streaming_module +{ +class MqttSubscriberFbHelper +{ +public: + std::unique_ptr obj; + + void CreateSubFB(std::string topic) + { + auto config = PropertyObject(); + config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, "")); + const auto fbType = FunctionBlockType(SUB_FB_NAME, SUB_FB_NAME, "", config); + config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); + obj = std::make_unique(NullContext(), nullptr, fbType, nullptr, config); + } + + auto getSignals() + { + auto signalList = List(); + obj->getSignals(&signalList); + return signalList; + } + + std::string buildTopicName(const std::string& postfix = "") + { + return std::string("test/topic/") + std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + postfix; + } + + std::string buildClientId() + { + return std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + "_ClientId"; + } + + void onSignalsMessage(mqtt::MqttMessage& msg) + { + mqtt::MqttAsyncClient unused; + obj->onSignalsMessage(unused, msg); + } +}; + +class MqttSubscriberFbTest : public testing::Test, public DaqTestHelper, public MqttSubscriberFbHelper +{ +}; + +class MqttSubscriberFbTopicPTest : public ::testing::TestWithParam>, + public DaqTestHelper, + public MqttSubscriberFbHelper +{ +}; + +class MqttSubscriberFbConfigPTest : public ::testing::TestWithParam, + public DaqTestHelper, + public MqttSubscriberFbHelper +{ +}; + +class MqttSubscriberFbConfigFilePTest : public ::testing::TestWithParam, + public DaqTestHelper, + public MqttSubscriberFbHelper +{ +}; +} // namespace daq::modules::mqtt_streaming_module + +TEST_F(MqttSubscriberFbTest, DefaultConfig) +{ + StartUp(); + daq::DictPtr fbTypes; + daq::FunctionBlockTypePtr fbt; + daq::PropertyObjectPtr defaultConfig; + ASSERT_NO_THROW(fbTypes = rootMqttFb.getAvailableFunctionBlockTypes()); + ASSERT_NO_THROW(fbt = fbTypes.get(SUB_FB_NAME)); + ASSERT_NO_THROW(defaultConfig = fbt.createDefaultConfig()); + + ASSERT_TRUE(defaultConfig.assigned()); + + ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 4u); + + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_JSON_CONFIG)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_JSON_CONFIG).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_JSON_CONFIG).asPtr().getLength(), 0u); + + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_JSON_CONFIG_FILE)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_JSON_CONFIG_FILE).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE).asPtr().getLength(), 0u); + + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_TOPIC)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_TOPIC).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().getLength(), 0u); + + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_QOS)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_QOS).getValueType(), CoreType::ctInt); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_SUB_QOS).asPtr().getValue(DEFAULT_SUB_QOS), DEFAULT_SUB_QOS); +} + +TEST_F(MqttSubscriberFbTest, Config) +{ + StartUp(); + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + + config.setPropertyValue(PROPERTY_NAME_TOPIC, buildTopicName()); + daq::FunctionBlockPtr subFb; + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + + const auto allProperties = subFb.getAllProperties(); + ASSERT_EQ(allProperties.getCount(), config.getAllProperties().getCount()); + + for (const auto& pror : config.getAllProperties()) + { + const auto propName = pror.getName(); + ASSERT_TRUE(subFb.hasProperty(propName)); + ASSERT_EQ(subFb.getPropertyValue(propName), config.getPropertyValue(propName)); + } +} + +TEST_F(MqttSubscriberFbTest, CreationWithDefaultConfig) +{ + StartUp(); + daq::FunctionBlockPtr subFb; + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME)); + EXPECT_EQ(subFb.getSignals().getCount(), 1u); + ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Warning", daqInstance.getContext().getTypeManager())); +} + +TEST_F(MqttSubscriberFbTest, CreationWithPartialConfig) +{ + // If FB has only one property, partial config is equivalent to custom config + StartUp(); + daq::FunctionBlockPtr subFb; + auto config = PropertyObject(); + config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, String(buildTopicName()))); + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + EXPECT_EQ(subFb.getSignals().getCount(), 1u); + ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); +} + +TEST_F(MqttSubscriberFbTest, CreationWithCustomConfig) +{ + // If FB has only one property, partial config is equivalent to custom config + StartUp(); + daq::FunctionBlockPtr subFb; + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_TOPIC, String(buildTopicName())); + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + EXPECT_EQ(subFb.getSignals().getCount(), 1u); + ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); +} + +TEST_F(MqttSubscriberFbTest, SubscriptionStatusWaitingForData) +{ + StartUp(); + + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + + config.setPropertyValue(PROPERTY_NAME_TOPIC, buildTopicName()); + daq::FunctionBlockPtr subFb; + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + EXPECT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); + + EXPECT_EQ(subFb.getStatusContainer().getStatus("SubscriptionStatus"), + EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, + static_cast(MqttSubscriberFbImpl::SubscriptionStatus::WaitingForData), + daqInstance.getContext().getTypeManager())); +} + +TEST_P(MqttSubscriberFbTopicPTest, CheckSubscriberFbTopic) +{ + auto [topic, result] = GetParam(); + StartUp(); + + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + + config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); + daq::FunctionBlockPtr fb; + ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + auto signals = fb.getSignals(); + ASSERT_EQ(signals.getCount(), 1); + const auto expectedComponentStatus = result ? "Ok" : "Warning"; + EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", expectedComponentStatus, daqInstance.getContext().getTypeManager())); + if (result) + { + EXPECT_NE(fb.getStatusContainer().getStatus("SubscriptionStatus"), + EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, + static_cast(MqttSubscriberFbImpl::SubscriptionStatus::InvalidTopicName), + daqInstance.getContext().getTypeManager())); + } + else + { + EXPECT_EQ(fb.getStatusContainer().getStatus("SubscriptionStatus"), + EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, + static_cast(MqttSubscriberFbImpl::SubscriptionStatus::InvalidTopicName), + daqInstance.getContext().getTypeManager())); + } +} + +INSTANTIATE_TEST_SUITE_P(TopicTest, + MqttSubscriberFbTopicPTest, + ::testing::Values(std::make_pair("", false), + std::make_pair("goodTopic/test", true), + std::make_pair("/goodTopic/test0", true), + std::make_pair("badTopic/+/test/topic", false), + std::make_pair("badTopic/+/+/topic", false), + std::make_pair("badTopic/#", false))); + +TEST_F(MqttSubscriberFbTest, RemovingNestedFunctionBlock) +{ + StartUp(); + daq::FunctionBlockPtr subFb; + { + auto config = PropertyObject(); + config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, String(buildTopicName()))); + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); + } + daq::FunctionBlockPtr jsonDecoderFb; + { + auto config = PropertyObject(); + config.addProperty(StringProperty(PROPERTY_NAME_VALUE_NAME, String("temp"))); + ASSERT_NO_THROW(jsonDecoderFb = subFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); + } + ASSERT_EQ(subFb.getFunctionBlocks().getCount(), 1u); + + ASSERT_NO_THROW(subFb.removeFunctionBlock(jsonDecoderFb)); + ASSERT_EQ(subFb.getFunctionBlocks().getCount(), 0u); +} + +TEST_F(MqttSubscriberFbTest, TwoFbCreation) +{ + StartUp(); + { + daq::FunctionBlockPtr fb; + auto config = PropertyObject(); + config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, buildTopicName("0"))); + ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); + } + { + daq::FunctionBlockPtr fb; + auto config = PropertyObject(); + config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, buildTopicName("1"))); + ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); + } + auto fbs = rootMqttFb.getFunctionBlocks(); + ASSERT_EQ(fbs.getCount(), 2u); +} + +TEST_F(MqttSubscriberFbTest, PropertyChanged) +{ + StartUp(); + + daq::FunctionBlockPtr fb; + auto config = PropertyObject(); + auto topic = buildTopicName("0"); + config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, topic)); + ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); + auto subFb = reinterpret_cast(*fb); + + ASSERT_EQ(topic, subFb->getSubscribedTopic()); + topic = buildTopicName("1"); + fb.setPropertyValue(PROPERTY_NAME_TOPIC, topic); + ASSERT_EQ(topic, subFb->getSubscribedTopic()); +} + +TEST_F(MqttSubscriberFbTest, JsonInit0) +{ + StartUp(); + daq::FunctionBlockPtr subFb; + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG, String(VALID_JSON_1_TOPIC_0)); + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_EQ(subFb.getFunctionBlocks().getCount(), 3u); + ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); + auto lambda = [&](FunctionBlockPtr nestedFb, std::string value, std::string ts, std::string symbol) + { + EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); + if (!symbol.empty()) + EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_TS_NAME).asPtr().toStdString(), ts); + }; + EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "openDAQ/RefDev0/IO/AI/RefCh0/Sig/AI0"); + + lambda(subFb.getFunctionBlocks()[0], "value", "timestamp", "V"); + lambda(subFb.getFunctionBlocks()[1], "value1", "", ""); + lambda(subFb.getFunctionBlocks()[2], "value2", "", "W"); + +} + +TEST_F(MqttSubscriberFbTest, JsonInit1) +{ + StartUp(); + daq::FunctionBlockPtr subFb; + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG, String(VALID_JSON_1_TOPIC_1)); + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_EQ(subFb.getFunctionBlocks().getCount(), 3u); + ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); + auto lambda = [&](FunctionBlockPtr nestedFb, std::string value, std::string ts, std::string symbol) + { + EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); + if (!symbol.empty()) + EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_TS_NAME).asPtr().toStdString(), ts); + }; + EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "/mirip/UNet3AC2/sensor/data"); + + lambda(subFb.getFunctionBlocks()[0], "temp", "ts", "°C"); + lambda(subFb.getFunctionBlocks()[1], "humi", "ts", "%"); + lambda(subFb.getFunctionBlocks()[2], "tds_value", "ts", "ppm"); + +} + +TEST_P(MqttSubscriberFbConfigPTest, JsonWrongInit) +{ + const auto configJson = GetParam(); + StartUp(); + daq::FunctionBlockPtr subFb; + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG, String(configJson)); + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + EXPECT_EQ(subFb.getFunctionBlocks().getCount(), 0u); + EXPECT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); + EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), ""); +} + +INSTANTIATE_TEST_SUITE_P( + JsonConfigTest, + MqttSubscriberFbConfigPTest, + ::testing::Values( + VALID_JSON_3_TOPIC_2, + WILDCARD_JSON_0, + WILDCARD_JSON_1, + INVALID_JSON_1, + INVALID_JSON_3)); + +TEST_P(MqttSubscriberFbConfigFilePTest, JsonInitFromFile) +{ + const auto configJson = GetParam(); + StartUp(); + daq::FunctionBlockPtr subFb; + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE, String(configJson)); + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); +} + +INSTANTIATE_TEST_SUITE_P(JsonConfigTest, + MqttSubscriberFbConfigFilePTest, + ::testing::Values("data/public-example0.json", + "data/public-example1.json", + "data/public-example2.json", + "data/public-example3.json")); + +TEST_F(MqttSubscriberFbTest, JsonInitFromFileWithChecking) +{ + StartUp(); + daq::FunctionBlockPtr subFb; + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE, String("data/public-example0.json")); + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); + ASSERT_EQ(subFb.getFunctionBlocks().getCount(), 3u); + auto lambda = [&](FunctionBlockPtr nestedFb, std::string value, std::string ts, std::string symbol) + { + EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); + if (!symbol.empty()) + EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_TS_NAME).asPtr().toStdString(), ts); + }; + EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "/mirip/UNet3AC2/sensor/data"); + + lambda(subFb.getFunctionBlocks()[0], "temp", "ts", "°C"); + lambda(subFb.getFunctionBlocks()[1], "humi", "ts", "%"); + lambda(subFb.getFunctionBlocks()[2], "tds_value", "ts", "ppm"); + +} + +TEST_F(MqttSubscriberFbTest, JsonInitFromFileWrongPath) +{ + StartUp(); + daq::FunctionBlockPtr subFb; + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE, String("/justWrongPath/wrongFile.txt")); + ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + EXPECT_EQ(subFb.getFunctionBlocks().getCount(), 0u); + EXPECT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); + EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), ""); +} + +TEST_F(MqttSubscriberFbTest, DataTransfer) +{ + const auto topic = buildTopicName(); + const auto dataToSend = std::vector>{std::vector{0x01, 0x02, 0x03, 0x04, 0x05}, + std::vector{0x11, 0x12, 0x13, 0x14}, + std::vector{0x21, 0x22, 0x23, 0x24, 0x25, 0x26}, + std::vector{0x31}, + std::vector{0x41, 0x42, 0x43, 0x44, 0x45}}; + std::vector> dataToReceive; + + CreateSubFB({topic}); + + auto signalList = List(); + obj->getSignals(&signalList); + auto reader = daq::PacketReader(signalList[0]); + + for (const auto& data : dataToSend) + { + mqtt::MqttMessage msg = {topic, data, 1, 0}; + onSignalsMessage(msg); + } + + while (!reader.getEmpty()) + { + auto packet = reader.read(); + if (const auto eventPacket = packet.asPtrOrNull(); eventPacket.assigned()) + { + continue; + } + if (const auto dataPacket = packet.asPtrOrNull(); dataPacket.assigned()) + { + std::vector readData(dataPacket.getDataSize()); + memcpy(readData.data(), dataPacket.getData(), dataPacket.getDataSize()); + dataToReceive.push_back(std::move(readData)); + } + } + ASSERT_EQ(dataToSend.size(), dataToReceive.size()); + ASSERT_EQ(dataToSend, dataToReceive); +} + +TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransfer) +{ + const std::string topic = buildTopicName(); + + StartUp(); + + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + + config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); + auto singal = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config).getSignals()[0]; + auto reader = daq::PacketReader(singal); + + MqttAsyncClientWrapper publisher("testPublisherId"); + ASSERT_TRUE(publisher.connect("127.0.0.1")); + + const auto dataToSend = std::vector>{std::vector{0x01, 0x02, 0x03, 0x04, 0x05}, + std::vector{0x11, 0x12, 0x13, 0x14}, + std::vector{0x21, 0x22, 0x23, 0x24, 0x25, 0x26}, + std::vector{0x31}, + std::vector{0x41, 0x42, 0x43, 0x44, 0x45}}; + std::vector> dataToReceive; + + for (const auto& data : dataToSend) + { + mqtt::MqttMessage msg = {topic, data, 1, 0}; + ASSERT_TRUE(publisher.publishMsg(msg)); + } + helper::utils::Timer tmr(3000, true); + while ((!reader.getEmpty() || !tmr.expired()) && dataToReceive.size() != dataToSend.size()) + { + auto packet = reader.read(); + if (const auto eventPacket = packet.asPtrOrNull(); eventPacket.assigned()) + { + continue; + } + if (const auto dataPacket = packet.asPtrOrNull(); dataPacket.assigned()) + { + std::vector readData(dataPacket.getDataSize()); + memcpy(readData.data(), dataPacket.getData(), dataPacket.getDataSize()); + dataToReceive.push_back(std::move(readData)); + } + } + + ASSERT_EQ(dataToSend.size(), dataToReceive.size()); + ASSERT_EQ(dataToSend, dataToReceive); +} + +TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransferWithReconfiguring) +{ + const std::string topic0 = buildTopicName("0"); + const std::string topic1 = buildTopicName("1"); + const auto dataToSend = std::vector>{std::vector{0x01, 0x02, 0x03, 0x04, 0x05}, + std::vector{0x11, 0x12, 0x13, 0x14}}; + std::vector> dataToReceive; + + StartUp(); + + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_TOPIC, topic0); + auto rawFB = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config); + auto singal = rawFB.getSignals()[0]; + auto reader = daq::PacketReader(singal); + + const auto stHasData = EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, + static_cast(MqttSubscriberFbImpl::SubscriptionStatus::HasData), + daqInstance.getContext().getTypeManager()); + + const auto stWaitData = EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, + static_cast(MqttSubscriberFbImpl::SubscriptionStatus::WaitingForData), + daqInstance.getContext().getTypeManager()); + + MqttAsyncClientWrapper publisher("testPublisherId"); + ASSERT_TRUE(publisher.connect("127.0.0.1")); + EXPECT_EQ(rawFB.getStatusContainer().getStatus("SubscriptionStatus"), stWaitData); + + mqtt::MqttMessage msg = {topic0, dataToSend[0], 2, 0}; + ASSERT_TRUE(publisher.publishMsg(msg)); + + auto readerLambda = [&reader, &dataToReceive]() + { + while (!reader.getEmpty()) + { + auto packet = reader.read(); + if (const auto eventPacket = packet.asPtrOrNull(); eventPacket.assigned()) + { + continue; + } + if (const auto dataPacket = packet.asPtrOrNull(); dataPacket.assigned()) + { + std::vector readData(dataPacket.getDataSize()); + memcpy(readData.data(), dataPacket.getData(), dataPacket.getDataSize()); + dataToReceive.push_back(std::move(readData)); + } + } + }; + helper::utils::Timer tmr(1000, true); + + bool hasData = false; + while (tmr.expired() == false && hasData == false) + hasData = rawFB.getStatusContainer().getStatus("SubscriptionStatus") == stHasData; + + EXPECT_TRUE(hasData); + + readerLambda(); + ASSERT_EQ(dataToReceive.size(), 1u); + ASSERT_EQ(dataToSend[0], dataToReceive[0]); + + dataToReceive.clear(); + + ASSERT_NO_THROW(rawFB.setPropertyValue(PROPERTY_NAME_TOPIC, topic1)); + EXPECT_EQ(rawFB.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); + EXPECT_EQ(rawFB.getStatusContainer().getStatus("SubscriptionStatus"), stWaitData); + + msg = {topic1, dataToSend[1], 2, 0}; + ASSERT_TRUE(publisher.publishMsg(msg)); + tmr.restart(); + + hasData = false; + while (tmr.expired() == false && hasData == false) + hasData = rawFB.getStatusContainer().getStatus("SubscriptionStatus") == stHasData; + + EXPECT_TRUE(hasData); + + readerLambda(); + ASSERT_EQ(dataToReceive.size(), 1u); + ASSERT_EQ(dataToSend[1], dataToReceive[0]); +} + From bb3b570503e0303c9e34d06e0f325c4e120fc61f Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Thu, 15 Jan 2026 15:05:56 +0100 Subject: [PATCH 07/49] mqtt: Topics property for publisherFB --- .../atomic_signal_atomic_sample_handler.h | 2 +- .../include/mqtt_streaming_module/constants.h | 1 + .../group_signal_shared_ts_handler.h | 1 + .../mqtt_streaming_module/handler_base.h | 1 + .../mqtt_publisher_fb_impl.h | 1 + .../signal_arr_atomic_sample_handler.h | 1 + .../atomic_signal_atomic_sample_handler.cpp | 13 +++++++ .../src/group_signal_shared_ts_handler.cpp | 6 ++++ .../src/mqtt_publisher_fb_impl.cpp | 25 ++++++++++++-- .../src/signal_arr_atomic_sample_handler.cpp | 6 ++++ .../tests/test_mqtt_publisher_fb.cpp | 34 ++++++++++++++++++- 11 files changed, 86 insertions(+), 5 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h index 0359996..b438e95 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h @@ -31,7 +31,7 @@ class AtomicSignalAtomicSampleHandler : public HandlerBase { return ProcedureStatus{true, {}}; }; - + ListPtr getTopics(const std::vector& signalContexts) override; protected: bool useSignalNames; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index 0a4b34f..e0ed634 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -45,6 +45,7 @@ static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE = "GroupVa static constexpr const char* PROPERTY_NAME_PUB_QOS = "QoS"; static constexpr const char* PROPERTY_NAME_SUB_QOS = "QoS"; static constexpr const char* PROPERTY_NAME_PUB_READ_PERIOD = "ReaderPeriod"; +static constexpr const char* PROPERTY_NAME_PUB_TOPICS = "Topics"; static constexpr const char* SUB_FB_NAME = "MQTTSubscriberFB"; static constexpr const char* PUB_FB_NAME = "PublisherMQTTFB"; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h index a182110..a9ee6a1 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h @@ -38,6 +38,7 @@ class GroupSignalSharedTsHandler : public HandlerBase MqttData processSignalContexts(std::vector& signalContexts) override; ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const override; ProcedureStatus signalListChanged(std::vector& signalContexts) override; + ListPtr getTopics(const std::vector& signalContexts) override; protected: bool useSignalNames; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h index 41666e4..4288795 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h @@ -30,6 +30,7 @@ class HandlerBase virtual MqttData processSignalContexts(std::vector& signalContexts) = 0; virtual ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const = 0; virtual ProcedureStatus signalListChanged(std::vector& signalContexts) = 0; + virtual ListPtr getTopics(const std::vector& signalContexts) = 0; protected: static std::pair calculateRatio(const DataDescriptorPtr descriptor) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h index a687dae..804e9c7 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h @@ -104,6 +104,7 @@ class MqttPublisherFbImpl final : public FunctionBlock void updateInputPorts(); void updateStatuses(); void validateInputPorts(); + void updateTopics(); template retT readProperty(const std::string& propertyName, const retT defaultValue); void runReaderThread(); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h index e16b43c..b3b3e86 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h @@ -29,6 +29,7 @@ class SignalArrayAtomicSampleHandler : public HandlerBase MqttData processSignalContexts(std::vector& signalContexts) override; ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const override; ProcedureStatus signalListChanged(std::vector& signalContexts) override; + ListPtr getTopics(const std::vector& signalContexts) override; protected: bool useSignalNames; diff --git a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp index 53fdf6b..ada14af 100644 --- a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp @@ -152,4 +152,17 @@ MqttDataSample AtomicSignalAtomicSampleHandler::processDataPacket(SignalContext& return MqttDataSample{topic, msg}; } +ListPtr AtomicSignalAtomicSampleHandler::getTopics(const std::vector& signalContexts) +{ + auto res = List(); + for (const auto& sigCtx : signalContexts) + { + if (!sigCtx.inputPort.getConnection().assigned()) + continue; + auto t = buildTopicName(sigCtx); + res.pushBack(String(t)); + } + return res; +}; + END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp index be4c5be..c3b5457 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp @@ -169,6 +169,12 @@ ProcedureStatus GroupSignalSharedTsHandler::signalListChanged(std::vector GroupSignalSharedTsHandler::getTopics(const std::vector& signalContexts) +{ + auto res = List(String(buildTopicName())); + return res; +} + std::string GroupSignalSharedTsHandler::toString(const SampleType sampleType, const std::string& valueFieldName, void* data, SizeT offset) { switch (sampleType) diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index f693d1d..0b7b465 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -151,6 +151,7 @@ void MqttPublisherFbImpl::onConnected(const InputPortPtr& inputPort) updateInputPorts(); LOG_T("Connected to port {}", inputPort.getLocalId()); validateInputPorts(); + updateTopics(); updateStatuses(); } @@ -161,6 +162,7 @@ void MqttPublisherFbImpl::onDisconnected(const InputPortPtr& inputPort) updateInputPorts(); LOG_T("Disconnected from port {}", inputPort.getLocalId()); validateInputPorts(); + updateTopics(); updateStatuses(); } @@ -264,6 +266,12 @@ void MqttPublisherFbImpl::validateInputPorts() } } +void MqttPublisherFbImpl::updateTopics() +{ + const auto topics = handler->getTopics(signalContexts); + objPtr.getProperty(PROPERTY_NAME_PUB_TOPICS).asPtr().setValueProtected(topics); +} + void MqttPublisherFbImpl::initProperties(const PropertyObjectPtr& config) { for (const auto& prop : config.getAllProperties()) @@ -275,15 +283,25 @@ void MqttPublisherFbImpl::initProperties(const PropertyObjectPtr& config) { objPtr.addProperty(internalProp.clone()); objPtr.setPropertyValue(propName, prop.getValue()); - objPtr.getOnPropertyValueWrite(prop.getName()) += - [this](PropertyObjectPtr& obj, PropertyValueEventArgsPtr& args) { propertyChanged(); }; + objPtr.getOnPropertyValueWrite(prop.getName()) += [this](PropertyObjectPtr& obj, PropertyValueEventArgsPtr& args) + { propertyChanged(); }; } } else { objPtr.setPropertyValue(propName, prop.getValue()); } - } + + if (propName == PROPERTY_NAME_PUB_TOPIC_NAME) + { + auto builder = ListPropertyBuilder(PROPERTY_NAME_PUB_TOPICS, List()) + .setReadOnly(true) + .setVisible(EvalValue(std::string("$") + PROPERTY_NAME_PUB_TOPIC_MODE + " == 0")) + .setDescription("List of currently used MQTT topics for publishing."); + + objPtr.addProperty(builder.build()); + } + } readProperties(); } @@ -349,6 +367,7 @@ void MqttPublisherFbImpl::propertyChanged() readProperties(); handler = HandlerFactory::create(this->config, globalId.toStdString()); validateInputPorts(); + updateTopics(); updateStatuses(); } diff --git a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp index 44882e8..34848db 100644 --- a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp @@ -122,6 +122,12 @@ ProcedureStatus SignalArrayAtomicSampleHandler::signalListChanged(std::vector SignalArrayAtomicSampleHandler::getTopics(const std::vector& signalContexts) +{ + auto res = List(String(buildTopicName())); + return res; +} + std::string SignalArrayAtomicSampleHandler::toString(const std::string valueFieldName, daq::DataPacketPtr packet) { std::string result; diff --git a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp index d1c9e44..16500ca 100644 --- a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp @@ -613,7 +613,7 @@ TEST_F(MqttPublisherFbTest, Config) static_cast(MqttPublisherFbImpl::SettingStatus::Valid), daqInstance.getContext().getTypeManager())); const auto allProperties = fb.getAllProperties(); - ASSERT_EQ(allProperties.getCount(), config.getAllProperties().getCount()); + ASSERT_EQ(allProperties.getCount(), config.getAllProperties().getCount() + 1); // +1 for Topics property for (const auto& pror : config.getAllProperties()) { @@ -828,6 +828,38 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) } } +TEST_F(MqttPublisherFbTest, TopicsList) +{ + StartUp(); + + { + daq::FunctionBlockPtr fb; + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0); + ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + auto help = SignalHelper(); + + ASSERT_NO_THROW(fb.getInputPorts()[0].connect(help.signal0)); + ASSERT_EQ(fb.getPropertyValue(PROPERTY_NAME_PUB_TOPICS).asPtr().getCount(), 1u); + ASSERT_NO_THROW(fb.getInputPorts()[1].connect(help.signal1)); + ASSERT_EQ(fb.getPropertyValue(PROPERTY_NAME_PUB_TOPICS).asPtr().getCount(), 2u); + ASSERT_TRUE(fb.getProperty(PROPERTY_NAME_PUB_TOPICS).getVisible()); + } + + { + daq::FunctionBlockPtr fb; + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); + ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + auto help = SignalHelper(); + + ASSERT_NO_THROW(fb.getInputPorts()[0].connect(help.signal0)); + ASSERT_EQ(fb.getPropertyValue(PROPERTY_NAME_PUB_TOPICS).asPtr().getCount(), 1u); + ASSERT_NO_THROW(fb.getInputPorts()[1].connect(help.signal1)); + ASSERT_EQ(fb.getPropertyValue(PROPERTY_NAME_PUB_TOPICS).asPtr().getCount(), 1u); + ASSERT_FALSE(fb.getProperty(PROPERTY_NAME_PUB_TOPICS).getVisible()); + } +} TEST_F(MqttPublisherFbTest, WrongConfig) { From 8d90647464be8abba20205898294880d83b85f9b Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Thu, 15 Jan 2026 15:16:29 +0100 Subject: [PATCH 08/49] mqtt: renaming; removing unused code --- .../custom-mqtt-sub/src/custom-mqtt-sub.cpp | 2 +- examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp | 2 +- .../ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp | 8 +- .../include/mqtt_streaming_module/constants.h | 47 ++++++----- .../src/mqtt_json_decoder_fb_impl.cpp | 14 ++-- .../src/mqtt_root_fb_impl.cpp | 30 +++---- .../src/mqtt_subscriber_fb_impl.cpp | 32 +++---- .../tests/test_daq_test_helper.h | 12 +-- .../tests/test_mqtt_json_decoder_fb.cpp | 83 +++++------------- .../tests/test_mqtt_root_fb.cpp | 56 ++++++------- .../tests/test_mqtt_streaming_module.cpp | 4 +- .../tests/test_mqtt_subscriber_fb.cpp | 84 +++++++++---------- 12 files changed, 166 insertions(+), 208 deletions(-) diff --git a/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp b/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp index b98bb44..9558dfb 100644 --- a/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp +++ b/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp @@ -125,7 +125,7 @@ int main(int argc, char* argv[]) const InstancePtr instance = InstanceBuilder().addModulePath(MODULE_PATH).build(); const std::string clientFbName = "MQTTClientFB"; auto clientFbConfig = instance.getAvailableFunctionBlockTypes().get(clientFbName).createDefaultConfig(); - clientFbConfig.setPropertyValue("MQTTBrokerAddress", appConfig.brokerAddress); + clientFbConfig.setPropertyValue("BrokerAddress", appConfig.brokerAddress); auto brokerFB = instance.addFunctionBlock(clientFbName, clientFbConfig); auto availableFbs = brokerFB.getAvailableFunctionBlockTypes(); diff --git a/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp b/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp index d7452b9..21f0077 100644 --- a/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp +++ b/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp @@ -57,7 +57,7 @@ int main(int argc, char* argv[]) const InstancePtr instance = InstanceBuilder().addModulePath(MODULE_PATH).build(); const std::string clientFbName = "MQTTClientFB"; auto clientFbConfig = instance.getAvailableFunctionBlockTypes().get(clientFbName).createDefaultConfig(); - clientFbConfig.setPropertyValue("MQTTBrokerAddress", appConfig.brokerAddress); + clientFbConfig.setPropertyValue("BrokerAddress", appConfig.brokerAddress); auto brokerFB = instance.addFunctionBlock(clientFbName, clientFbConfig); auto availableFbs = brokerFB.getAvailableFunctionBlockTypes(); diff --git a/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp b/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp index 4b9fc52..755f2fe 100644 --- a/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp +++ b/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp @@ -91,15 +91,15 @@ int main(int argc, char* argv[]) // Create and configure MQTT server const std::string rootFbName = "MQTTClientFB"; auto rootFbConfig = instance.getAvailableFunctionBlockTypes().get(rootFbName).createDefaultConfig(); - rootFbConfig.setPropertyValue("MQTTBrokerAddress", appConfig.brokerAddress); + rootFbConfig.setPropertyValue("BrokerAddress", appConfig.brokerAddress); auto brokerFB = instance.addFunctionBlock(rootFbName, rootFbConfig); auto availableFbs = brokerFB.getAvailableFunctionBlockTypes(); - const std::string fbName = "PublisherMQTTFB"; + const std::string fbName = "MQTTJSONPublisherFB"; std::cout << "Try to add the " << fbName << std::endl; auto config = availableFbs.get(fbName).createDefaultConfig(); config.setPropertyValue("QoS", 1); - config.setPropertyValue("ReaderPeriod", 20); + config.setPropertyValue("ReaderWaitPeriod", 20); config.setPropertyValue("UseSignalNames", True); switch (appConfig.mode) { case Mode::ATOMIC_SIGNAL_ATOMIC_SAMPLE: @@ -111,7 +111,7 @@ int main(int argc, char* argv[]) config.setPropertyValue("SharedTimestamp", False); config.setPropertyValue("TopicMode", 0); config.setPropertyValue("GroupValues", True); - config.setPropertyValue("GroupValuesPackSize", 3); + config.setPropertyValue("SamplesPerMessage", 3); break; case Mode::SIGNAL_ARRAY_ATOMIC_SAMPLE: config.setPropertyValue("SharedTimestamp", False); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index e0ed634..4c4b419 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -22,43 +22,44 @@ static constexpr uint32_t DEFAULT_PUB_PACK_SIZE = 1; static constexpr const char* DEFAULT_VALUE_SIGNAL_LOCAL_ID = "MQTTValueSignal"; static constexpr const char* DEFAULT_TS_SIGNAL_LOCAL_ID = "MQTTTimestampSignal"; -static constexpr const char* PROPERTY_NAME_MQTT_BROKER_ADDRESS = "MQTTBrokerAddress"; -static constexpr const char* PROPERTY_NAME_MQTT_BROKER_PORT = "MQTTBrokerPort"; -static constexpr const char* PROPERTY_NAME_MQTT_USERNAME = "MQTTUsername"; -static constexpr const char* PROPERTY_NAME_MQTT_PASSWORD = "MQTTPassword"; -static constexpr const char* PROPERTY_NAME_CONNECT_TIMEOUT = "ConnectTimeout"; -static constexpr const char* PROPERTY_NAME_SIGNAL_LIST = "SignalList"; -static constexpr const char* PROPERTY_NAME_JSON_CONFIG = "JSONConfig"; -static constexpr const char* PROPERTY_NAME_JSON_CONFIG_FILE = "JSONConfigFile"; -static constexpr const char* PROPERTY_NAME_TOPIC = "Topic"; -static constexpr const char* PROPERTY_NAME_VALUE_NAME = "ValueName"; -static constexpr const char* PROPERTY_NAME_TS_NAME = "TimestampName"; -static constexpr const char* PROPERTY_NAME_UNIT = "Unit"; +static constexpr const char* PROPERTY_NAME_CLIENT_BROKER_ADDRESS = "BrokerAddress"; +static constexpr const char* PROPERTY_NAME_CLIENT_BROKER_PORT = "BrokerPort"; +static constexpr const char* PROPERTY_NAME_CLIENT_USERNAME = "Username"; +static constexpr const char* PROPERTY_NAME_CLIENT_PASSWORD = "Password"; +static constexpr const char* PROPERTY_NAME_CLIENT_CONNECT_TIMEOUT = "ConnectionTimeout"; + +static constexpr const char* PROPERTY_NAME_SUB_JSON_CONFIG = "JSONConfig"; +static constexpr const char* PROPERTY_NAME_SUB_JSON_CONFIG_FILE = "JSONConfigFile"; +static constexpr const char* PROPERTY_NAME_SUB_QOS = "QoS"; +static constexpr const char* PROPERTY_NAME_SUB_TOPIC = "Topic"; + +static constexpr const char* PROPERTY_NAME_DEC_VALUE_NAME = "ValueKey"; +static constexpr const char* PROPERTY_NAME_DEC_TS_NAME = "DomainKey"; +static constexpr const char* PROPERTY_NAME_DEC_UNIT = "Unit"; static constexpr const char* PROPERTY_NAME_PUB_TOPIC_MODE = "TopicMode"; static constexpr const char* PROPERTY_NAME_PUB_TOPIC_NAME = "Topic"; static constexpr const char* PROPERTY_NAME_PUB_SHARED_TS = "SharedTimestamp"; static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES = "GroupValues"; static constexpr const char* PROPERTY_NAME_PUB_USE_SIGNAL_NAMES = "UseSignalNames"; -static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE = "GroupValuesPackSize"; +static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE = "SamplesPerMessage"; static constexpr const char* PROPERTY_NAME_PUB_QOS = "QoS"; -static constexpr const char* PROPERTY_NAME_SUB_QOS = "QoS"; -static constexpr const char* PROPERTY_NAME_PUB_READ_PERIOD = "ReaderPeriod"; +static constexpr const char* PROPERTY_NAME_PUB_READ_PERIOD = "ReaderWaitPeriod"; static constexpr const char* PROPERTY_NAME_PUB_TOPICS = "Topics"; static constexpr const char* SUB_FB_NAME = "MQTTSubscriberFB"; -static constexpr const char* PUB_FB_NAME = "PublisherMQTTFB"; -static constexpr const char* ROOT_FB_NAME = "MQTTClientFB"; -static constexpr const char* JSON_DECODER_FB_NAME = "JSONDecoderMQTTTFB"; +static constexpr const char* PUB_FB_NAME = "MQTTJSONPublisherFB"; +static constexpr const char* CLIENT_FB_NAME = "MQTTClientFB"; +static constexpr const char* JSON_DECODER_FB_NAME = "MQTTJSONDecoderFB"; -static const char* MQTT_LOCAL_ROOT_FB_ID_PREFIX = "MQTTClientFB"; -static const char* MQTT_LOCAL_PUB_FB_ID_PREFIX = "PublisherMQTTFB"; +static const char* MQTT_LOCAL_CLIENT_FB_ID_PREFIX = "MQTTClientFB"; +static const char* MQTT_LOCAL_PUB_FB_ID_PREFIX = "MQTTJSONPublisherFB"; static const char* MQTT_LOCAL_SUB_FB_ID_PREFIX = "MQTTSubscriberFB"; -static const char* MQTT_LOCAL_JSON_DECODER_FB_ID_PREFIX = "JSONDecoderMQTTTFB"; +static const char* MQTT_LOCAL_JSON_DECODER_FB_ID_PREFIX = "MQTTJSONDecoderFB"; -static const char* MQTT_ROOT_FB_CON_STATUS_TYPE = "BrokerConnectionStatusType"; +static const char* MQTT_CLIENT_FB_CON_STATUS_TYPE = "BrokerConnectionStatusType"; static const char* MQTT_FB_SUB_STATUS_TYPE = "MQTTSubscriptionStatusType"; static const char* MQTT_PUB_FB_SIG_STATUS_TYPE = "MQTTSignalStatusType"; static const char* MQTT_PUB_FB_PUB_STATUS_TYPE = "MQTTPublishingStatusType"; @@ -66,7 +67,7 @@ static const char* MQTT_FB_PARSING_STATUS_TYPE = "MQTTParsingStatusType"; static const char* MQTT_PUB_FB_SET_STATUS_TYPE = "MQTTSettingStatusType"; -static const char* MQTT_ROOT_FB_CON_STATUS_NAME = "ConnectionStatus"; +static const char* MQTT_CLIENT_FB_CON_STATUS_NAME = "ConnectionStatus"; static const char* MQTT_PUB_FB_SIG_STATUS_NAME = "SignalStatus"; static const char* MQTT_PUB_FB_PUB_STATUS_NAME = "PublishingStatus"; static const char* MQTT_FB_SUB_STATUS_NAME = "SubscriptionStatus"; diff --git a/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp index e57fd19..e249fc4 100644 --- a/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp @@ -39,17 +39,17 @@ FunctionBlockTypePtr MqttJsonDecoderFbImpl::CreateType() { auto defaultConfig = PropertyObject(); { - auto builder = StringPropertyBuilder(PROPERTY_NAME_VALUE_NAME, String("")).setDescription(""); + auto builder = StringPropertyBuilder(PROPERTY_NAME_DEC_VALUE_NAME, String("")).setDescription(""); defaultConfig.addProperty(builder.build()); } { - auto builder = StringPropertyBuilder(PROPERTY_NAME_TS_NAME, String("")).setDescription(""); + auto builder = StringPropertyBuilder(PROPERTY_NAME_DEC_TS_NAME, String("")).setDescription(""); defaultConfig.addProperty(builder.build()); } { - auto builder = StringPropertyBuilder(PROPERTY_NAME_UNIT, String("")).setDescription(""); + auto builder = StringPropertyBuilder(PROPERTY_NAME_DEC_UNIT, String("")).setDescription(""); defaultConfig.addProperty(builder.build()); } @@ -93,14 +93,14 @@ void MqttJsonDecoderFbImpl::readProperties() auto lock = this->getRecursiveConfigLock(); configStatus.configValid = true; configStatus.configMsg.clear(); - config.valueFieldName = readProperty(PROPERTY_NAME_VALUE_NAME, ""); + config.valueFieldName = readProperty(PROPERTY_NAME_DEC_VALUE_NAME, ""); if (config.valueFieldName.empty()) { - configStatus.configMsg = fmt::format("\"{}\" property is empty!", PROPERTY_NAME_VALUE_NAME); + configStatus.configMsg = fmt::format("\"{}\" property is empty!", PROPERTY_NAME_DEC_VALUE_NAME); configStatus.configValid = false; } - config.tsFieldName = readProperty(PROPERTY_NAME_TS_NAME, ""); - config.unitSymbol = readProperty(PROPERTY_NAME_UNIT, ""); + config.tsFieldName = readProperty(PROPERTY_NAME_DEC_TS_NAME, ""); + config.unitSymbol = readProperty(PROPERTY_NAME_DEC_UNIT, ""); jsonDataWorker.setValueFieldName(config.valueFieldName); jsonDataWorker.setTimestampFieldName(config.tsFieldName); diff --git a/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp index 71c691c..5d8bc5a 100644 --- a/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp @@ -19,8 +19,8 @@ MqttRootFbImpl::MqttRootFbImpl(const ContextPtr& ctx, const ComponentPtr& parent : FunctionBlock(CreateType(), ctx, parent, generateLocalId()), subscriber(std::make_shared()), connectTimeout(0), - connectionStatus(MQTT_ROOT_FB_CON_STATUS_TYPE, - MQTT_ROOT_FB_CON_STATUS_NAME, + connectionStatus(MQTT_CLIENT_FB_CON_STATUS_TYPE, + MQTT_CLIENT_FB_CON_STATUS_NAME, statusContainer, connectionStatusMap, ConnectionStatus::Disconnected, @@ -150,13 +150,13 @@ void MqttRootFbImpl::initProperties(const PropertyObjectPtr& config) void MqttRootFbImpl::readProperties() { - connectionSettings.mqttUrl = objPtr.getPropertyValue(PROPERTY_NAME_MQTT_BROKER_ADDRESS).asPtr().toStdString(); - connectionSettings.port = objPtr.getPropertyValue(PROPERTY_NAME_MQTT_BROKER_PORT); - connectionSettings.username = objPtr.getPropertyValue(PROPERTY_NAME_MQTT_USERNAME).asPtr().toStdString(); - connectionSettings.password = objPtr.getPropertyValue(PROPERTY_NAME_MQTT_PASSWORD).asPtr().toStdString(); + connectionSettings.mqttUrl = objPtr.getPropertyValue(PROPERTY_NAME_CLIENT_BROKER_ADDRESS).asPtr().toStdString(); + connectionSettings.port = objPtr.getPropertyValue(PROPERTY_NAME_CLIENT_BROKER_PORT); + connectionSettings.username = objPtr.getPropertyValue(PROPERTY_NAME_CLIENT_USERNAME).asPtr().toStdString(); + connectionSettings.password = objPtr.getPropertyValue(PROPERTY_NAME_CLIENT_PASSWORD).asPtr().toStdString(); connectionSettings.clientId = globalId.toStdString(); - connectTimeout = objPtr.getPropertyValue(PROPERTY_NAME_CONNECT_TIMEOUT); + connectTimeout = objPtr.getPropertyValue(PROPERTY_NAME_CLIENT_CONNECT_TIMEOUT); } bool MqttRootFbImpl::waitForConnection(const int timeoutMs) @@ -214,7 +214,7 @@ FunctionBlockPtr MqttRootFbImpl::onAddFunctionBlock(const StringPtr& typeId, con std::string MqttRootFbImpl::generateLocalId() { - return std::string(MQTT_LOCAL_ROOT_FB_ID_PREFIX + std::to_string(localIndex++)); + return std::string(MQTT_LOCAL_CLIENT_FB_ID_PREFIX + std::to_string(localIndex++)); } FunctionBlockTypePtr MqttRootFbImpl::CreateType() @@ -222,33 +222,33 @@ FunctionBlockTypePtr MqttRootFbImpl::CreateType() auto config = PropertyObject(); { auto builder = - StringPropertyBuilder(PROPERTY_NAME_MQTT_BROKER_ADDRESS, DEFAULT_BROKER_ADDRESS) + StringPropertyBuilder(PROPERTY_NAME_CLIENT_BROKER_ADDRESS, DEFAULT_BROKER_ADDRESS) .setDescription(fmt::format("MQTT broker address. It can be an IP address or a hostname. By default it is set to \"{}\".", DEFAULT_BROKER_ADDRESS)); config.addProperty(builder.build()); } { auto builder = - StringPropertyBuilder(PROPERTY_NAME_MQTT_USERNAME, DEFAULT_USERNAME) + StringPropertyBuilder(PROPERTY_NAME_CLIENT_USERNAME, DEFAULT_USERNAME) .setDescription(fmt::format("Username for MQTT broker authentication. By default it is set to \"{}\".", DEFAULT_USERNAME)); config.addProperty(builder.build()); } { auto builder = - StringPropertyBuilder(PROPERTY_NAME_MQTT_PASSWORD, DEFAULT_PASSWORD) + StringPropertyBuilder(PROPERTY_NAME_CLIENT_PASSWORD, DEFAULT_PASSWORD) .setDescription(fmt::format("Password for MQTT broker authentication. By default it is set to \"{}\".", DEFAULT_PASSWORD)); config.addProperty(builder.build()); } { auto builder = - IntPropertyBuilder(PROPERTY_NAME_MQTT_BROKER_PORT, DEFAULT_PORT) + IntPropertyBuilder(PROPERTY_NAME_CLIENT_BROKER_PORT, DEFAULT_PORT) .setMinValue(1) .setMaxValue(65535) .setDescription(fmt::format("Port number for MQTT broker connection. By default it is set to {}.", DEFAULT_PORT)); config.addProperty(builder.build()); } { - auto builder = IntPropertyBuilder(PROPERTY_NAME_CONNECT_TIMEOUT, DEFAULT_INIT_TIMEOUT) + auto builder = IntPropertyBuilder(PROPERTY_NAME_CLIENT_CONNECT_TIMEOUT, DEFAULT_INIT_TIMEOUT) .setMinValue(0) .setUnit(Unit("ms")) .setDescription(fmt::format("Timeout in milliseconds for the initial connection to the MQTT broker. If the " @@ -256,8 +256,8 @@ FunctionBlockTypePtr MqttRootFbImpl::CreateType() DEFAULT_INIT_TIMEOUT)); config.addProperty(builder.build()); } - const auto fbType = FunctionBlockType(ROOT_FB_NAME, - ROOT_FB_NAME, + const auto fbType = FunctionBlockType(CLIENT_FB_NAME, + CLIENT_FB_NAME, "The MQTT function block allows connecting to MQTT broker. It may contain nested " "publisher/subscriber FBs.", config); diff --git a/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp index e3bebf5..69544f1 100644 --- a/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp @@ -68,7 +68,7 @@ void MqttSubscriberFbImpl::initProperties(const PropertyObjectPtr& config) for (const auto& prop : config.getAllProperties()) { const auto propName = prop.getName(); - if (propName == PROPERTY_NAME_JSON_CONFIG || propName == PROPERTY_NAME_JSON_CONFIG_FILE) + if (propName == PROPERTY_NAME_SUB_JSON_CONFIG || propName == PROPERTY_NAME_SUB_JSON_CONFIG_FILE) { if (!objPtr.hasProperty(propName)) { @@ -108,7 +108,7 @@ FunctionBlockTypePtr MqttSubscriberFbImpl::CreateType() auto defaultConfig = PropertyObject(); { auto builder = - StringPropertyBuilder(PROPERTY_NAME_TOPIC, String("")).setDescription("An MQTT topic to subscribe to for receiving data."); + StringPropertyBuilder(PROPERTY_NAME_SUB_TOPIC, String("")).setDescription("An MQTT topic to subscribe to for receiving data."); defaultConfig.addProperty(builder.build()); } { @@ -122,12 +122,12 @@ FunctionBlockTypePtr MqttSubscriberFbImpl::CreateType() } { auto builder = - StringPropertyBuilder(PROPERTY_NAME_JSON_CONFIG, String("")) + StringPropertyBuilder(PROPERTY_NAME_SUB_JSON_CONFIG, String("")) .setDescription("JSON configuration string that defines an MQTT topic and corresponding signals to subscribe to."); defaultConfig.addProperty(builder.build()); } { - auto builder = StringPropertyBuilder(PROPERTY_NAME_JSON_CONFIG_FILE, String("")) + auto builder = StringPropertyBuilder(PROPERTY_NAME_SUB_JSON_CONFIG_FILE, String("")) .setDescription("Path to file where the JSON configuration string is stored."); defaultConfig.addProperty(builder.build()); } @@ -160,9 +160,9 @@ void MqttSubscriberFbImpl::readProperties() auto lock = this->getRecursiveConfigLock(); topicForSubscribing.clear(); bool isPresent = false; - if (objPtr.hasProperty(PROPERTY_NAME_TOPIC)) + if (objPtr.hasProperty(PROPERTY_NAME_SUB_TOPIC)) { - auto topicStr = objPtr.getPropertyValue(PROPERTY_NAME_TOPIC).asPtrOrNull(); + auto topicStr = objPtr.getPropertyValue(PROPERTY_NAME_SUB_TOPIC).asPtrOrNull(); if (topicStr.assigned()) { isPresent = true; @@ -180,7 +180,7 @@ void MqttSubscriberFbImpl::readProperties() } if (!isPresent) { - LOG_W("\'{}\' property is missing!", PROPERTY_NAME_TOPIC); + LOG_W("\'{}\' property is missing!", PROPERTY_NAME_SUB_TOPIC); setComponentStatus(ComponentStatus::Warning); subscriptionStatus.setStatus(SubscriptionStatus::InvalidTopicName, "The topic property is not set!"); } @@ -189,9 +189,9 @@ void MqttSubscriberFbImpl::readProperties() void MqttSubscriberFbImpl::readJsonConfig() { bool hasJsonConfig = false; - if (objPtr.hasProperty(PROPERTY_NAME_JSON_CONFIG)) + if (objPtr.hasProperty(PROPERTY_NAME_SUB_JSON_CONFIG)) { - const auto signalConfig = objPtr.getPropertyValue(PROPERTY_NAME_JSON_CONFIG).asPtrOrNull(); + const auto signalConfig = objPtr.getPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG).asPtrOrNull(); if (signalConfig.assigned()) { if (!signalConfig.toStdString().empty()) @@ -201,9 +201,9 @@ void MqttSubscriberFbImpl::readJsonConfig() } } } - if (hasJsonConfig == false && objPtr.hasProperty(PROPERTY_NAME_JSON_CONFIG_FILE)) + if (hasJsonConfig == false && objPtr.hasProperty(PROPERTY_NAME_SUB_JSON_CONFIG_FILE)) { - const auto configPath = objPtr.getPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE).asPtrOrNull(); + const auto configPath = objPtr.getPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG_FILE).asPtrOrNull(); if (configPath.assigned()) { if (!configPath.toStdString().empty()) @@ -247,9 +247,9 @@ void MqttSubscriberFbImpl::setJsonConfig(const std::string config) if (result.success) { { - auto event = objPtr.getOnPropertyValueWrite(PROPERTY_NAME_TOPIC); + auto event = objPtr.getOnPropertyValueWrite(PROPERTY_NAME_SUB_TOPIC); event.mute(); - objPtr.setPropertyValue(PROPERTY_NAME_TOPIC, String(topic)); + objPtr.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, String(topic)); event.unmute(); } if (const auto signalDscs = jsonDataWorker.extractDescription(); !signalDscs.empty()) @@ -258,10 +258,10 @@ void MqttSubscriberFbImpl::setJsonConfig(const std::string config) for (const auto& [signalName, descriptor] : signalDscs) { LOG_I("Creating a decoder FB for the signal \"{}\":", signalName); - fbConfig.setPropertyValue(PROPERTY_NAME_VALUE_NAME, descriptor.valueFieldName); - fbConfig.setPropertyValue(PROPERTY_NAME_TS_NAME, descriptor.tsFieldName); + fbConfig.setPropertyValue(PROPERTY_NAME_DEC_VALUE_NAME, descriptor.valueFieldName); + fbConfig.setPropertyValue(PROPERTY_NAME_DEC_TS_NAME, descriptor.tsFieldName); if (descriptor.unit.assigned()) - fbConfig.setPropertyValue(PROPERTY_NAME_UNIT, descriptor.unit.getSymbol()); + fbConfig.setPropertyValue(PROPERTY_NAME_DEC_UNIT, descriptor.unit.getSymbol()); MqttSubscriberFbImpl::onAddFunctionBlock(JSON_DECODER_FB_NAME, fbConfig); } } diff --git a/mqtt_streaming_module/tests/test_daq_test_helper.h b/mqtt_streaming_module/tests/test_daq_test_helper.h index 0b31b41..44e3f7c 100644 --- a/mqtt_streaming_module/tests/test_daq_test_helper.h +++ b/mqtt_streaming_module/tests/test_daq_test_helper.h @@ -28,7 +28,7 @@ class DaqTestHelper daq::FunctionBlockPtr DaqAddRootMqttFb(std::string url = DEFAULT_BROKER_ADDRESS, uint16_t port = DEFAULT_PORT) { auto config = DaqMqttFbConfig(url, port); - rootMqttFb = daqInstance.addFunctionBlock(ROOT_FB_NAME, config); + rootMqttFb = daqInstance.addFunctionBlock(CLIENT_FB_NAME, config); return rootMqttFb; } @@ -37,7 +37,7 @@ class DaqTestHelper if (!rootMqttFb.assigned()) { auto config = DaqMqttFbConfig(url, port); - rootMqttFb = daqInstance.addFunctionBlock(ROOT_FB_NAME, config); + rootMqttFb = daqInstance.addFunctionBlock(CLIENT_FB_NAME, config); } return rootMqttFb; } @@ -47,9 +47,9 @@ class DaqTestHelper daq::ModulePtr module; createModule(&module, daq::NullContext()); - auto config = module.getAvailableFunctionBlockTypes().get(daq::modules::mqtt_streaming_module::ROOT_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_MQTT_BROKER_ADDRESS, url); - config.setPropertyValue(PROPERTY_NAME_MQTT_BROKER_PORT, port); + auto config = module.getAvailableFunctionBlockTypes().get(daq::modules::mqtt_streaming_module::CLIENT_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_CLIENT_BROKER_ADDRESS, url); + config.setPropertyValue(PROPERTY_NAME_CLIENT_BROKER_PORT, port); return config; } @@ -63,7 +63,7 @@ class DaqTestHelper daq::FunctionBlockPtr AddSubFb(std::string topic = "") { auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_TOPIC, daq::String(topic)); + config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, daq::String(topic)); subMqttFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config); return subMqttFb; } diff --git a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp index 8e459cb..2185b1c 100644 --- a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp @@ -48,21 +48,12 @@ class MqttJsonDecoderFbHelper : public DaqTestHelper fb->onSignalsMessage(unused, msg); } - void CreateJsonFbFromConfig(const std::string& jsonConfig) - { - auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_SIGNAL_LIST, String(""))); - const auto fbType = FunctionBlockType(SUB_FB_NAME, SUB_FB_NAME, "", config); - config.setPropertyValue(PROPERTY_NAME_SIGNAL_LIST, jsonConfig); - subMqttFb = new MqttSubscriberFbImpl(NullContext(), nullptr, fbType, nullptr, config); - } - void CreateJsonFb(const std::string& topic) { auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, String(""))); + config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, String(""))); const auto fbType = FunctionBlockType(SUB_FB_NAME, SUB_FB_NAME, "", config); - config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); + config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic); subMqttFb = new MqttSubscriberFbImpl(NullContext(), nullptr, fbType, nullptr, config); } @@ -77,9 +68,9 @@ class MqttJsonDecoderFbHelper : public DaqTestHelper daq::StringPtr typeId = daq::String(JSON_DECODER_FB_NAME); auto config = subMqttFb.getAvailableFunctionBlockTypes().get(JSON_DECODER_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_VALUE_NAME, valueF); - config.setPropertyValue(PROPERTY_NAME_TS_NAME, tsF); - config.setPropertyValue(PROPERTY_NAME_UNIT, unitSymbol); + config.setPropertyValue(PROPERTY_NAME_DEC_VALUE_NAME, valueF); + config.setPropertyValue(PROPERTY_NAME_DEC_TS_NAME, tsF); + config.setPropertyValue(PROPERTY_NAME_DEC_UNIT, unitSymbol); decoderObj = subMqttFb.addFunctionBlock(typeId, config); return decoderObj; } @@ -152,21 +143,6 @@ class MqttJsonDecoderFbHelper : public DaqTestHelper return result; } - template - std::vector> - transferData(const std::vector>& data, const std::string& jsonConfigTemplate, const std::string& jsonDataTemplate) - { - return transferData>(data, jsonConfigTemplate, jsonDataTemplate); - } - - template - std::vector transferDataWithoutDomain(const std::vector>& data, - const std::string& jsonConfigTemplate, - const std::string& jsonDataTemplate) - { - return transferData(data, jsonConfigTemplate, jsonDataTemplate); - } - template std::vector> transferData(const std::vector>& data, const std::string& jsonDataTemplate) { @@ -337,25 +313,6 @@ class MqttJsonDecoderFbHelper : public DaqTestHelper } private: - template std::vector transferData(const std::vector>& data, const std::string& jsonConfigTemplate, const std::string& jsonDataTemplate) - { - const auto topic = buildTopicName(); - const auto jsonConfig = replacePlaceholder(jsonConfigTemplate, "", topic); - CreateJsonFbFromConfig(jsonConfig); - - auto signal = getSignals()[0]; - auto reader = daq::PacketReader(signal); - - auto msgs = replacePlaceholders(data, jsonDataTemplate); - for (const auto& str : msgs) - { - onSignalsMessage({topic, std::vector(str.begin(), str.end()), 1, 0}); - } - - std::vector dataToReceive = read(reader, signal, 0); - return dataToReceive; - } - template std::vector transferData(const std::vector>& data, const std::string& jsonDataTemplate) { const auto topic = buildTopicName(); @@ -467,17 +424,17 @@ TEST_F(MqttJsonDecoderFbTest, DefaultConfig) ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 3u); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_VALUE_NAME)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_VALUE_NAME).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().getLength(), 0u); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_DEC_VALUE_NAME)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_DEC_VALUE_NAME).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_DEC_VALUE_NAME).asPtr().getLength(), 0u); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_TS_NAME)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_TS_NAME).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_TS_NAME).asPtr().getLength(), 0u); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_DEC_TS_NAME)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_DEC_TS_NAME).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_DEC_TS_NAME).asPtr().getLength(), 0u); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_UNIT)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_UNIT).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_UNIT).asPtr().getLength(), 0u); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_DEC_UNIT)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_DEC_UNIT).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_DEC_UNIT).asPtr().getLength(), 0u); } TEST_F(MqttJsonDecoderFbTest, Config) @@ -486,9 +443,9 @@ TEST_F(MqttJsonDecoderFbTest, Config) AddSubFb(buildTopicName()); auto config = subMqttFb.getAvailableFunctionBlockTypes().get(JSON_DECODER_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_VALUE_NAME, "value"); - config.setPropertyValue(PROPERTY_NAME_TS_NAME, "timestamp"); - config.setPropertyValue(PROPERTY_NAME_UNIT, "ppm"); + config.setPropertyValue(PROPERTY_NAME_DEC_VALUE_NAME, "value"); + config.setPropertyValue(PROPERTY_NAME_DEC_TS_NAME, "timestamp"); + config.setPropertyValue(PROPERTY_NAME_DEC_UNIT, "ppm"); daq::FunctionBlockPtr fb; ASSERT_NO_THROW(fb = subMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); EXPECT_EQ(fb.getSignals().getCount(), 1u); @@ -531,7 +488,7 @@ TEST_F(MqttJsonDecoderFbTest, CreationWithPartialConfig) { daq::FunctionBlockPtr fb; auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_VALUE_NAME, String("value"))); + config.addProperty(StringProperty(PROPERTY_NAME_DEC_VALUE_NAME, String("value"))); ASSERT_NO_THROW(fb = subMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); EXPECT_EQ(fb.getSignals().getCount(), 1u); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), @@ -545,7 +502,7 @@ TEST_F(MqttJsonDecoderFbTest, CreationWithPartialConfig) { daq::FunctionBlockPtr fb; auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TS_NAME, String("ts"))); + config.addProperty(StringProperty(PROPERTY_NAME_DEC_TS_NAME, String("ts"))); ASSERT_NO_THROW(fb = subMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); EXPECT_EQ(fb.getSignals().getCount(), 1u); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), @@ -915,7 +872,7 @@ TEST_F(MqttJsonDecoderFbTest, RemovingNestedFunctionBlock) daq::FunctionBlockPtr jsonDecoderFb; { auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_VALUE_NAME, String("temp"))); + config.addProperty(StringProperty(PROPERTY_NAME_DEC_VALUE_NAME, String("temp"))); ASSERT_NO_THROW(jsonDecoderFb = subMqttFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); } ASSERT_EQ(subMqttFb.getFunctionBlocks().getCount(), 1u); diff --git a/mqtt_streaming_module/tests/test_mqtt_root_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_root_fb.cpp index 418711b..7b2ca23 100644 --- a/mqtt_streaming_module/tests/test_mqtt_root_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_root_fb.cpp @@ -22,36 +22,36 @@ TEST_F(MqttFbTest, DefaultMqttFbConfig) ASSERT_NO_THROW(types = module.getAvailableFunctionBlockTypes()); ASSERT_EQ(types.getCount(), 1u); - ASSERT_TRUE(types.hasKey(ROOT_FB_NAME)); - auto defaultConfig = types.get(ROOT_FB_NAME).createDefaultConfig(); + ASSERT_TRUE(types.hasKey(CLIENT_FB_NAME)); + auto defaultConfig = types.get(CLIENT_FB_NAME).createDefaultConfig(); ASSERT_TRUE(defaultConfig.assigned()); ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 5u); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_MQTT_BROKER_ADDRESS)); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_MQTT_BROKER_PORT)); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_MQTT_USERNAME)); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_MQTT_PASSWORD)); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_CONNECT_TIMEOUT)); - - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_MQTT_BROKER_ADDRESS).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_MQTT_BROKER_PORT).getValueType(), CoreType::ctInt); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_MQTT_USERNAME).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_MQTT_PASSWORD).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_CONNECT_TIMEOUT).getValueType(), CoreType::ctInt); - - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_MQTT_BROKER_ADDRESS), DEFAULT_BROKER_ADDRESS); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_MQTT_BROKER_PORT), DEFAULT_PORT); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_MQTT_USERNAME), DEFAULT_USERNAME); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_MQTT_PASSWORD), DEFAULT_PASSWORD); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_CONNECT_TIMEOUT), DEFAULT_INIT_TIMEOUT); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_CLIENT_BROKER_ADDRESS)); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_CLIENT_BROKER_PORT)); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_CLIENT_USERNAME)); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_CLIENT_PASSWORD)); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_CLIENT_CONNECT_TIMEOUT)); + + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_CLIENT_BROKER_ADDRESS).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_CLIENT_BROKER_PORT).getValueType(), CoreType::ctInt); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_CLIENT_USERNAME).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_CLIENT_PASSWORD).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_CLIENT_CONNECT_TIMEOUT).getValueType(), CoreType::ctInt); + + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_CLIENT_BROKER_ADDRESS), DEFAULT_BROKER_ADDRESS); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_CLIENT_BROKER_PORT), DEFAULT_PORT); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_CLIENT_USERNAME), DEFAULT_USERNAME); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_CLIENT_PASSWORD), DEFAULT_PASSWORD); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_CLIENT_CONNECT_TIMEOUT), DEFAULT_INIT_TIMEOUT); } TEST_F(MqttFbTest, CreatingMqttFbWithDefaultConfig) { const auto instance = Instance(); daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = instance.addFunctionBlock(ROOT_FB_NAME)); + ASSERT_NO_THROW(fb = instance.addFunctionBlock(CLIENT_FB_NAME)); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", instance.getContext().getTypeManager())); @@ -60,7 +60,7 @@ TEST_F(MqttFbTest, CreatingMqttFbWithDefaultConfig) daq::FunctionBlockPtr fbFromList; for (const auto& fbInst : fbs) { - contain = (fbInst.getName() == std::string(MQTT_LOCAL_ROOT_FB_ID_PREFIX) + std::to_string(0)); + contain = (fbInst.getName() == std::string(MQTT_LOCAL_CLIENT_FB_ID_PREFIX) + std::to_string(0)); if (contain) { fbFromList = fbInst; @@ -77,7 +77,7 @@ TEST_F(MqttFbTest, CreatingMqttFbWithCustomConfig) const auto instance = Instance(); daq::FunctionBlockPtr fb; auto config = DaqMqttFbConfig(); - ASSERT_NO_THROW(fb = instance.addFunctionBlock(ROOT_FB_NAME, config)); + ASSERT_NO_THROW(fb = instance.addFunctionBlock(CLIENT_FB_NAME, config)); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", instance.getContext().getTypeManager())); } @@ -87,7 +87,7 @@ TEST_F(MqttFbTest, CreatingMqttFbWithEmptyConfig) const auto instance = Instance(); daq::FunctionBlockPtr fb; auto config = PropertyObject(); - ASSERT_NO_THROW(fb = instance.addFunctionBlock(ROOT_FB_NAME, config)); + ASSERT_NO_THROW(fb = instance.addFunctionBlock(CLIENT_FB_NAME, config)); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", instance.getContext().getTypeManager())); } @@ -97,8 +97,8 @@ TEST_F(MqttFbTest, CreatingMqttFbWithPartialConfig) const auto instance = Instance(); daq::FunctionBlockPtr fb; auto config = PropertyObject(); - config.addProperty(IntProperty(PROPERTY_NAME_CONNECT_TIMEOUT, 1000)); - ASSERT_NO_THROW(fb = instance.addFunctionBlock(ROOT_FB_NAME, config)); + config.addProperty(IntProperty(PROPERTY_NAME_CLIENT_CONNECT_TIMEOUT, 1000)); + ASSERT_NO_THROW(fb = instance.addFunctionBlock(CLIENT_FB_NAME, config)); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", instance.getContext().getTypeManager())); } @@ -107,11 +107,11 @@ TEST_F(MqttFbTest, CreatingSeveralMqttFbs) { const auto instance = Instance(); daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = instance.addFunctionBlock(ROOT_FB_NAME, DaqMqttFbConfig("127.0.0.1", 1883))); + ASSERT_NO_THROW(fb = instance.addFunctionBlock(CLIENT_FB_NAME, DaqMqttFbConfig("127.0.0.1", 1883))); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", instance.getContext().getTypeManager())); daq::FunctionBlockPtr anotherFb; - ASSERT_NO_THROW(anotherFb = instance.addFunctionBlock(ROOT_FB_NAME, DaqMqttFbConfig("127.0.0.1", 1884))); + ASSERT_NO_THROW(anotherFb = instance.addFunctionBlock(CLIENT_FB_NAME, DaqMqttFbConfig("127.0.0.1", 1884))); ASSERT_EQ(anotherFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", instance.getContext().getTypeManager())); ASSERT_EQ(instance.getFunctionBlocks().getCount(), 2u); @@ -122,7 +122,7 @@ TEST_F(MqttFbTest, RemovingMqttFb) const auto instance = Instance(); daq::FunctionBlockPtr fb; auto config = DaqMqttFbConfig(); - ASSERT_NO_THROW(fb = instance.addFunctionBlock(ROOT_FB_NAME, config)); + ASSERT_NO_THROW(fb = instance.addFunctionBlock(CLIENT_FB_NAME, config)); ASSERT_NO_THROW(instance.removeFunctionBlock(fb)); } diff --git a/mqtt_streaming_module/tests/test_mqtt_streaming_module.cpp b/mqtt_streaming_module/tests/test_mqtt_streaming_module.cpp index d82a71a..bfc7cc2 100644 --- a/mqtt_streaming_module/tests/test_mqtt_streaming_module.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_streaming_module.cpp @@ -65,8 +65,8 @@ TEST_F(MqttStreamingClientModuleTest, GetAvailableComponentTypes) DictPtr functionBlockTypes; ASSERT_NO_THROW(functionBlockTypes = module.getAvailableFunctionBlockTypes()); ASSERT_EQ(functionBlockTypes.getCount(), 1u); - ASSERT_TRUE(functionBlockTypes.hasKey(ROOT_FB_NAME)); - ASSERT_EQ(functionBlockTypes.get(ROOT_FB_NAME).getId(), ROOT_FB_NAME); + ASSERT_TRUE(functionBlockTypes.hasKey(CLIENT_FB_NAME)); + ASSERT_EQ(functionBlockTypes.get(CLIENT_FB_NAME).getId(), CLIENT_FB_NAME); DictPtr deviceTypes; ASSERT_NO_THROW(deviceTypes = module.getAvailableDeviceTypes()); diff --git a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp index 94f2364..185fbd1 100644 --- a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp @@ -23,9 +23,9 @@ class MqttSubscriberFbHelper void CreateSubFB(std::string topic) { auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, "")); + config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, "")); const auto fbType = FunctionBlockType(SUB_FB_NAME, SUB_FB_NAME, "", config); - config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); + config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic); obj = std::make_unique(NullContext(), nullptr, fbType, nullptr, config); } @@ -90,17 +90,17 @@ TEST_F(MqttSubscriberFbTest, DefaultConfig) ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 4u); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_JSON_CONFIG)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_JSON_CONFIG).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_JSON_CONFIG).asPtr().getLength(), 0u); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_JSON_CONFIG)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_JSON_CONFIG).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG).asPtr().getLength(), 0u); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_JSON_CONFIG_FILE)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_JSON_CONFIG_FILE).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE).asPtr().getLength(), 0u); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_JSON_CONFIG_FILE)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_JSON_CONFIG_FILE).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG_FILE).asPtr().getLength(), 0u); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_TOPIC)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_TOPIC).getValueType(), CoreType::ctString); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().getLength(), 0u); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_TOPIC)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_TOPIC).getValueType(), CoreType::ctString); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_SUB_TOPIC).asPtr().getLength(), 0u); ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_QOS)); ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_QOS).getValueType(), CoreType::ctInt); @@ -112,7 +112,7 @@ TEST_F(MqttSubscriberFbTest, Config) StartUp(); auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_TOPIC, buildTopicName()); + config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, buildTopicName()); daq::FunctionBlockPtr subFb; ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); @@ -143,7 +143,7 @@ TEST_F(MqttSubscriberFbTest, CreationWithPartialConfig) StartUp(); daq::FunctionBlockPtr subFb; auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, String(buildTopicName()))); + config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, String(buildTopicName()))); ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(subFb.getSignals().getCount(), 1u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), @@ -156,7 +156,7 @@ TEST_F(MqttSubscriberFbTest, CreationWithCustomConfig) StartUp(); daq::FunctionBlockPtr subFb; auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_TOPIC, String(buildTopicName())); + config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, String(buildTopicName())); ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(subFb.getSignals().getCount(), 1u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), @@ -169,7 +169,7 @@ TEST_F(MqttSubscriberFbTest, SubscriptionStatusWaitingForData) auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_TOPIC, buildTopicName()); + config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, buildTopicName()); daq::FunctionBlockPtr subFb; ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), @@ -188,7 +188,7 @@ TEST_P(MqttSubscriberFbTopicPTest, CheckSubscriberFbTopic) auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); + config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic); daq::FunctionBlockPtr fb; ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); auto signals = fb.getSignals(); @@ -227,7 +227,7 @@ TEST_F(MqttSubscriberFbTest, RemovingNestedFunctionBlock) daq::FunctionBlockPtr subFb; { auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, String(buildTopicName()))); + config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, String(buildTopicName()))); ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -235,7 +235,7 @@ TEST_F(MqttSubscriberFbTest, RemovingNestedFunctionBlock) daq::FunctionBlockPtr jsonDecoderFb; { auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_VALUE_NAME, String("temp"))); + config.addProperty(StringProperty(PROPERTY_NAME_DEC_VALUE_NAME, String("temp"))); ASSERT_NO_THROW(jsonDecoderFb = subFb.addFunctionBlock(JSON_DECODER_FB_NAME, config)); } ASSERT_EQ(subFb.getFunctionBlocks().getCount(), 1u); @@ -250,7 +250,7 @@ TEST_F(MqttSubscriberFbTest, TwoFbCreation) { daq::FunctionBlockPtr fb; auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, buildTopicName("0"))); + config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, buildTopicName("0"))); ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -258,7 +258,7 @@ TEST_F(MqttSubscriberFbTest, TwoFbCreation) { daq::FunctionBlockPtr fb; auto config = PropertyObject(); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, buildTopicName("1"))); + config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, buildTopicName("1"))); ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -274,7 +274,7 @@ TEST_F(MqttSubscriberFbTest, PropertyChanged) daq::FunctionBlockPtr fb; auto config = PropertyObject(); auto topic = buildTopicName("0"); - config.addProperty(StringProperty(PROPERTY_NAME_TOPIC, topic)); + config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, topic)); ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -282,7 +282,7 @@ TEST_F(MqttSubscriberFbTest, PropertyChanged) ASSERT_EQ(topic, subFb->getSubscribedTopic()); topic = buildTopicName("1"); - fb.setPropertyValue(PROPERTY_NAME_TOPIC, topic); + fb.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic); ASSERT_EQ(topic, subFb->getSubscribedTopic()); } @@ -291,7 +291,7 @@ TEST_F(MqttSubscriberFbTest, JsonInit0) StartUp(); daq::FunctionBlockPtr subFb; auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG, String(VALID_JSON_1_TOPIC_0)); + config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG, String(VALID_JSON_1_TOPIC_0)); ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); ASSERT_EQ(subFb.getFunctionBlocks().getCount(), 3u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), @@ -301,10 +301,10 @@ TEST_F(MqttSubscriberFbTest, JsonInit0) EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); if (!symbol.empty()) EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_TS_NAME).asPtr().toStdString(), ts); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_DEC_VALUE_NAME).asPtr().toStdString(), value); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_DEC_TS_NAME).asPtr().toStdString(), ts); }; - EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "openDAQ/RefDev0/IO/AI/RefCh0/Sig/AI0"); + EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_SUB_TOPIC).asPtr().toStdString(), "openDAQ/RefDev0/IO/AI/RefCh0/Sig/AI0"); lambda(subFb.getFunctionBlocks()[0], "value", "timestamp", "V"); lambda(subFb.getFunctionBlocks()[1], "value1", "", ""); @@ -317,7 +317,7 @@ TEST_F(MqttSubscriberFbTest, JsonInit1) StartUp(); daq::FunctionBlockPtr subFb; auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG, String(VALID_JSON_1_TOPIC_1)); + config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG, String(VALID_JSON_1_TOPIC_1)); ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); ASSERT_EQ(subFb.getFunctionBlocks().getCount(), 3u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), @@ -327,10 +327,10 @@ TEST_F(MqttSubscriberFbTest, JsonInit1) EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); if (!symbol.empty()) EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_TS_NAME).asPtr().toStdString(), ts); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_DEC_VALUE_NAME).asPtr().toStdString(), value); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_DEC_TS_NAME).asPtr().toStdString(), ts); }; - EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "/mirip/UNet3AC2/sensor/data"); + EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_SUB_TOPIC).asPtr().toStdString(), "/mirip/UNet3AC2/sensor/data"); lambda(subFb.getFunctionBlocks()[0], "temp", "ts", "°C"); lambda(subFb.getFunctionBlocks()[1], "humi", "ts", "%"); @@ -344,12 +344,12 @@ TEST_P(MqttSubscriberFbConfigPTest, JsonWrongInit) StartUp(); daq::FunctionBlockPtr subFb; auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG, String(configJson)); + config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG, String(configJson)); ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(subFb.getFunctionBlocks().getCount(), 0u); EXPECT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); - EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), ""); + EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_SUB_TOPIC).asPtr().toStdString(), ""); } INSTANTIATE_TEST_SUITE_P( @@ -368,7 +368,7 @@ TEST_P(MqttSubscriberFbConfigFilePTest, JsonInitFromFile) StartUp(); daq::FunctionBlockPtr subFb; auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE, String(configJson)); + config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG_FILE, String(configJson)); ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -386,7 +386,7 @@ TEST_F(MqttSubscriberFbTest, JsonInitFromFileWithChecking) StartUp(); daq::FunctionBlockPtr subFb; auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE, String("data/public-example0.json")); + config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG_FILE, String("data/public-example0.json")); ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -396,10 +396,10 @@ TEST_F(MqttSubscriberFbTest, JsonInitFromFileWithChecking) EXPECT_EQ(nestedFb.getSignals()[0].getName().toStdString(), value); if (!symbol.empty()) EXPECT_EQ(nestedFb.getSignals()[0].getDescriptor().getUnit().getSymbol().toStdString(), symbol); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_VALUE_NAME).asPtr().toStdString(), value); - EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_TS_NAME).asPtr().toStdString(), ts); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_DEC_VALUE_NAME).asPtr().toStdString(), value); + EXPECT_EQ(nestedFb.getPropertyValue(PROPERTY_NAME_DEC_TS_NAME).asPtr().toStdString(), ts); }; - EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), "/mirip/UNet3AC2/sensor/data"); + EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_SUB_TOPIC).asPtr().toStdString(), "/mirip/UNet3AC2/sensor/data"); lambda(subFb.getFunctionBlocks()[0], "temp", "ts", "°C"); lambda(subFb.getFunctionBlocks()[1], "humi", "ts", "%"); @@ -412,12 +412,12 @@ TEST_F(MqttSubscriberFbTest, JsonInitFromFileWrongPath) StartUp(); daq::FunctionBlockPtr subFb; auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_JSON_CONFIG_FILE, String("/justWrongPath/wrongFile.txt")); + config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG_FILE, String("/justWrongPath/wrongFile.txt")); ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(subFb.getFunctionBlocks().getCount(), 0u); EXPECT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); - EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_TOPIC).asPtr().toStdString(), ""); + EXPECT_EQ(subFb.getPropertyValue(PROPERTY_NAME_SUB_TOPIC).asPtr().toStdString(), ""); } TEST_F(MqttSubscriberFbTest, DataTransfer) @@ -468,7 +468,7 @@ TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransfer) auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_TOPIC, topic); + config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic); auto singal = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config).getSignals()[0]; auto reader = daq::PacketReader(singal); @@ -518,7 +518,7 @@ TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransferWithReconfiguring) StartUp(); auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_TOPIC, topic0); + config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic0); auto rawFB = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config); auto singal = rawFB.getSignals()[0]; auto reader = daq::PacketReader(singal); @@ -569,7 +569,7 @@ TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransferWithReconfiguring) dataToReceive.clear(); - ASSERT_NO_THROW(rawFB.setPropertyValue(PROPERTY_NAME_TOPIC, topic1)); + ASSERT_NO_THROW(rawFB.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic1)); EXPECT_EQ(rawFB.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); EXPECT_EQ(rawFB.getStatusContainer().getStatus("SubscriptionStatus"), stWaitData); From 821c758a81eb9d0e079eddf6b3ec4a8839c87580 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Thu, 15 Jan 2026 17:08:35 +0100 Subject: [PATCH 09/49] mqtt: SignalValueJSONKey property for publisher FB --- .../ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp | 2 +- .../atomic_signal_atomic_sample_handler.h | 4 ++-- .../atomic_signal_sample_arr_handler.h | 2 +- .../include/mqtt_streaming_module/constants.h | 2 +- .../group_signal_shared_ts_handler.h | 4 ++-- .../mqtt_streaming_module/handler_base.h | 18 ++++++++++++++++++ .../mqtt_streaming_module/handler_factory.h | 10 +++++----- .../signal_arr_atomic_sample_handler.h | 4 ++-- .../include/mqtt_streaming_module/types.h | 9 ++++++++- .../atomic_signal_atomic_sample_handler.cpp | 6 +++--- .../src/atomic_signal_sample_arr_handler.cpp | 6 +++--- .../src/group_signal_shared_ts_handler.cpp | 6 +++--- .../src/mqtt_publisher_fb_impl.cpp | 16 +++++++++++++--- .../src/signal_arr_atomic_sample_handler.cpp | 6 +++--- .../tests/test_mqtt_publisher_fb.cpp | 14 +++++++------- 15 files changed, 72 insertions(+), 37 deletions(-) diff --git a/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp b/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp index 755f2fe..7573789 100644 --- a/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp +++ b/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp @@ -100,7 +100,7 @@ int main(int argc, char* argv[]) auto config = availableFbs.get(fbName).createDefaultConfig(); config.setPropertyValue("QoS", 1); config.setPropertyValue("ReaderWaitPeriod", 20); - config.setPropertyValue("UseSignalNames", True); + config.setPropertyValue("SignalValueJSONKey", 2); switch (appConfig.mode) { case Mode::ATOMIC_SIGNAL_ATOMIC_SAMPLE: config.setPropertyValue("SharedTimestamp", False); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h index b438e95..c124225 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h @@ -23,7 +23,7 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE class AtomicSignalAtomicSampleHandler : public HandlerBase { public: - explicit AtomicSignalAtomicSampleHandler(bool useSignalNames); + explicit AtomicSignalAtomicSampleHandler(SignalValueJSONKey signalNamesMode); MqttData processSignalContexts(std::vector& signalContexts) override; ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const override; @@ -33,7 +33,7 @@ class AtomicSignalAtomicSampleHandler : public HandlerBase }; ListPtr getTopics(const std::vector& signalContexts) override; protected: - bool useSignalNames; + SignalValueJSONKey signalNamesMode; virtual MqttData processSignalContext(SignalContext& signalContext); void diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h index 4210209..77e8961 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h @@ -23,7 +23,7 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE class AtomicSignalSampleArrayHandler : public AtomicSignalAtomicSampleHandler { public: - explicit AtomicSignalSampleArrayHandler(bool useSignalNames, size_t packSize); + explicit AtomicSignalSampleArrayHandler(SignalValueJSONKey signalNamesMode, size_t packSize); protected: size_t packSize; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index 4c4b419..46690ed 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -42,7 +42,7 @@ static constexpr const char* PROPERTY_NAME_PUB_TOPIC_MODE = "TopicMode"; static constexpr const char* PROPERTY_NAME_PUB_TOPIC_NAME = "Topic"; static constexpr const char* PROPERTY_NAME_PUB_SHARED_TS = "SharedTimestamp"; static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES = "GroupValues"; -static constexpr const char* PROPERTY_NAME_PUB_USE_SIGNAL_NAMES = "UseSignalNames"; +static constexpr const char* PROPERTY_NAME_PUB_VALUE_FIELD_NAME = "SignalValueJSONKey"; static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE = "SamplesPerMessage"; static constexpr const char* PROPERTY_NAME_PUB_QOS = "QoS"; static constexpr const char* PROPERTY_NAME_PUB_READ_PERIOD = "ReaderWaitPeriod"; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h index a9ee6a1..2f789d8 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h @@ -33,7 +33,7 @@ struct TimestampTickStruct class GroupSignalSharedTsHandler : public HandlerBase { public: - explicit GroupSignalSharedTsHandler(bool useSignalNames, std::string topic); + explicit GroupSignalSharedTsHandler(SignalValueJSONKey signalNamesMode, std::string topic); MqttData processSignalContexts(std::vector& signalContexts) override; ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const override; @@ -41,7 +41,7 @@ class GroupSignalSharedTsHandler : public HandlerBase ListPtr getTopics(const std::vector& signalContexts) override; protected: - bool useSignalNames; + SignalValueJSONKey signalNamesMode; const size_t buffersSize; const std::string topic; std::vector dataBuffers; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h index 4288795..1b38787 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h @@ -110,6 +110,24 @@ class HandlerBase } return data; } + + static std::string buildValueFieldName(SignalValueJSONKey signalNamesMode, daq::SignalPtr signal) + { + std::string valueFieldName; + if (signalNamesMode == SignalValueJSONKey::LocalID) + { + valueFieldName = signal.getLocalId().toStdString(); + } + else if (signalNamesMode == SignalValueJSONKey::Name) + { + valueFieldName = signal.getName().toStdString(); + } + else + { + valueFieldName = signal.getGlobalId().toStdString(); + } + return valueFieldName; + } }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h b/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h index 49f7b51..755945c 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h @@ -31,23 +31,23 @@ class HandlerFactory { if (config.sharedTs) { - return std::make_unique(config.useSignalNames, + return std::make_unique(config.valueFieldName, config.topicName.empty() ? publisherFbGlobalId : config.topicName); } else if (config.topicMode == TopicMode::PerSignal) { if (config.groupValues) - return std::make_unique(config.useSignalNames, config.groupValuesPackSize); + return std::make_unique(config.valueFieldName, config.groupValuesPackSize); else - return std::make_unique(config.useSignalNames); + return std::make_unique(config.valueFieldName); } else if (config.topicMode == TopicMode::Single) { - return std::make_unique(config.useSignalNames, + return std::make_unique(config.valueFieldName, config.topicName.empty() ? publisherFbGlobalId : config.topicName); } - return std::make_unique(config.useSignalNames); + return std::make_unique(config.valueFieldName); } }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h index b3b3e86..0a30d97 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h @@ -24,7 +24,7 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE class SignalArrayAtomicSampleHandler : public HandlerBase { public: - explicit SignalArrayAtomicSampleHandler(bool useSignalNames, std::string topic); + explicit SignalArrayAtomicSampleHandler(SignalValueJSONKey signalNamesMode, std::string topic); MqttData processSignalContexts(std::vector& signalContexts) override; ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const override; @@ -32,7 +32,7 @@ class SignalArrayAtomicSampleHandler : public HandlerBase ListPtr getTopics(const std::vector& signalContexts) override; protected: - bool useSignalNames; + SignalValueJSONKey signalNamesMode; const std::string topic; std::string toString(const std::string valueFieldName, daq::DataPacketPtr packet); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/types.h b/mqtt_streaming_module/include/mqtt_streaming_module/types.h index 9879c35..9e0de21 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/types.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/types.h @@ -17,12 +17,19 @@ enum class TopicMode { _count }; +enum class SignalValueJSONKey { + GlobalID = 0, + LocalID, + Name, + _count +}; + struct PublisherFbConfig { TopicMode topicMode; std::string topicName; bool sharedTs; bool groupValues; - bool useSignalNames; + SignalValueJSONKey valueFieldName; size_t groupValuesPackSize; int qos; int periodMs; diff --git a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp index ada14af..5fad9ae 100644 --- a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp @@ -8,8 +8,8 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -AtomicSignalAtomicSampleHandler::AtomicSignalAtomicSampleHandler(bool useSignalNames) - : useSignalNames(useSignalNames) +AtomicSignalAtomicSampleHandler::AtomicSignalAtomicSampleHandler(SignalValueJSONKey signalNamesMode) + : signalNamesMode(signalNamesMode) { } @@ -146,7 +146,7 @@ std::string AtomicSignalAtomicSampleHandler::buildTopicName(const SignalContext& MqttDataSample AtomicSignalAtomicSampleHandler::processDataPacket(SignalContext& signalContext, const DataPacketPtr& dataPacket) { const auto signal = signalContext.inputPort.getSignal(); - std::string valueFieldName = useSignalNames ? signal.getName().toStdString() : signal.getGlobalId().toStdString(); + std::string valueFieldName = buildValueFieldName(signalNamesMode, signal); auto msg = toString(valueFieldName, dataPacket); std::string topic = buildTopicName(signalContext); return MqttDataSample{topic, msg}; diff --git a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp index 6956312..ad51a6c 100644 --- a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp @@ -7,8 +7,8 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -AtomicSignalSampleArrayHandler::AtomicSignalSampleArrayHandler(bool useSignalNames, size_t packSize) - : AtomicSignalAtomicSampleHandler(useSignalNames), +AtomicSignalSampleArrayHandler::AtomicSignalSampleArrayHandler(SignalValueJSONKey signalNamesMode, size_t packSize) + : AtomicSignalAtomicSampleHandler(signalNamesMode), packSize(packSize > 0 ? packSize : 1) { } @@ -92,7 +92,7 @@ MqttDataSample AtomicSignalSampleArrayHandler::processDataPackets(SignalContext& if (dataPacket.empty()) return MqttDataSample{"", ""}; const auto signal = signalContext.inputPort.getSignal(); - std::string valueFieldName = useSignalNames ? signal.getName().toStdString() : signal.getGlobalId().toStdString(); + std::string valueFieldName = buildValueFieldName(signalNamesMode, signal); auto msg = toString(valueFieldName, dataPacket); std::string topic = buildTopicName(signalContext); return MqttDataSample{topic, msg}; diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp index c3b5457..267c2d6 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp @@ -11,8 +11,8 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -GroupSignalSharedTsHandler::GroupSignalSharedTsHandler(bool useSignalNames, std::string topic) - : useSignalNames(useSignalNames), +GroupSignalSharedTsHandler::GroupSignalSharedTsHandler(SignalValueJSONKey signalNamesMode, std::string topic) + : signalNamesMode(signalNamesMode), buffersSize(1000), topic(topic) { @@ -35,7 +35,7 @@ MqttData GroupSignalSharedTsHandler::processSignalContexts(std::vector("GlobalID", "LocalID", "Name"), 0) + .setDescription("Describes how to name a JSON value field. By default it is set to GlobalID."); defaultConfig.addProperty(builder.build()); } { @@ -312,11 +312,21 @@ void MqttPublisherFbImpl::readProperties() config.sharedTs = readProperty(PROPERTY_NAME_PUB_SHARED_TS, false); config.groupValues = readProperty(PROPERTY_NAME_PUB_GROUP_VALUES, false); - config.useSignalNames = readProperty(PROPERTY_NAME_PUB_USE_SIGNAL_NAMES, false); + int tmpValueFieldName = (readProperty(PROPERTY_NAME_PUB_VALUE_FIELD_NAME, 0)); config.groupValuesPackSize = readProperty(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE, DEFAULT_PUB_PACK_SIZE); config.qos = readProperty(PROPERTY_NAME_PUB_QOS, DEFAULT_PUB_QOS); config.periodMs = readProperty(PROPERTY_NAME_PUB_READ_PERIOD, DEFAULT_PUB_READ_PERIOD); config.topicName = readProperty(PROPERTY_NAME_PUB_TOPIC_NAME, globalId.toStdString()); + if (tmpValueFieldName < static_cast(SignalValueJSONKey::_count) && tmpValueFieldName >= 0) + { + config.valueFieldName = static_cast(tmpValueFieldName); + } + else + { + config.valueFieldName = SignalValueJSONKey::GlobalID; + hasSettingError = true; + settingErrors.push_back(fmt::format("{} property has invalid value.", PROPERTY_NAME_PUB_VALUE_FIELD_NAME)); + } if (tmpTopicMode < static_cast(TopicMode::_count) && tmpTopicMode >= 0) { diff --git a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp index 34848db..a8eee01 100644 --- a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp @@ -10,8 +10,8 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -SignalArrayAtomicSampleHandler::SignalArrayAtomicSampleHandler(bool useSignalNames, std::string topic) - : useSignalNames(useSignalNames), +SignalArrayAtomicSampleHandler::SignalArrayAtomicSampleHandler(SignalValueJSONKey signalNamesMode, std::string topic) + : signalNamesMode(signalNamesMode), topic(topic) { } @@ -41,7 +41,7 @@ MqttData SignalArrayAtomicSampleHandler::processSignalContexts(std::vector(), False); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_PUB_USE_SIGNAL_NAMES)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_PUB_USE_SIGNAL_NAMES).getValueType(), CoreType::ctBool); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_PUB_USE_SIGNAL_NAMES).asPtr(), False); + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_PUB_VALUE_FIELD_NAME)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_PUB_VALUE_FIELD_NAME).getValueType(), CoreType::ctInt); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_PUB_VALUE_FIELD_NAME).asPtr(), 0u); ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE)); ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE).getValueType(), CoreType::ctInt); @@ -595,7 +595,7 @@ TEST_F(MqttPublisherFbTest, Config) config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); config.setPropertyValue(PROPERTY_NAME_PUB_GROUP_VALUES, True); - config.setPropertyValue(PROPERTY_NAME_PUB_USE_SIGNAL_NAMES, True); + config.setPropertyValue(PROPERTY_NAME_PUB_VALUE_FIELD_NAME, 1); config.setPropertyValue(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE, 3); config.setPropertyValue(PROPERTY_NAME_PUB_QOS, 2); config.setPropertyValue(PROPERTY_NAME_PUB_READ_PERIOD, 100); @@ -626,7 +626,7 @@ TEST_F(MqttPublisherFbTest, Config) EXPECT_EQ(ptr->getFbConfig().topicMode, TopicMode::Single); EXPECT_TRUE(ptr->getFbConfig().sharedTs); EXPECT_TRUE(ptr->getFbConfig().groupValues); - EXPECT_TRUE(ptr->getFbConfig().useSignalNames); + EXPECT_EQ(ptr->getFbConfig().valueFieldName, SignalValueJSONKey::LocalID); EXPECT_EQ(ptr->getFbConfig().groupValuesPackSize, 3); EXPECT_EQ(ptr->getFbConfig().qos, 2); EXPECT_EQ(ptr->getFbConfig().periodMs, 100); @@ -701,7 +701,7 @@ TEST_F(MqttPublisherFbTest, CreationWithPartialConfig) StartUp(); daq::FunctionBlockPtr fb; auto config = PropertyObject(); - config.addProperty(BoolProperty(PROPERTY_NAME_PUB_USE_SIGNAL_NAMES, True)); + config.addProperty(IntProperty(PROPERTY_NAME_PUB_READ_PERIOD, 20)); ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); SignalHelper helper; fb.getInputPorts()[0].connect(helper.signal0); From 19dc87dbfecf9b9e0489738df0003f754626fced Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Thu, 15 Jan 2026 17:45:39 +0100 Subject: [PATCH 10/49] mqtt: EnablePreviewSignal for subscriber FB --- .../include/mqtt_streaming_module/constants.h | 2 + .../mqtt_subscriber_fb_impl.h | 1 + .../src/mqtt_subscriber_fb_impl.cpp | 43 ++++++++++++++++--- .../tests/test_mqtt_subscriber_fb.cpp | 18 ++++++-- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index 46690ed..70509a9 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -33,6 +33,7 @@ static constexpr const char* PROPERTY_NAME_SUB_JSON_CONFIG = "JSONConfig"; static constexpr const char* PROPERTY_NAME_SUB_JSON_CONFIG_FILE = "JSONConfigFile"; static constexpr const char* PROPERTY_NAME_SUB_QOS = "QoS"; static constexpr const char* PROPERTY_NAME_SUB_TOPIC = "Topic"; +static constexpr const char* PROPERTY_NAME_SUB_PREVIEW_SIGNAL = "EnablePreviewSignal"; static constexpr const char* PROPERTY_NAME_DEC_VALUE_NAME = "ValueKey"; static constexpr const char* PROPERTY_NAME_DEC_TS_NAME = "DomainKey"; @@ -47,6 +48,7 @@ static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE = "Samples static constexpr const char* PROPERTY_NAME_PUB_QOS = "QoS"; static constexpr const char* PROPERTY_NAME_PUB_READ_PERIOD = "ReaderWaitPeriod"; static constexpr const char* PROPERTY_NAME_PUB_TOPICS = "Topics"; +static constexpr const char* PROPERTY_NAME_PUB_PREVIEW_SIGNAL = "EnablePreviewSignal"; static constexpr const char* SUB_FB_NAME = "MQTTSubscriberFB"; static constexpr const char* PUB_FB_NAME = "MQTTJSONPublisherFB"; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h index 55ec4fa..928f1b9 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h @@ -76,6 +76,7 @@ class MqttSubscriberFbImpl final : public FunctionBlock DictObjectPtr nestedFbTypes; std::vector nestedFunctionBlocks; SignalConfigPtr outputSignal; + bool enablePreview; DAQ_MQTT_STREAM_MODULE_API void onSignalsMessage(const mqtt::MqttAsyncClient& subscriber, const mqtt::MqttMessage& msg); diff --git a/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp index 69544f1..05c2f13 100644 --- a/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp @@ -120,6 +120,12 @@ FunctionBlockTypePtr MqttSubscriberFbImpl::CreateType() DEFAULT_SUB_QOS)); defaultConfig.addProperty(builder.build()); } + { + auto builder = BoolPropertyBuilder(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, False) + .setDescription("Enables previewing of the subscribed signal data in the function block's output signal. " + "By default it is set to false."); + defaultConfig.addProperty(builder.build()); + } { auto builder = StringPropertyBuilder(PROPERTY_NAME_SUB_JSON_CONFIG, String("")) @@ -178,6 +184,14 @@ void MqttSubscriberFbImpl::readProperties() this->qos = (qos < 0 || qos > 2) ? DEFAULT_SUB_QOS : qos; } } + if (objPtr.hasProperty(PROPERTY_NAME_SUB_PREVIEW_SIGNAL)) + { + auto previewProp = objPtr.getPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL).asPtrOrNull(); + if (previewProp.assigned()) + { + this->enablePreview = previewProp.getValue(False); + } + } if (!isPresent) { LOG_W("\'{}\' property is missing!", PROPERTY_NAME_SUB_TOPIC); @@ -289,6 +303,19 @@ void MqttSubscriberFbImpl::propertyChanged() return; } readProperties(); + if (enablePreview) + { + if (!outputSignal.assigned()) + createSignals(); + } + else + { + if (outputSignal.assigned()) + { + removeSignal(outputSignal); + outputSignal = nullptr; + } + } result = subscribeToTopic(); } @@ -372,9 +399,12 @@ void MqttSubscriberFbImpl::processMessage(const mqtt::MqttMessage& msg) std::string jsonObjStr(msg.getData().begin(), msg.getData().end()); auto acqlock = this->getAcquisitionLock2(); - const auto outputPacket = BinaryDataPacket(nullptr, outputSignal.getDescriptor(), msg.getData().size()); - memcpy(outputPacket.getData(), msg.getData().data(), msg.getData().size()); - outputSignal.sendPacket(outputPacket); + if (enablePreview) + { + const auto outputPacket = BinaryDataPacket(nullptr, outputSignal.getDescriptor(), msg.getData().size()); + memcpy(outputPacket.getData(), msg.getData().data(), msg.getData().size()); + outputSignal.sendPacket(outputPacket); + } for (const auto& fb : nestedFunctionBlocks) { @@ -390,8 +420,11 @@ void MqttSubscriberFbImpl::processMessage(const mqtt::MqttMessage& msg) void MqttSubscriberFbImpl::createSignals() { auto lock = this->getRecursiveConfigLock(); // ??? - const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::Binary).build(); - outputSignal = createAndAddSignal(DEFAULT_VALUE_SIGNAL_LOCAL_ID, signalDsc); + if (enablePreview) + { + const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::Binary).build(); + outputSignal = createAndAddSignal(DEFAULT_VALUE_SIGNAL_LOCAL_ID, signalDsc); + } } std::string MqttSubscriberFbImpl::getSubscribedTopic() const diff --git a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp index 185fbd1..4a8dbaf 100644 --- a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp @@ -24,8 +24,10 @@ class MqttSubscriberFbHelper { auto config = PropertyObject(); config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, "")); + config.addProperty(BoolProperty(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, False)); const auto fbType = FunctionBlockType(SUB_FB_NAME, SUB_FB_NAME, "", config); config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic); + config.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, True); obj = std::make_unique(NullContext(), nullptr, fbType, nullptr, config); } @@ -88,7 +90,7 @@ TEST_F(MqttSubscriberFbTest, DefaultConfig) ASSERT_TRUE(defaultConfig.assigned()); - ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 4u); + ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 5u); ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_JSON_CONFIG)); ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_JSON_CONFIG).getValueType(), CoreType::ctString); @@ -105,6 +107,10 @@ TEST_F(MqttSubscriberFbTest, DefaultConfig) ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_QOS)); ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_QOS).getValueType(), CoreType::ctInt); ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_SUB_QOS).asPtr().getValue(DEFAULT_SUB_QOS), DEFAULT_SUB_QOS); + + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_PREVIEW_SIGNAL)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_PREVIEW_SIGNAL).getValueType(), CoreType::ctBool); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL).asPtr().getValue(False), False); } TEST_F(MqttSubscriberFbTest, Config) @@ -132,7 +138,7 @@ TEST_F(MqttSubscriberFbTest, CreationWithDefaultConfig) StartUp(); daq::FunctionBlockPtr subFb; ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME)); - EXPECT_EQ(subFb.getSignals().getCount(), 1u); + EXPECT_EQ(subFb.getSignals().getCount(), 0u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Warning", daqInstance.getContext().getTypeManager())); } @@ -145,7 +151,7 @@ TEST_F(MqttSubscriberFbTest, CreationWithPartialConfig) auto config = PropertyObject(); config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, String(buildTopicName()))); ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); - EXPECT_EQ(subFb.getSignals().getCount(), 1u); + EXPECT_EQ(subFb.getSignals().getCount(), 0u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); } @@ -156,7 +162,8 @@ TEST_F(MqttSubscriberFbTest, CreationWithCustomConfig) StartUp(); daq::FunctionBlockPtr subFb; auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, String(buildTopicName())); + config.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, True); + config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, buildTopicName()); ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(subFb.getSignals().getCount(), 1u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), @@ -189,6 +196,7 @@ TEST_P(MqttSubscriberFbTopicPTest, CheckSubscriberFbTopic) auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic); + config.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, True); daq::FunctionBlockPtr fb; ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); auto signals = fb.getSignals(); @@ -469,6 +477,7 @@ TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransfer) auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic); + config.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, True); auto singal = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config).getSignals()[0]; auto reader = daq::PacketReader(singal); @@ -519,6 +528,7 @@ TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransferWithReconfiguring) auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic0); + config.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, True); auto rawFB = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config); auto singal = rawFB.getSignals()[0]; auto reader = daq::PacketReader(singal); From 71cf5c369e2855cc04998ccc833ca408b51fb383 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 16 Jan 2026 16:10:04 +0100 Subject: [PATCH 11/49] mqtt: EnablePreviewSignal for publisher FB --- .../include/mqtt_streaming_module/constants.h | 1 + .../mqtt_publisher_fb_impl.h | 3 +- .../include/mqtt_streaming_module/types.h | 10 +- .../atomic_signal_atomic_sample_handler.cpp | 2 +- .../src/atomic_signal_sample_arr_handler.cpp | 4 +- .../src/group_signal_shared_ts_handler.cpp | 2 +- .../src/mqtt_publisher_fb_impl.cpp | 105 ++++++++-- .../src/signal_arr_atomic_sample_handler.cpp | 2 +- .../tests/test_mqtt_publisher_fb.cpp | 193 +++++++++++++++++- 9 files changed, 295 insertions(+), 27 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index 70509a9..da1f998 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -49,6 +49,7 @@ static constexpr const char* PROPERTY_NAME_PUB_QOS = "QoS"; static constexpr const char* PROPERTY_NAME_PUB_READ_PERIOD = "ReaderWaitPeriod"; static constexpr const char* PROPERTY_NAME_PUB_TOPICS = "Topics"; static constexpr const char* PROPERTY_NAME_PUB_PREVIEW_SIGNAL = "EnablePreviewSignal"; +static constexpr const char* PUB_PREVIEW_SIGNAL_NAME = "PreviewSignal"; static constexpr const char* SUB_FB_NAME = "MQTTSubscriberFB"; static constexpr const char* PUB_FB_NAME = "MQTTJSONPublisherFB"; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h index 804e9c7..1c887c7 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h @@ -95,13 +95,14 @@ class MqttPublisherFbImpl final : public FunctionBlock std::atomic publishedMsgCnt; std::string lastSkippedReason; helper::utils::Timer publishingStatusTimer; + SignalConfigPtr commonPreviewSignal; static std::string generateLocalId(); void updatePublishingStatus(bool force); void initProperties(const PropertyObjectPtr& config); void readProperties(); void propertyChanged(); - void updateInputPorts(); + void updatePortsAndSignals(bool reassignPorts); void updateStatuses(); void validateInputPorts(); void updateTopics(); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/types.h b/mqtt_streaming_module/include/mqtt_streaming_module/types.h index 9e0de21..bffd918 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/types.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/types.h @@ -7,8 +7,12 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -// std::vector> -using MqttDataSample = std::pair; +struct MqttDataSample { + SignalConfigPtr previewSignal; + std::string topic; + std::string message; +}; + using MqttData = std::vector; enum class TopicMode { @@ -33,6 +37,7 @@ struct PublisherFbConfig { size_t groupValuesPackSize; int qos; int periodMs; + bool enablePreview; }; struct SignalContext @@ -40,6 +45,7 @@ struct SignalContext size_t index; InputPortConfigPtr inputPort; std::vector data; + SignalConfigPtr previewSignal; }; struct ProcedureStatus diff --git a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp index 5fad9ae..bc1aef9 100644 --- a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp @@ -149,7 +149,7 @@ MqttDataSample AtomicSignalAtomicSampleHandler::processDataPacket(SignalContext& std::string valueFieldName = buildValueFieldName(signalNamesMode, signal); auto msg = toString(valueFieldName, dataPacket); std::string topic = buildTopicName(signalContext); - return MqttDataSample{topic, msg}; + return MqttDataSample{signalContext.previewSignal, topic, msg}; } ListPtr AtomicSignalAtomicSampleHandler::getTopics(const std::vector& signalContexts) diff --git a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp index ad51a6c..99df12f 100644 --- a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp @@ -90,11 +90,11 @@ std::string AtomicSignalSampleArrayHandler::toString(const std::string valueFiel MqttDataSample AtomicSignalSampleArrayHandler::processDataPackets(SignalContext& signalContext, const std::vector& dataPacket) { if (dataPacket.empty()) - return MqttDataSample{"", ""}; + return MqttDataSample{nullptr, "", ""}; const auto signal = signalContext.inputPort.getSignal(); std::string valueFieldName = buildValueFieldName(signalNamesMode, signal); auto msg = toString(valueFieldName, dataPacket); std::string topic = buildTopicName(signalContext); - return MqttDataSample{topic, msg}; + return MqttDataSample{signalContext.previewSignal, topic, msg}; } END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp index 267c2d6..7c9c001 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp @@ -42,7 +42,7 @@ MqttData GroupSignalSharedTsHandler::processSignalContexts(std::vector #include #include +#include #include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE @@ -49,7 +50,7 @@ MqttPublisherFbImpl::MqttPublisherFbImpl(const ContextPtr& ctx, initProperties(type.createDefaultConfig()); handler = HandlerFactory::create(this->config, globalId.toStdString()); - updateInputPorts(); + updatePortsAndSignals(true); validateInputPorts(); updateStatuses(); runReaderThread(); @@ -121,6 +122,12 @@ FunctionBlockTypePtr MqttPublisherFbImpl::CreateType() DEFAULT_PUB_QOS)); defaultConfig.addProperty(builder.build()); } + { + auto builder = BoolPropertyBuilder(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, False) + .setDescription("Enables previewing of the publishing data in the function block's output signal. " + "By default it is set to false."); + defaultConfig.addProperty(builder.build()); + } { auto builder = IntPropertyBuilder(PROPERTY_NAME_PUB_READ_PERIOD, DEFAULT_PUB_READ_PERIOD) @@ -148,7 +155,7 @@ void MqttPublisherFbImpl::onConnected(const InputPortPtr& inputPort) { auto lock = this->getRecursiveConfigLock(); - updateInputPorts(); + updatePortsAndSignals(true); LOG_T("Connected to port {}", inputPort.getLocalId()); validateInputPorts(); updateTopics(); @@ -159,31 +166,93 @@ void MqttPublisherFbImpl::onDisconnected(const InputPortPtr& inputPort) { auto lock = this->getRecursiveConfigLock(); - updateInputPorts(); + updatePortsAndSignals(true); LOG_T("Disconnected from port {}", inputPort.getLocalId()); validateInputPorts(); updateTopics(); updateStatuses(); } -void MqttPublisherFbImpl::updateInputPorts() +void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) { + if (reassignPorts) + { + for (auto it = signalContexts.begin(); it != signalContexts.end();) + { + if (!it->inputPort.getSignal().assigned()) + { + removeInputPort(it->inputPort); + if (it->previewSignal.assigned() && (!commonPreviewSignal.assigned() || commonPreviewSignal != it->previewSignal)) + { + removeSignal(it->previewSignal); + } + it = signalContexts.erase(it); + } + else + { + ++it; + } + } + } + if (config.enablePreview) + { + if (config.topicMode == TopicMode::Single && !commonPreviewSignal.assigned()) + { + const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::Binary).build(); + commonPreviewSignal = createAndAddSignal(fmt::format("{}{}", PUB_PREVIEW_SIGNAL_NAME, "Common"), signalDsc); + } + } + for (auto it = signalContexts.begin(); it != signalContexts.end();) { if (!it->inputPort.getSignal().assigned()) { - removeInputPort(it->inputPort); - it = signalContexts.erase(it); + ++it; + continue; + } + if (config.enablePreview) + { + if (config.topicMode == TopicMode::Single) + { + if (it->previewSignal != commonPreviewSignal) + { + if (it->previewSignal.assigned()) + removeSignal(it->previewSignal); + it->previewSignal = commonPreviewSignal; + } + } + else if (config.topicMode == TopicMode::PerSignal) + { + if (!it->previewSignal.assigned() || (commonPreviewSignal.assigned() && commonPreviewSignal == it->previewSignal)) + { + const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::Binary).build(); + it->previewSignal = createAndAddSignal(fmt::format("{}{}", PUB_PREVIEW_SIGNAL_NAME, size_t(it->index)), signalDsc); + } + } } else - ++it; + { + if (it->previewSignal.assigned()) + { + if (!commonPreviewSignal.assigned() || commonPreviewSignal != it->previewSignal) + { + removeSignal(it->previewSignal); + } + it->previewSignal = nullptr; + } + } + ++it; + } + if (commonPreviewSignal.assigned() && (!config.enablePreview || config.topicMode == TopicMode::PerSignal)) + { + removeSignal(commonPreviewSignal); + commonPreviewSignal = nullptr; + } + if (reassignPorts) + { + const auto inputPort = createAndAddInputPort(fmt::format("Input{}", size_t(inputPortCount)), PacketReadyNotification::SameThread); + signalContexts.emplace_back(SignalContext{size_t(inputPortCount++), inputPort, {}, nullptr}); } - - const auto inputPort = createAndAddInputPort(fmt::format("Input{}", inputPortCount++), PacketReadyNotification::SameThread); - - signalContexts.emplace_back(SignalContext{0, inputPort, {}}); - for (size_t i = 0; i < signalContexts.size(); i++) - signalContexts[i].index = i; } void MqttPublisherFbImpl::updateStatuses() @@ -317,6 +386,7 @@ void MqttPublisherFbImpl::readProperties() config.qos = readProperty(PROPERTY_NAME_PUB_QOS, DEFAULT_PUB_QOS); config.periodMs = readProperty(PROPERTY_NAME_PUB_READ_PERIOD, DEFAULT_PUB_READ_PERIOD); config.topicName = readProperty(PROPERTY_NAME_PUB_TOPIC_NAME, globalId.toStdString()); + config.enablePreview = readProperty(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, false); if (tmpValueFieldName < static_cast(SignalValueJSONKey::_count) && tmpValueFieldName >= 0) { config.valueFieldName = static_cast(tmpValueFieldName); @@ -376,6 +446,7 @@ void MqttPublisherFbImpl::propertyChanged() auto lock = this->getRecursiveConfigLock(); readProperties(); handler = HandlerFactory::create(this->config, globalId.toStdString()); + updatePortsAndSignals(false); validateInputPorts(); updateTopics(); updateStatuses(); @@ -423,8 +494,14 @@ void MqttPublisherFbImpl::readerLoop() void MqttPublisherFbImpl::sendMessages(const MqttData& data) { - for (const auto& [topic, msg] : data) + for (const auto& [signal, topic, msg] : data) { + if (signal.assigned()) + { + const auto outputPacket = BinaryDataPacket(nullptr, signal.getDescriptor(), msg.size()); + memcpy(outputPacket.getData(), msg.data(), msg.size()); + signal.sendPacket(outputPacket); + } auto status = mqttClient->publish(topic, (void*)msg.c_str(), msg.length(), config.qos); if (!status.success) { diff --git a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp index a8eee01..8f25a14 100644 --- a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp @@ -50,7 +50,7 @@ MqttData SignalArrayAtomicSampleHandler::processSignalContexts(std::vector(buildClientId("_subscriberId")); + subscriber = std::make_unique(buildClientId(postfix)); return subscriber->connect("127.0.0.1"); } @@ -519,7 +521,7 @@ TEST_F(MqttPublisherFbTest, DefaultConfig) ASSERT_TRUE(defaultConfig.assigned()); - ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 8u); + ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 9u); ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_PUB_TOPIC_MODE)); ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_PUB_TOPIC_MODE).getValueType(), CoreType::ctInt); @@ -555,6 +557,10 @@ TEST_F(MqttPublisherFbTest, DefaultConfig) ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_PUB_TOPIC_NAME).getValueType(), CoreType::ctString); ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME).asPtr().toStdString(), ""); ASSERT_FALSE(defaultConfig.getProperty(PROPERTY_NAME_PUB_TOPIC_NAME).getVisible()); + + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_PUB_PREVIEW_SIGNAL)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_PUB_PREVIEW_SIGNAL).getValueType(), CoreType::ctBool); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL).asPtr().getValue(False), False); } TEST_F(MqttPublisherFbTest, PropertyVisibility) @@ -600,6 +606,7 @@ TEST_F(MqttPublisherFbTest, Config) config.setPropertyValue(PROPERTY_NAME_PUB_QOS, 2); config.setPropertyValue(PROPERTY_NAME_PUB_READ_PERIOD, 100); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME, buildTopicName()); + config.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, True); daq::FunctionBlockPtr fb; ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), @@ -626,6 +633,7 @@ TEST_F(MqttPublisherFbTest, Config) EXPECT_EQ(ptr->getFbConfig().topicMode, TopicMode::Single); EXPECT_TRUE(ptr->getFbConfig().sharedTs); EXPECT_TRUE(ptr->getFbConfig().groupValues); + EXPECT_TRUE(ptr->getFbConfig().enablePreview); EXPECT_EQ(ptr->getFbConfig().valueFieldName, SignalValueJSONKey::LocalID); EXPECT_EQ(ptr->getFbConfig().groupValuesPackSize, 3); EXPECT_EQ(ptr->getFbConfig().qos, 2); @@ -828,6 +836,49 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) } } +TEST_F(MqttPublisherFbTest, PreviewSignals) +{ + StartUp(); + + daq::FunctionBlockPtr fb; + auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0); + config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, False); + config.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, False); + ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + auto help = SignalHelper(); + + ASSERT_EQ(fb.getSignals().getCount(), 0u); + fb.getInputPorts()[0].connect(help.signal0); + ASSERT_EQ(fb.getSignals().getCount(), 0u); + + fb.getInputPorts()[1].connect(help.signal0); + ASSERT_EQ(fb.getSignals().getCount(), 0u); + ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, True)); + ASSERT_EQ(fb.getSignals().getCount(), 2u); + ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1)); + ASSERT_EQ(fb.getSignals().getCount(), 1u); + ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0)); + ASSERT_EQ(fb.getSignals().getCount(), 2u); + ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1)); + + // disconnection + fb.getInputPorts()[1].disconnect(); + ASSERT_EQ(fb.getSignals().getCount(), 1u); + // disconnection + fb.getInputPorts()[0].disconnect(); + ASSERT_EQ(fb.getSignals().getCount(), 1u); + ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, False)); + ASSERT_EQ(fb.getSignals().getCount(), 0u); + ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0)); + fb.getInputPorts()[0].connect(help.signal0); + fb.getInputPorts()[1].connect(help.signal1); + fb.getInputPorts()[2].connect(help.signal0); + ASSERT_EQ(fb.getSignals().getCount(), 0u); + ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, True)); + ASSERT_EQ(fb.getSignals().getCount(), 3u); +} + TEST_F(MqttPublisherFbTest, TopicsList) { StartUp(); @@ -904,7 +955,74 @@ TEST_F(MqttPublisherFbTest, WrongConfig) daqInstance.getContext().getTypeManager())); } -TEST_P(MqttPublisherFbPTest, TransferSingle) +TEST_F(MqttPublisherFbTest, TransferSingle0) +{ + const size_t sampleCnt0 = 15; + const size_t sampleCnt1 = sampleCnt0 * 2; + SignalHelper helpDouble{}; + SignalHelper helpUint{}; + StartUp(); + + ASSERT_NO_THROW(CreatePublisherFB()); + fb.getInputPorts()[0].connect(helpDouble.signal0); + fb.getInputPorts()[1].connect(helpUint.signal1); + + auto signalList = List(); + fb->getSignals(&signalList); + auto reader0 = daq::PacketReader(signalList[0]); + auto reader1 = daq::PacketReader(signalList[1]); + std::vector dataToReceive0; + std::vector dataToReceive1; + ASSERT_TRUE(CreateSubscriber("_0")); + + const auto data0 = helpDouble.generateTestData(sampleCnt0); + const std::vector messages0 = expectedMsgsForSingle(helpDouble.signal0.getGlobalId().toStdString(), data0); + const std::string topic0 = helpDouble.signal0.getGlobalId().toStdString(); + auto ok = transfer(topic0, messages0, helpDouble, data0); + ASSERT_TRUE(ok); + + ASSERT_TRUE(CreateSubscriber("_1")); + const auto data1 = helpUint.generateTestData(sampleCnt1); + const std::vector messages1 = expectedMsgsForSingle(helpUint.signal1.getGlobalId().toStdString(), data1); + const std::string topic1 = helpUint.signal1.getGlobalId().toStdString(); + ok = transfer(topic1, messages1, helpUint, data1); + ASSERT_TRUE(ok); + ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); + ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SET_STATUS_NAME), + EnumerationWithIntValue(MQTT_PUB_FB_SET_STATUS_TYPE, + static_cast(MqttPublisherFbImpl::SettingStatus::Valid), + daqInstance.getContext().getTypeManager())); + ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_PUB_STATUS_NAME), + EnumerationWithIntValue(MQTT_PUB_FB_PUB_STATUS_TYPE, + static_cast(MqttPublisherFbImpl::PublishingStatus::Ok), + daqInstance.getContext().getTypeManager())); + + auto lambdaReader = [](PacketReaderPtr reader, const std::vector& messages) + { + std::vector dataToReceive; + while (!reader.getEmpty()) + { + auto packet = reader.read(); + if (const auto eventPacket = packet.asPtrOrNull(); eventPacket.assigned()) + { + continue; + } + if (const auto dataPacket = packet.asPtrOrNull(); dataPacket.assigned()) + { + std::vector readData(dataPacket.getDataSize()); + memcpy(readData.data(), dataPacket.getData(), dataPacket.getDataSize()); + dataToReceive.emplace_back(readData.cbegin(), readData.cend()); + } + } + ASSERT_EQ(messages.size(), dataToReceive.size()); + ASSERT_EQ(messages, dataToReceive); + }; + lambdaReader(reader0, messages0); + lambdaReader(reader1, messages1); +} + +TEST_P(MqttPublisherFbPTest, TransferSingle1) { const size_t sampleCnt = 15; H param = GetParam(); @@ -914,8 +1032,12 @@ TEST_P(MqttPublisherFbPTest, TransferSingle) StartUp(); ASSERT_NO_THROW(CreatePublisherFB()); - fb.getInputPorts()[0].connect(help.signal0); + + auto signalList = List(); + fb->getSignals(&signalList); + auto reader = daq::PacketReader(signalList[0]); + std::vector dataToReceive; ASSERT_TRUE(CreateSubscriber()); const auto data = help.generateTestData(sampleCnt); @@ -933,6 +1055,23 @@ TEST_P(MqttPublisherFbPTest, TransferSingle) EnumerationWithIntValue(MQTT_PUB_FB_PUB_STATUS_TYPE, static_cast(MqttPublisherFbImpl::PublishingStatus::Ok), daqInstance.getContext().getTypeManager())); + + while (!reader.getEmpty()) + { + auto packet = reader.read(); + if (const auto eventPacket = packet.asPtrOrNull(); eventPacket.assigned()) + { + continue; + } + if (const auto dataPacket = packet.asPtrOrNull(); dataPacket.assigned()) + { + std::vector readData(dataPacket.getDataSize()); + memcpy(readData.data(), dataPacket.getData(), dataPacket.getDataSize()); + dataToReceive.emplace_back(readData.cbegin(), readData.cend()); + } + } + ASSERT_EQ(messages.size(), dataToReceive.size()); + ASSERT_EQ(messages, dataToReceive); }, param); } @@ -950,6 +1089,11 @@ TEST_P(MqttPublisherFbPTest, TransferSingleGroupValues) ASSERT_NO_THROW(CreatePublisherFB(false, false, true, false, buildTopicName(), packSize)); fb.getInputPorts()[0].connect(help.signal0); + + auto signalList = List(); + fb->getSignals(&signalList); + auto reader = daq::PacketReader(signalList[0]); + std::vector dataToReceive; ASSERT_TRUE(CreateSubscriber()); const auto data = help.generateTestData(sampleCnt); @@ -968,6 +1112,23 @@ TEST_P(MqttPublisherFbPTest, TransferSingleGroupValues) EnumerationWithIntValue(MQTT_PUB_FB_PUB_STATUS_TYPE, static_cast(MqttPublisherFbImpl::PublishingStatus::Ok), daqInstance.getContext().getTypeManager())); + + while (!reader.getEmpty()) + { + auto packet = reader.read(); + if (const auto eventPacket = packet.asPtrOrNull(); eventPacket.assigned()) + { + continue; + } + if (const auto dataPacket = packet.asPtrOrNull(); dataPacket.assigned()) + { + std::vector readData(dataPacket.getDataSize()); + memcpy(readData.data(), dataPacket.getData(), dataPacket.getDataSize()); + dataToReceive.emplace_back(readData.cbegin(), readData.cend()); + } + } + ASSERT_EQ(messages.size(), dataToReceive.size()); + ASSERT_EQ(messages, dataToReceive); }, param); } @@ -985,6 +1146,11 @@ TEST_P(MqttPublisherFbPTest, TransferSharedTs) fb.getInputPorts()[0].connect(help.signal0); fb.getInputPorts()[1].connect(help.signal1); + + auto signalList = List(); + fb->getSignals(&signalList); + auto reader = daq::PacketReader(signalList[0]); + std::vector dataToReceive; ASSERT_TRUE(CreateSubscriber()); const auto data = help.generateTestData(sampleCnt); @@ -1002,6 +1168,23 @@ TEST_P(MqttPublisherFbPTest, TransferSharedTs) EnumerationWithIntValue(MQTT_PUB_FB_PUB_STATUS_TYPE, static_cast(MqttPublisherFbImpl::PublishingStatus::Ok), daqInstance.getContext().getTypeManager())); + + while (!reader.getEmpty()) + { + auto packet = reader.read(); + if (const auto eventPacket = packet.asPtrOrNull(); eventPacket.assigned()) + { + continue; + } + if (const auto dataPacket = packet.asPtrOrNull(); dataPacket.assigned()) + { + std::vector readData(dataPacket.getDataSize()); + memcpy(readData.data(), dataPacket.getData(), dataPacket.getDataSize()); + dataToReceive.emplace_back(readData.cbegin(), readData.cend()); + } + } + ASSERT_EQ(messages.size(), dataToReceive.size()); + ASSERT_EQ(messages, dataToReceive); }, param); } From 3b67aa3c98fad0c90f17b659042b4e35e8237a48 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 16 Jan 2026 17:16:48 +0100 Subject: [PATCH 12/49] mqtt: Schema property for publisher FB --- .../atomic_signal_atomic_sample_handler.h | 1 + .../atomic_signal_sample_arr_handler.h | 1 + .../include/mqtt_streaming_module/constants.h | 1 + .../group_signal_shared_ts_handler.h | 1 + .../mqtt_streaming_module/handler_base.h | 19 +++++++++++++++++++ .../mqtt_publisher_fb_impl.h | 1 + .../signal_arr_atomic_sample_handler.h | 1 + .../atomic_signal_atomic_sample_handler.cpp | 5 +++++ .../src/atomic_signal_sample_arr_handler.cpp | 16 ++++++++++++++++ .../src/group_signal_shared_ts_handler.cpp | 5 +++++ .../src/mqtt_publisher_fb_impl.cpp | 16 +++++++++++++++- .../src/signal_arr_atomic_sample_handler.cpp | 5 +++++ .../tests/test_mqtt_publisher_fb.cpp | 2 +- 13 files changed, 72 insertions(+), 2 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h index c124225..1a52bd9 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h @@ -32,6 +32,7 @@ class AtomicSignalAtomicSampleHandler : public HandlerBase return ProcedureStatus{true, {}}; }; ListPtr getTopics(const std::vector& signalContexts) override; + std::string getSchema() override; protected: SignalValueJSONKey signalNamesMode; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h index 77e8961..724a6cb 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h @@ -24,6 +24,7 @@ class AtomicSignalSampleArrayHandler : public AtomicSignalAtomicSampleHandler { public: explicit AtomicSignalSampleArrayHandler(SignalValueJSONKey signalNamesMode, size_t packSize); + std::string getSchema() override; protected: size_t packSize; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index da1f998..d66272d 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -48,6 +48,7 @@ static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE = "Samples static constexpr const char* PROPERTY_NAME_PUB_QOS = "QoS"; static constexpr const char* PROPERTY_NAME_PUB_READ_PERIOD = "ReaderWaitPeriod"; static constexpr const char* PROPERTY_NAME_PUB_TOPICS = "Topics"; +static constexpr const char* PROPERTY_NAME_PUB_SCHEMA = "Schema"; static constexpr const char* PROPERTY_NAME_PUB_PREVIEW_SIGNAL = "EnablePreviewSignal"; static constexpr const char* PUB_PREVIEW_SIGNAL_NAME = "PreviewSignal"; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h index 2f789d8..e8c9b7b 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h @@ -39,6 +39,7 @@ class GroupSignalSharedTsHandler : public HandlerBase ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const override; ProcedureStatus signalListChanged(std::vector& signalContexts) override; ListPtr getTopics(const std::vector& signalContexts) override; + std::string getSchema() override; protected: SignalValueJSONKey signalNamesMode; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h index 1b38787..f9fa8b9 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h @@ -31,6 +31,7 @@ class HandlerBase virtual ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const = 0; virtual ProcedureStatus signalListChanged(std::vector& signalContexts) = 0; virtual ListPtr getTopics(const std::vector& signalContexts) = 0; + virtual std::string getSchema() = 0; protected: static std::pair calculateRatio(const DataDescriptorPtr descriptor) @@ -128,6 +129,24 @@ class HandlerBase } return valueFieldName; } + + static std::string buildValueFieldNameForSchema(SignalValueJSONKey signalNamesMode, std::string postfix = "") + { + std::string valueFieldName; + if (signalNamesMode == SignalValueJSONKey::LocalID) + { + valueFieldName = fmt::format("", postfix); + } + else if (signalNamesMode == SignalValueJSONKey::Name) + { + valueFieldName = fmt::format("", postfix); + } + else + { + valueFieldName = fmt::format("", postfix); + } + return valueFieldName; + } }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h index 1c887c7..7b637fe 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h @@ -106,6 +106,7 @@ class MqttPublisherFbImpl final : public FunctionBlock void updateStatuses(); void validateInputPorts(); void updateTopics(); + void updateSchema(); template retT readProperty(const std::string& propertyName, const retT defaultValue); void runReaderThread(); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h index 0a30d97..e0b9092 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h @@ -30,6 +30,7 @@ class SignalArrayAtomicSampleHandler : public HandlerBase ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const override; ProcedureStatus signalListChanged(std::vector& signalContexts) override; ListPtr getTopics(const std::vector& signalContexts) override; + std::string getSchema() override; protected: SignalValueJSONKey signalNamesMode; diff --git a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp index bc1aef9..830ad62 100644 --- a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp @@ -163,6 +163,11 @@ ListPtr AtomicSignalAtomicSampleHandler::getTopics(const std::vector, \"timestamp\": }}", buildValueFieldNameForSchema(signalNamesMode)); }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp index 99df12f..af5907c 100644 --- a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp @@ -13,6 +13,22 @@ AtomicSignalSampleArrayHandler::AtomicSignalSampleArrayHandler(SignalValueJSONKe { } +std::string AtomicSignalSampleArrayHandler::getSchema() +{ + if (packSize == 1) + { + return fmt::format("{{\"{}\" : [], \"timestamp\": []}}", buildValueFieldNameForSchema(signalNamesMode)); + } + else if (packSize == 2) + { + return fmt::format("{{\"{}\" : [, ], \"timestamp\": [, ]}}", buildValueFieldNameForSchema(signalNamesMode)); + } + else + { + return fmt::format("{{\"{}\" : [, ..., ], \"timestamp\": [, ..., ]}}", buildValueFieldNameForSchema(signalNamesMode), packSize - 1, packSize - 1); + } +} + MqttData AtomicSignalSampleArrayHandler::processSignalContext(SignalContext& signalContext) { MqttData messages; diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp index 7c9c001..bff3642 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp @@ -175,6 +175,11 @@ ListPtr GroupSignalSharedTsHandler::getTopics(const std::vector, ..., \"{}\" : , \"timestamp\": }}", buildValueFieldNameForSchema(signalNamesMode, "_0"), buildValueFieldNameForSchema(signalNamesMode, "_N")); +} + std::string GroupSignalSharedTsHandler::toString(const SampleType sampleType, const std::string& valueFieldName, void* data, SizeT offset) { switch (sampleType) diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index 9c1cc8e..494e04a 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -52,6 +52,7 @@ MqttPublisherFbImpl::MqttPublisherFbImpl(const ContextPtr& ctx, handler = HandlerFactory::create(this->config, globalId.toStdString()); updatePortsAndSignals(true); validateInputPorts(); + updateSchema(); updateStatuses(); runReaderThread(); } @@ -341,6 +342,12 @@ void MqttPublisherFbImpl::updateTopics() objPtr.getProperty(PROPERTY_NAME_PUB_TOPICS).asPtr().setValueProtected(topics); } +void MqttPublisherFbImpl::updateSchema() +{ + const auto schema = handler->getSchema(); + objPtr.getProperty(PROPERTY_NAME_PUB_SCHEMA).asPtr().setValueProtected(String(schema)); +} + void MqttPublisherFbImpl::initProperties(const PropertyObjectPtr& config) { for (const auto& prop : config.getAllProperties()) @@ -370,7 +377,13 @@ void MqttPublisherFbImpl::initProperties(const PropertyObjectPtr& config) objPtr.addProperty(builder.build()); } - } + } + { + auto builder = + StringPropertyBuilder(PROPERTY_NAME_PUB_SCHEMA, "").setReadOnly(true).setDescription("Publishing JSON schema."); + + objPtr.addProperty(builder.build()); + } readProperties(); } @@ -449,6 +462,7 @@ void MqttPublisherFbImpl::propertyChanged() updatePortsAndSignals(false); validateInputPorts(); updateTopics(); + updateSchema(); updateStatuses(); } diff --git a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp index 8f25a14..c6cfe23 100644 --- a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp @@ -128,6 +128,11 @@ ListPtr SignalArrayAtomicSampleHandler::getTopics(const std::vector, \"timestamp\": }}, ..., {{\"{}\" : , \"timestamp\": }}]", buildValueFieldNameForSchema(signalNamesMode, "_0"), buildValueFieldNameForSchema(signalNamesMode, "_N")); +} + std::string SignalArrayAtomicSampleHandler::toString(const std::string valueFieldName, daq::DataPacketPtr packet) { std::string result; diff --git a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp index d99555f..7e68217 100644 --- a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp @@ -620,7 +620,7 @@ TEST_F(MqttPublisherFbTest, Config) static_cast(MqttPublisherFbImpl::SettingStatus::Valid), daqInstance.getContext().getTypeManager())); const auto allProperties = fb.getAllProperties(); - ASSERT_EQ(allProperties.getCount(), config.getAllProperties().getCount() + 1); // +1 for Topics property + ASSERT_EQ(allProperties.getCount(), config.getAllProperties().getCount() + 2); // +2 for Topics property, Schema property for (const auto& pror : config.getAllProperties()) { From 828314e5abce2b7ab57b9a59223b5ee54ec7be80 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Mon, 19 Jan 2026 14:32:15 +0100 Subject: [PATCH 13/49] mqtt: renaming, README updating --- README.md | 80 +++++++++---------- .../ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp | 8 +- ...t_root_fb_impl.h => mqtt_client_fb_impl.h} | 6 +- mqtt_streaming_module/src/CMakeLists.txt | 8 +- ...ot_fb_impl.cpp => mqtt_client_fb_impl.cpp} | 30 +++---- .../src/mqtt_json_decoder_fb_impl.cpp | 23 ++++-- .../src/mqtt_publisher_fb_impl.cpp | 6 +- .../src/mqtt_streaming_module_impl.cpp | 6 +- mqtt_streaming_module/tests/CMakeLists.txt | 2 +- .../tests/test_daq_test_helper.h | 18 ++--- ...tt_root_fb.cpp => test_mqtt_client_fb.cpp} | 2 +- .../tests/test_mqtt_json_decoder_fb.cpp | 8 +- .../tests/test_mqtt_publisher_fb.cpp | 60 +++++++------- .../tests/test_mqtt_subscriber_fb.cpp | 64 +++++++-------- 14 files changed, 163 insertions(+), 158 deletions(-) rename mqtt_streaming_module/include/mqtt_streaming_module/{mqtt_root_fb_impl.h => mqtt_client_fb_impl.h} (92%) rename mqtt_streaming_module/src/{mqtt_root_fb_impl.cpp => mqtt_client_fb_impl.cpp} (91%) rename mqtt_streaming_module/tests/{test_mqtt_root_fb.cpp => test_mqtt_client_fb.cpp} (98%) diff --git a/README.md b/README.md index d198b00..a94327e 100644 --- a/README.md +++ b/README.md @@ -2,26 +2,26 @@ ## Description -MQTT module for the [OpenDAQ SDK](https://github.com/openDAQ/openDAQ). The module is designed for software communication via the *MQTT 3.1.1* protocol using an external broker. It allows publishing and subscribing to openDAQ signal data over MQTT. The module consists of five key openDAQ components: the *MQTT root function block* (**rootMqttFb**) and its nested function blocks — the *publisher* (**publisherMqttFb**) with its nested block *JSON decoder* (**jsonDecoderMqttFb**) , the *raw subscriber* (**rawMqttFb**), and the *JSON subscriber* (**jsonMqttFb**). +MQTT module for the [OpenDAQ SDK](https://github.com/openDAQ/openDAQ). The module is designed for software communication via the *MQTT 3.1.1* protocol using an external broker. It allows publishing and subscribing to openDAQ signal data over MQTT. The module consists of five key openDAQ components: the *MQTT client function block* (**MQTTClientFB**) and its nested function blocks — the *publisher* (**MQTTJSONPublisherFB**) and the *subscriber* (**MQTTSubscriberFB**) with its nested block *JSON decoder* (**MQTTJSONDecoderFB**). ### Functional - Connecting to an MQTT broker; -- Publishing openDAQ signals as MQTT messages (*publisher FB*); -- Subscribing to MQTT topics and converting incoming messages into openDAQ signals (*raw FB and JSON FB + JSON decoder FB*); +- Publishing openDAQ signals as MQTT JSON messages (*publisher FB*); +- Subscribing to MQTT topics and converting incoming messages into openDAQ signals (*subscriber FB, JSON decoder FB*); - Support for multiple message types and formats for both publishing and subscribing; - A set of examples and *gtests* for verifying functionality. ### Key components -1) **MQTT root Function Block (rootMqttFb)**: - - **Where**: *mqtt_streaming_module/src/mqtt_root_fb_impl.cpp, include/mqtt_streaming_module/...* +1) **MQTT client Function Block (MQTTClientFB)**: + - **Where**: *mqtt_streaming_module/src/mqtt_client_fb_impl.cpp, include/mqtt_streaming_module/...* - **Purpose**: Represents the MQTT broker as an openDAQ function block - the connection point through which function blocks are created. - **Main properties:** - - *MqttBrokerAddress* (string) - MQTT broker address. It can be an IP address or a hostname. By default, it is set to *"127.0.0.1"*. - - *MqttBrokerPort* (integer) - Port number for the MQTT broker connection. By default, it is set to *1883*. - - *MqttUsername* (string) — Username for MQTT broker authentication. By default, it is empty. - - *MqttPassword* (string) — Password for MQTT broker authentication. By default, it is empty. - - *ConnectTimeout* (integer) — Timeout in milliseconds for the initial connection to the MQTT broker. If the connection fails, an exception is thrown. By default, it is set to *3000 ms*. -2) **Publisher MQTT Function Block (publisherMqttFb)**: + - *BrokerAddress* (string) - MQTT broker address. It can be an IP address or a hostname. By default, it is set to *"127.0.0.1"*. + - *BrokerPort* (integer) - Port number for the MQTT broker connection. By default, it is set to *1883*. + - *Username* (string) — Username for MQTT broker authentication. By default, it is empty. + - *Password* (string) — Password for MQTT broker authentication. By default, it is empty. + - *ConnectionTimeout* (integer) — Timeout in milliseconds for the initial connection to the MQTT broker. If the connection fails, an exception is thrown. By default, it is set to *3000 ms*. +2) **MQTT publisher Function Block (MQTTJSONPublisherFB)**: - **Where**: *include/mqtt_streaming_module/mqtt_publisher_fb_impl.h, src/mqtt_publisher_fb_impl.cpp* - **Purpose**: Publishes openDAQ signal data to MQTT topics. There are **four** general data publishing schemes: 1) One MQTT message per signal / one message per sample / one topic per signal / one timestamp for each sample. Example: *{"AI0": 1.1, "timestamp": 1763716736100000}* @@ -35,38 +35,35 @@ MQTT module for the [OpenDAQ SDK](https://github.com/openDAQ/openDAQ). The modul The schemes are configured through combinations of properties. - **Main properties**: - - *TopicMode* (list) — Selects whether to publish all signals to separate MQTT topics (one per signal, *single-topic mode*) or to a single topic (*multiple-topic mode*), one for all signals. Choose *0* for *single-topic* mode and *1* for *multiple-topic* mode. By default, it is set to *single-topic* mode. - - *MqttQoS* (integer) — MQTT Quality of Service level. It can be *0* (at most once), *1* (at least once), or *2* (exactly once). By default, it is set to *1*. - - *Topic* (string) — Topic name for publishing in multiple-topic mode. If left empty, the Publisher's *Global ID* is used as the topic name. - - *SharedTimestamp* (bool) — Enables the use of a shared timestamp for all signals when publishing in *multiple-topic* mode. By default, it is set to *false*. - - *GroupValues* (bool) — Enables the use of a sample pack for a signal when publishing in *single-topic* mode. By default, it is set to *false*. - - *UseSignalNames* (bool) — Uses signal names as JSON field names instead of Global IDs. By default, it is set to *false*. - - *GroupValuesPackSize* (integer) — Sets the size of the sample pack when publishing grouped values in *single-topic* mode. By default, it is set to *1*. - - *ReaderPeriod* (integer) — Polling period in milliseconds, specifying how often the server collects and publishes the connected signals’ data to an MQTT broker. By default, it is set to *20 ms*. + - *TopicMode* (list) — Selects whether to publish all signals to separate MQTT topics (one per signal, *TopicPerSignal mode*) or to a single topic (*SingleTopic mode*), one for all signals. Choose *0* for *TopicPerSignal* mode and *1* for *SingleTopic* mode. By default, it is set to *TopicPerSignal* mode. + - *QoS* (list) — MQTT Quality of Service level. It can be *0* (at most once), *1* (at least once), or *2* (exactly once). By default, it is set to *1*. + - *Topic* (string) — Topic name for publishing in *SingleTopic* mode. If left empty, the Publisher's *Global ID* is used as the topic name. + - *Topics* (list of strings, read-only) — Contains a list of topics used for publishing data in the *TopicPerSignal* mode. The order in the list is the same as the input ports order. + - *SharedTimestamp* (bool) — Enables the use of a shared timestamp for all signals when publishing in *SingleTopic* mode. By default, it is set to *false*. + - *GroupValues* (bool) — Enables the use of a sample pack for a signal when publishing in *TopicPerSignal* mode. By default, it is set to *false*. + - *SignalValueJSONKey* (list) — Describes how to name a JSON value field. By default it is set to *GlobalID*. + - *SamplesPerMessage* (integer) — Sets the size of the sample pack when publishing grouped values in *TopicPerSignal* mode. By default, it is set to *1*. + - *ReaderWaitPeriod* (integer) — Polling period in milliseconds, specifying how often the server calls an internal reader to collect and publish the connected signals’ data to an MQTT broker. By default, it is set to *20 ms*. + - *EnablePreviewSignal* (bool) — Enable the creation of preview signals: one signal in *SingleTopic* mode and one signal per connected input port in *TopicPerSignal* mode. These signals contain the same JSON string data that is published to MQTT topics. + - *Schema* (string, read-only) - Describes the general representation of a JSON data packet according to the current function block settings. To configure the publishing schemes, set the properties as follows: 1) *TopicMode(0), SharedTimestamp(false), GroupValues(false)*; - 2) *TopicMode(0), SharedTimestamp(false), GroupValues(true), GroupValuesPackSize()*; + 2) *TopicMode(0), SharedTimestamp(false), GroupValues(true), SamplesPerMessage()*; 3) *TopicMode(1), SharedTimestamp(false), GroupValues(false)*; 4) *TopicMode(1), SharedTimestamp(true), GroupValues(false)*; -3) **Raw MQTT Function Block (rawMqttFb)**: +3) **MQTT subscriber Function Block (MQTTSubscriberFB)**: - - **Where**: *include/mqtt_streaming_module/mqtt_raw_receiver_fb_impl.h, src/mqtt_raw_receiver_fb_impl.cpp* - - **Purpose**: Subscribes to raw MQTT messages and converts them into openDAQ signals (binary data) without any parsing — suitable for binary/unstructured messages or simple numeric values. + - **Where**: *include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h, src/mqtt_subscriber_fb_impl.cpp* + - **Purpose**: Subscribes to raw MQTT messages and converts them into openDAQ signals (binary data) without any parsing — suitable for binary/unstructured messages, simple numeric values or for further processing by nested blocks (**MQTTJSONDecoderFB**). - **Main properties**: - *Topic* (string) — MQTT topic to subscribe to for receiving raw binary data. - - *MqttQoS* (integer) — MQTT Quality of Service level. It can be *0* (at most once), *1* (at least once), or *2* (exactly once). By default, it is set to *1*. - -4) **JSON MQTT Function Block (jsonMqttFb)**: - - **Where**: *include/mqtt_streaming_module/mqtt_json_receiver_fb_impl.h, src/mqtt_json_receiver_fb_impl.cpp* - - **Purpose**: Subscribes to MQTT topics, extracts values and timestamps from MQTT JSON messages via nested *JSON decoder MQTT Function Blocks*. - - **Main properties**: - - *Topic* (string) — MQTT topic to subscribe to for receiving JSON data. - - *MqttQoS* (integer) — MQTT Quality of Service level. It can be *0* (at most once), *1* (at least once), or *2* (exactly once). By default, it is set to *1*. - - *JsonConfigFile* (string) — path to file with **JSON configuration string**. See the *JsonConfig* property for more details. This property could be set only at creation. It is not visible. - - *JsonConfig* (string) — **JSON configuration string** that defines the MQTT topic and the corresponding signals to subscribe to. This property could be set only at creation. It is not visible. A typical string structure: + - *QoS* (list) — MQTT Quality of Service level. It can be *0* (at most once), *1* (at least once), or *2* (exactly once). By default, it is set to *1*. + - *EnablePreviewSignal* (bool) — Enable the creation of a preview signal. This signal contains the raw binary data received from an MQTT topic. + - *JSONConfigFile* (string) — path to file with **JSON configuration string**. See the *JSONConfig* property for more details. This property could be set only at creation. It is not visible. + - *JSONConfig* (string) — **JSON configuration string** that defines the MQTT topic and the corresponding signals to subscribe to. This property could be set only at creation. It is not visible. A typical string structure: ```json { "":[ @@ -117,21 +114,20 @@ MQTT module for the [OpenDAQ SDK](https://github.com/openDAQ/openDAQ). The modul ] } ``` - In this example, the *JSON MQTT Function Block* creates 3 nested *jsonDecoderMqttFb*, subscribes to the *"/mirip/UNet3AC2/sensor/data"* topic, and extracts 3 signal samples from each message (one sample per *jsonDecoderMqttFb*). The signals are named *“temp”*, *“humidity”*, and *“tds”*. The *“temp”* signal is created with a domain signal because the *“Timestamp”* field is present. Each domain-signal sample is extracted from the *“ts”* field of the JSON MQTT message. The value of the *“ts”* field (the timestamp field) may be in **ISO8601** format or **Unix epoch time** in seconds, milliseconds, or microseconds. The value of the *“temp”* signal sample is extracted from the *“temp”* field of the JSON message. The unit of the values is “°C”. + In this example, the *MQTT subscriber Function Block* creates 3 nested *MQTT JSON decoder Function Block*, subscribes to the *"/mirip/UNet3AC2/sensor/data"* topic, and extracts 3 signal samples from each message (one sample per *jsonDecoderMqttFb*). The *“temp”* signal is created with a domain signal because the *“Timestamp”* field is present. Each domain-signal sample is extracted from the *“ts”* field of the JSON MQTT message. The value of the *“ts”* field (the timestamp field) may be in **ISO8601** format or **Unix epoch time** in seconds, milliseconds, or microseconds. The value of the *“temp”* signal sample is extracted from the *“temp”* field of the JSON message. The unit of the values is “°C”. Example of JSON MQTT message for this configuration: ```json {"ts":"2025-10-08 20:35:43", "bdn":"SanbonFishTank3", "temp":27.20,"humi":72.40, "tds_value":275.22, "fan_status":"off", "auto_mode":"on", "fan_comp":"26.3", "humi_comp":"55"} ``` -5) **JSON decoder MQTT Function Block (jsonDecoderMqttFb)**: +4) **MQTT JSON decoder Function Block (MQTTJSONDecoderFB)**: - **Where**: *include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h, src/mqtt_json_decoder_fb_impl.cpp* - **Purpose**: To parse JSON string data to extract a value and a timestamp, and to send data and domain samples based on this data. - **Main properties**: - - *ValueName* (string) — indicates which JSON field contains the sample value. - - *TimestampName* (string) — indicates which JSON field contains the timestamp. - - *Unit* (string) — describes the unit symbol of the decoded signal value. - - *SignalName* (string) — specifies the name to assign to the signal created by a *jsonDecoderMqttFb*. + - *ValueKey* (string) — Specifies the JSON field name from which value data will be extracted. This property is required. It should be contained in the incoming JSON messages. Otherwise, a parsing error will occur. + - *DomainKey* (string) — Specifies the JSON field name from which timestamp will be extracted. This property is optional. If it is set it should be contained in the incoming JSON messages. Otherwise, a parsing error will occur. + - *Unit* (string) — Specifies the unit symbol for the decoded value. This property is optional. --- ## Building MQTTStreamingModule @@ -179,15 +175,15 @@ cmake --build . ## Examples There are 3 example C++ application: - - **custom-mqtt-sub** - demonstrates how to work with the *JSON receiver MQTT FB* and *JSON decoder MQTT FB*. The application creates an *MQTT root FB* and a *JSON MQTT FB* to receive JSON MQTT messages, parse them, and create openDAQ signals to send the parsed data. The application also creates *packet readers* for all FB signals and prints the samples to standard output. The *JsonConfigFile* property of the JSON MQTT FB is set to the value of path whose is provided as a command-line argument when the application starts (see the **Key components** section). Usage: + - **custom-mqtt-sub** - demonstrates how to work with the *MQTT subscriber MQTT FB* and *MQTT JSON decoder MQTT FB*. The application creates an *MQTTClientFB* and a *MQTTSubscriberFB* with nested *MQTTJSONDecoderFB* function blocks to receive JSON MQTT messages, parse them, and create openDAQ signals to send the parsed data. The application also creates *packet readers* for all FB signals and prints the samples to standard output. The *JSONConfigFile* property of the *MQTTSubscriberFB* is set to the value of path whose is provided as a command-line argument when the application starts (see the **Key components** section). Usage: ```bash ./custom-mqtt-sub --address broker.emqx.io examples/custom-mqtt-sub/public-example0.json ``` - - **raw-mqtt-sub** - demonstrates how to work with the *raw MQTT FB*. The application creates an *MQTT root FB* and a *raw MQTT FB* to receive MQTT messages and create openDAQ signals to send the data as binary packets. The application also creates packet readers for all FB signals and prints the binary packets as strings to standard output. The *Topic* property of the raw MQTT FB is filled from the application arguments. Usage: + - **raw-mqtt-sub** - demonstrates how to work with the *MQTT subscriber MQTT FB* in a raw mode (binary data without parsing). The application creates an *MQTTClientFB* and a *MQTTSubscriberFB* to receive MQTT messages and create openDAQ signals to send the data as binary packets. The application also creates packet readers for all FB signals and prints the binary packets as strings to standard output. The *Topic* property of the *MQTTSubscriberFB* is filled from the application arguments. Usage: ```bash ./raw-mqtt-sub --address broker.emqx.io /mirip/UNet3AC2/sensor/data ``` - - **ref-dev-mqtt-pub** - demonstrates how to work with the *publisher MQTT FB*. The application creates an *openDAQ ref-device* with four channels, an *MQTT root FB*, and a *publisher MQTT FB* to publish JSON MQTT messages with the channels’ data. The properties of the *publisher MQTT FB* are set according to the selected mode, which can be specified via the *--mode* option. Posible values are: + - **ref-dev-mqtt-pub** - demonstrates how to work with the *MQTTJSONPublisherFB*. The application creates an *openDAQ ref-device* with four channels, an *MQTTClientFB*, and a *MQTTJSONPublisherFB* to publish JSON MQTT messages with the channels’ data. The properties of the *MQTTJSONPublisherFB* are set according to the selected mode, which can be specified via the *--mode* option. Posible values are: - 0 - One MQTT message per signal / one message per sample / one topic per signal / one timestamp for each sample; - 1 - One MQTT message per signal / one message containing several samples / one topic per signal / one timestamp per sample (array of samples); - 2 - One MQTT message for several signals (from 1 to N) / one message per sample for each signal / one topic for all signals / separate timestamps for each signal; diff --git a/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp b/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp index 7573789..c4df6fd 100644 --- a/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp +++ b/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp @@ -89,10 +89,10 @@ int main(int argc, char* argv[]) channels[3].setPropertyValue("Frequency", 20); // Create and configure MQTT server - const std::string rootFbName = "MQTTClientFB"; - auto rootFbConfig = instance.getAvailableFunctionBlockTypes().get(rootFbName).createDefaultConfig(); - rootFbConfig.setPropertyValue("BrokerAddress", appConfig.brokerAddress); - auto brokerFB = instance.addFunctionBlock(rootFbName, rootFbConfig); + const std::string clientFbName = "MQTTClientFB"; + auto clientFbConfig = instance.getAvailableFunctionBlockTypes().get(clientFbName).createDefaultConfig(); + clientFbConfig.setPropertyValue("BrokerAddress", appConfig.brokerAddress); + auto brokerFB = instance.addFunctionBlock(clientFbName, clientFbConfig); auto availableFbs = brokerFB.getAvailableFunctionBlockTypes(); const std::string fbName = "MQTTJSONPublisherFB"; std::cout << "Try to add the " << fbName << std::endl; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_root_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_client_fb_impl.h similarity index 92% rename from mqtt_streaming_module/include/mqtt_streaming_module/mqtt_root_fb_impl.h rename to mqtt_streaming_module/include/mqtt_streaming_module/mqtt_client_fb_impl.h index 2b07928..76f93cd 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_root_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_client_fb_impl.h @@ -26,7 +26,7 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -class MqttRootFbImpl : public FunctionBlock +class MqttClientFbImpl : public FunctionBlock { enum class ConnectionStatus : EnumType { @@ -36,7 +36,7 @@ class MqttRootFbImpl : public FunctionBlock }; public: - explicit MqttRootFbImpl(const ContextPtr& ctx, + explicit MqttClientFbImpl(const ContextPtr& ctx, const ComponentPtr& parent, const PropertyObjectPtr& config); @@ -45,7 +45,7 @@ class MqttRootFbImpl : public FunctionBlock protected: static std::atomic localIndex; static std::string generateLocalId(); - static std::vector> connectionStatusMap; + static std::vector> connectionStatusMap; void removed() override; diff --git a/mqtt_streaming_module/src/CMakeLists.txt b/mqtt_streaming_module/src/CMakeLists.txt index ed3d39f..805fc29 100644 --- a/mqtt_streaming_module/src/CMakeLists.txt +++ b/mqtt_streaming_module/src/CMakeLists.txt @@ -4,7 +4,7 @@ set(MODULE_HEADERS_DIR ../include/${TARGET_FOLDER_NAME}) set(SRC_Include common.h module_dll.h mqtt_streaming_module_impl.h - mqtt_root_fb_impl.h + mqtt_client_fb_impl.h mqtt_json_decoder_fb_impl.h mqtt_subscriber_fb_impl.h mqtt_publisher_fb_impl.h @@ -22,7 +22,7 @@ set(SRC_Include common.h set(SRC_Srcs module_dll.cpp mqtt_streaming_module_impl.cpp - mqtt_root_fb_impl.cpp + mqtt_client_fb_impl.cpp mqtt_json_decoder_fb_impl.cpp mqtt_subscriber_fb_impl.cpp mqtt_publisher_fb_impl.cpp @@ -55,8 +55,8 @@ source_group("functionalBlock" FILES ${MODULE_HEADERS_DIR}/mqtt_json_decoder_fb_ mqtt_subscriber_fb_impl.cpp ${MODULE_HEADERS_DIR}/mqtt_publisher_fb_impl.h mqtt_publisher_fb_impl.cpp - ${MODULE_HEADERS_DIR}/mqtt_root_fb_impl.h - mqtt_root_fb_impl.cpp + ${MODULE_HEADERS_DIR}/mqtt_client_fb_impl.h + mqtt_client_fb_impl.cpp ) source_group("handlers" FILES ${MODULE_HEADERS_DIR}/handler_base.h diff --git a/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_client_fb_impl.cpp similarity index 91% rename from mqtt_streaming_module/src/mqtt_root_fb_impl.cpp rename to mqtt_streaming_module/src/mqtt_client_fb_impl.cpp index 5d8bc5a..bc49d14 100644 --- a/mqtt_streaming_module/src/mqtt_root_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_client_fb_impl.cpp @@ -1,7 +1,7 @@ #include "mqtt_streaming_module/constants.h" #include "mqtt_streaming_module/mqtt_subscriber_fb_impl.h" #include "mqtt_streaming_module/mqtt_publisher_fb_impl.h" -#include +#include #include #include @@ -9,13 +9,13 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE constexpr int MQTT_CLIENT_SYNC_DISCONNECT_TOUT = 3000; -std::atomic MqttRootFbImpl::localIndex = 0; -std::vector> MqttRootFbImpl::connectionStatusMap = +std::atomic MqttClientFbImpl::localIndex = 0; +std::vector> MqttClientFbImpl::connectionStatusMap = {{ConnectionStatus::Connected, "Connected"}, {ConnectionStatus::Reconnecting, "Reconnecting"}, {ConnectionStatus::Disconnected, "Disconnected"}}; -MqttRootFbImpl::MqttRootFbImpl(const ContextPtr& ctx, const ComponentPtr& parent, const PropertyObjectPtr& config) +MqttClientFbImpl::MqttClientFbImpl(const ContextPtr& ctx, const ComponentPtr& parent, const PropertyObjectPtr& config) : FunctionBlock(CreateType(), ctx, parent, generateLocalId()), subscriber(std::make_shared()), connectTimeout(0), @@ -41,7 +41,7 @@ MqttRootFbImpl::MqttRootFbImpl(const ContextPtr& ctx, const ComponentPtr& parent LOG_I("MQTT: Connection established"); } -void MqttRootFbImpl::removed() +void MqttClientFbImpl::removed() { FunctionBlock::removed(); LOG_I("MQTT: disconnecting from the MQTT broker...", connectionSettings.mqttUrl + ":" + std::to_string(connectionSettings.port)); @@ -56,7 +56,7 @@ void MqttRootFbImpl::removed() } } -void MqttRootFbImpl::initNestedFbTypes() +void MqttClientFbImpl::initNestedFbTypes() { nestedFbTypes = Dict(); // Add a MQTT subscriber function block type @@ -72,7 +72,7 @@ void MqttRootFbImpl::initNestedFbTypes() } } -void MqttRootFbImpl::initMqttSubscriber() +void MqttClientFbImpl::initMqttSubscriber() { const auto serverUrl = connectionSettings.mqttUrl + ((connectionSettings.port > 0) ? ":" + std::to_string(connectionSettings.port) : ""); subscriber->setServerURL(serverUrl); @@ -100,7 +100,7 @@ void MqttRootFbImpl::initMqttSubscriber() subscriber->connect(); } -void MqttRootFbImpl::initConnectionStatus() +void MqttClientFbImpl::initConnectionStatus() { subscriber->setConnectionLostCb( [this](std::string msg) @@ -111,7 +111,7 @@ void MqttRootFbImpl::initConnectionStatus() }); } -void MqttRootFbImpl::initProperties(const PropertyObjectPtr& config) +void MqttClientFbImpl::initProperties(const PropertyObjectPtr& config) { for (const auto& prop : config.getAllProperties()) { @@ -148,7 +148,7 @@ void MqttRootFbImpl::initProperties(const PropertyObjectPtr& config) readProperties(); } -void MqttRootFbImpl::readProperties() +void MqttClientFbImpl::readProperties() { connectionSettings.mqttUrl = objPtr.getPropertyValue(PROPERTY_NAME_CLIENT_BROKER_ADDRESS).asPtr().toStdString(); connectionSettings.port = objPtr.getPropertyValue(PROPERTY_NAME_CLIENT_BROKER_PORT); @@ -159,7 +159,7 @@ void MqttRootFbImpl::readProperties() connectTimeout = objPtr.getPropertyValue(PROPERTY_NAME_CLIENT_CONNECT_TIMEOUT); } -bool MqttRootFbImpl::waitForConnection(const int timeoutMs) +bool MqttClientFbImpl::waitForConnection(const int timeoutMs) { bool res = (connectedFuture.wait_for(std::chrono::milliseconds(timeoutMs)) == std::future_status::ready && connectedFuture.get() == true); @@ -173,12 +173,12 @@ bool MqttRootFbImpl::waitForConnection(const int timeoutMs) return res; } -DictPtr MqttRootFbImpl::onGetAvailableFunctionBlockTypes() +DictPtr MqttClientFbImpl::onGetAvailableFunctionBlockTypes() { return nestedFbTypes; } -FunctionBlockPtr MqttRootFbImpl::onAddFunctionBlock(const StringPtr& typeId, const PropertyObjectPtr& config) +FunctionBlockPtr MqttClientFbImpl::onAddFunctionBlock(const StringPtr& typeId, const PropertyObjectPtr& config) { FunctionBlockPtr nestedFunctionBlock; { @@ -212,12 +212,12 @@ FunctionBlockPtr MqttRootFbImpl::onAddFunctionBlock(const StringPtr& typeId, con return nestedFunctionBlock; } -std::string MqttRootFbImpl::generateLocalId() +std::string MqttClientFbImpl::generateLocalId() { return std::string(MQTT_LOCAL_CLIENT_FB_ID_PREFIX + std::to_string(localIndex++)); } -FunctionBlockTypePtr MqttRootFbImpl::CreateType() +FunctionBlockTypePtr MqttClientFbImpl::CreateType() { auto config = PropertyObject(); { diff --git a/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp index e249fc4..68b1f75 100644 --- a/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp @@ -39,24 +39,33 @@ FunctionBlockTypePtr MqttJsonDecoderFbImpl::CreateType() { auto defaultConfig = PropertyObject(); { - auto builder = StringPropertyBuilder(PROPERTY_NAME_DEC_VALUE_NAME, String("")).setDescription(""); + auto builder = + StringPropertyBuilder(PROPERTY_NAME_DEC_VALUE_NAME, String("")) + .setDescription("Specifies the JSON field name from which value data will be extracted. This property is required. It " + "should be contained in the incoming JSON messages. Otherwise, a parsing error will occur."); defaultConfig.addProperty(builder.build()); } { - auto builder = StringPropertyBuilder(PROPERTY_NAME_DEC_TS_NAME, String("")).setDescription(""); + auto builder = + StringPropertyBuilder(PROPERTY_NAME_DEC_TS_NAME, String("")) + .setDescription( + "Specifies the JSON field name from which timestamp will be extracted. This property is " + "optional. If it is set it should be contained in the incoming JSON messages. Otherwise, a parsing error will occur."); defaultConfig.addProperty(builder.build()); } { - auto builder = StringPropertyBuilder(PROPERTY_NAME_DEC_UNIT, String("")).setDescription(""); + auto builder = StringPropertyBuilder(PROPERTY_NAME_DEC_UNIT, String("")) + .setDescription("Specifies the unit symbol for the decoded value. This property is optional."); defaultConfig.addProperty(builder.build()); } - const auto fbType = FunctionBlockType(JSON_DECODER_FB_NAME, - JSON_DECODER_FB_NAME, - "", - defaultConfig); + const auto fbType = + FunctionBlockType(JSON_DECODER_FB_NAME, + JSON_DECODER_FB_NAME, + "The JSON decoder Function Block extracts data from a JSON string and builds a signal based on that data.", + defaultConfig); return fbType; } diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index 494e04a..82d8df4 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -134,8 +134,8 @@ FunctionBlockTypePtr MqttPublisherFbImpl::CreateType() IntPropertyBuilder(PROPERTY_NAME_PUB_READ_PERIOD, DEFAULT_PUB_READ_PERIOD) .setMinValue(0) .setUnit(Unit("ms")) - .setDescription(fmt::format("Polling period in milliseconds, which specifies how often the server collects and publishes " - "the connected signals’ data to an MQTT broker. By default it is set to {} ms.", + .setDescription(fmt::format("Polling period in milliseconds, which specifies how often the server calls an internal reader to " + "collect and publish the connected signals’ data to an MQTT broker. By default it is set to {} ms.", DEFAULT_PUB_READ_PERIOD)); defaultConfig.addProperty(builder.build()); } @@ -373,7 +373,7 @@ void MqttPublisherFbImpl::initProperties(const PropertyObjectPtr& config) auto builder = ListPropertyBuilder(PROPERTY_NAME_PUB_TOPICS, List()) .setReadOnly(true) .setVisible(EvalValue(std::string("$") + PROPERTY_NAME_PUB_TOPIC_MODE + " == 0")) - .setDescription("List of currently used MQTT topics for publishing."); + .setDescription("List of currently used MQTT topics for publishing in TopicPerSignal mode."); objPtr.addProperty(builder.build()); } diff --git a/mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp b/mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp index 9802c15..1842636 100644 --- a/mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,7 +44,7 @@ MqttStreamingModule::onCreateFunctionBlock(const StringPtr& id, if (!context.assigned()) DAQ_THROW_EXCEPTION(InvalidParameterException, "Context is not available."); - FunctionBlockPtr fb = createWithImplementation(context, parent, config); + FunctionBlockPtr fb = createWithImplementation(context, parent, config); LOG_I("MQTT function block (GlobalId: {}) created", fb.getGlobalId()); @@ -53,7 +53,7 @@ MqttStreamingModule::onCreateFunctionBlock(const StringPtr& id, FunctionBlockTypePtr MqttStreamingModule::createFbType() { - return MqttRootFbImpl::CreateType(); + return MqttClientFbImpl::CreateType(); } END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/tests/CMakeLists.txt b/mqtt_streaming_module/tests/CMakeLists.txt index 7c3ddeb..cc6d46d 100644 --- a/mqtt_streaming_module/tests/CMakeLists.txt +++ b/mqtt_streaming_module/tests/CMakeLists.txt @@ -2,7 +2,7 @@ set(MODULE_NAME mqtt_stream_module) set(TEST_APP test_${MODULE_NAME}) set(TEST_SOURCES test_mqtt_streaming_module.cpp - test_mqtt_root_fb.cpp + test_mqtt_client_fb.cpp test_mqtt_subscriber_fb.cpp test_mqtt_json_decoder_fb.cpp test_mqtt_publisher_fb.cpp diff --git a/mqtt_streaming_module/tests/test_daq_test_helper.h b/mqtt_streaming_module/tests/test_daq_test_helper.h index 44e3f7c..a3b1eec 100644 --- a/mqtt_streaming_module/tests/test_daq_test_helper.h +++ b/mqtt_streaming_module/tests/test_daq_test_helper.h @@ -9,7 +9,7 @@ class DaqTestHelper { public: daq::InstancePtr daqInstance; - daq::FunctionBlockPtr rootMqttFb; + daq::FunctionBlockPtr clientMqttFb; daq::FunctionBlockPtr subMqttFb; void StartUp(std::string url = DEFAULT_BROKER_ADDRESS, uint16_t port = DEFAULT_PORT) @@ -25,21 +25,21 @@ class DaqTestHelper return daqInstance; } - daq::FunctionBlockPtr DaqAddRootMqttFb(std::string url = DEFAULT_BROKER_ADDRESS, uint16_t port = DEFAULT_PORT) + daq::FunctionBlockPtr DaqAddClientMqttFb(std::string url = DEFAULT_BROKER_ADDRESS, uint16_t port = DEFAULT_PORT) { auto config = DaqMqttFbConfig(url, port); - rootMqttFb = daqInstance.addFunctionBlock(CLIENT_FB_NAME, config); - return rootMqttFb; + clientMqttFb = daqInstance.addFunctionBlock(CLIENT_FB_NAME, config); + return clientMqttFb; } daq::FunctionBlockPtr DaqMqttFbInit(std::string url = DEFAULT_BROKER_ADDRESS, uint16_t port = DEFAULT_PORT) { - if (!rootMqttFb.assigned()) + if (!clientMqttFb.assigned()) { auto config = DaqMqttFbConfig(url, port); - rootMqttFb = daqInstance.addFunctionBlock(CLIENT_FB_NAME, config); + clientMqttFb = daqInstance.addFunctionBlock(CLIENT_FB_NAME, config); } - return rootMqttFb; + return clientMqttFb; } daq::PropertyObjectPtr DaqMqttFbConfig(std::string url = DEFAULT_BROKER_ADDRESS, uint16_t port = DEFAULT_PORT) @@ -62,9 +62,9 @@ class DaqTestHelper daq::FunctionBlockPtr AddSubFb(std::string topic = "") { - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, daq::String(topic)); - subMqttFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config); + subMqttFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config); return subMqttFb; } }; diff --git a/mqtt_streaming_module/tests/test_mqtt_root_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_client_fb.cpp similarity index 98% rename from mqtt_streaming_module/tests/test_mqtt_root_fb.cpp rename to mqtt_streaming_module/tests/test_mqtt_client_fb.cpp index 7b2ca23..08a6da4 100644 --- a/mqtt_streaming_module/tests/test_mqtt_root_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_client_fb.cpp @@ -130,7 +130,7 @@ TEST_F(MqttFbTest, CheckMqttFbFunctionalBlocks) { StartUp(); daq::DictPtr fbTypes; - ASSERT_NO_THROW(fbTypes = rootMqttFb.getAvailableFunctionBlockTypes()); + ASSERT_NO_THROW(fbTypes = clientMqttFb.getAvailableFunctionBlockTypes()); ASSERT_GE(fbTypes.getCount(), 2); ASSERT_TRUE(fbTypes.hasKey(SUB_FB_NAME)); ASSERT_TRUE(fbTypes.hasKey(PUB_FB_NAME)); diff --git a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp index 2185b1c..f01522e 100644 --- a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp @@ -639,7 +639,7 @@ TEST_F(MqttJsonDecoderFbTest, DataTransferSeveralSignals) const auto topic = buildTopicName(); DaqInstanceInit(); - auto rootFb0 = DaqAddRootMqttFb("127.0.0.1", DEFAULT_PORT); + auto clientFb0 = DaqAddClientMqttFb("127.0.0.1", DEFAULT_PORT); auto jsonFb0 = AddSubFb(topic); auto decoderFb0 = AddDecoderFb(valueF0, tsF); auto decoderFb1 = AddDecoderFb(valueF1, tsF); @@ -738,7 +738,7 @@ TEST_F(MqttJsonDecoderFbTest, DataTransferMissingFieldSeveralSignals) const auto topic = buildTopicName(); DaqInstanceInit(); - auto rootFb0 = DaqAddRootMqttFb("127.0.0.1", DEFAULT_PORT); + auto clientFb0 = DaqAddClientMqttFb("127.0.0.1", DEFAULT_PORT); auto jsonFb0 = AddSubFb(topic); auto decoderFb0 = AddDecoderFb(valueF0, tsF); auto decoderFb1 = AddDecoderFb(valueF1, tsF); @@ -831,11 +831,11 @@ TEST_F(MqttJsonFbCommunicationTest, FullDataTransferFor2MqttFbs) const std::string topic1 = buildTopicName("1"); DaqInstanceInit(); - auto rootFb0 = DaqAddRootMqttFb("127.0.0.1", 1883); + auto clientFb0 = DaqAddClientMqttFb("127.0.0.1", 1883); auto jsonFb0 = AddSubFb(topic0); auto decoderFb0 = AddDecoderFb(valueF, tsF); - auto rootFb1 = DaqAddRootMqttFb("127.0.0.1", 1884); + auto clientFb1 = DaqAddClientMqttFb("127.0.0.1", 1884); auto jsonFb1 = AddSubFb(topic1); auto decoderFb1 = AddDecoderFb(valueF, tsF); diff --git a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp index 7e68217..9df6ef4 100644 --- a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp @@ -204,10 +204,10 @@ class MqttPublisherFbHelper : public DaqTestHelper void CreatePublisherFB() { - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_QOS, 2); config.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, True); - fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config); + fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config); } void CreatePublisherFB(bool multiTopic, @@ -219,7 +219,7 @@ class MqttPublisherFbHelper : public DaqTestHelper int qos = 2, uint32_t readPeriod = 20) { - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, multiTopic ? 1 : 0); config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, sharedTs ? True : False); config.setPropertyValue(PROPERTY_NAME_PUB_GROUP_VALUES, groupV ? True : False); @@ -229,7 +229,7 @@ class MqttPublisherFbHelper : public DaqTestHelper config.setPropertyValue(PROPERTY_NAME_PUB_READ_PERIOD, readPeriod); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME, topicName); config.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, True); - fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config); + fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config); } bool CreateSubscriber(std::string postfix = "_subscriberId") @@ -515,7 +515,7 @@ TEST_F(MqttPublisherFbTest, DefaultConfig) daq::DictPtr fbTypes; daq::FunctionBlockTypePtr fbt; daq::PropertyObjectPtr defaultConfig; - ASSERT_NO_THROW(fbTypes = rootMqttFb.getAvailableFunctionBlockTypes()); + ASSERT_NO_THROW(fbTypes = clientMqttFb.getAvailableFunctionBlockTypes()); ASSERT_NO_THROW(fbt = fbTypes.get(PUB_FB_NAME)); ASSERT_NO_THROW(defaultConfig = fbt.createDefaultConfig()); @@ -596,7 +596,7 @@ TEST_F(MqttPublisherFbTest, PropertyVisibility) TEST_F(MqttPublisherFbTest, Config) { StartUp(); - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); @@ -608,7 +608,7 @@ TEST_F(MqttPublisherFbTest, Config) config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME, buildTopicName()); config.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, True); daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Warning", daqInstance.getContext().getTypeManager())); SignalHelper helper; @@ -644,7 +644,7 @@ TEST_F(MqttPublisherFbTest, Creation) { StartUp(); daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME)); SignalHelper helper; fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), @@ -661,7 +661,7 @@ TEST_F(MqttPublisherFbTest, TwoFbCreation) SignalHelper helper; { daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME)); fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -672,7 +672,7 @@ TEST_F(MqttPublisherFbTest, TwoFbCreation) } { daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME)); fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -681,7 +681,7 @@ TEST_F(MqttPublisherFbTest, TwoFbCreation) static_cast(MqttPublisherFbImpl::SettingStatus::Valid), daqInstance.getContext().getTypeManager())); } - auto fbs = rootMqttFb.getFunctionBlocks(); + auto fbs = clientMqttFb.getFunctionBlocks(); ASSERT_EQ(fbs.getCount(), 2u); } @@ -689,7 +689,7 @@ TEST_F(MqttPublisherFbTest, CreationWithDefaultConfig) { StartUp(); daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME)); auto signals = fb.getSignals(); ASSERT_EQ(signals.getCount(), 0u); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), @@ -710,7 +710,7 @@ TEST_F(MqttPublisherFbTest, CreationWithPartialConfig) daq::FunctionBlockPtr fb; auto config = PropertyObject(); config.addProperty(IntProperty(PROPERTY_NAME_PUB_READ_PERIOD, 20)); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); SignalHelper helper; fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), @@ -727,11 +727,11 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) StatusHelper::addTypesToTypeManager(MQTT_PUB_FB_SIG_STATUS_TYPE, MQTT_PUB_FB_SIG_STATUS_NAME, MqttPublisherFbImpl::signalStatusMap, - rootMqttFb.getContext().getTypeManager()); + clientMqttFb.getContext().getTypeManager()); StatusHelper::addTypesToTypeManager(MQTT_PUB_FB_PUB_STATUS_TYPE, MQTT_PUB_FB_PUB_STATUS_NAME, MqttPublisherFbImpl::publishingStatusMap, - rootMqttFb.getContext().getTypeManager()); + clientMqttFb.getContext().getTypeManager()); const auto sigStValid = EnumerationWithIntValue(MQTT_PUB_FB_SIG_STATUS_TYPE, static_cast(MqttPublisherFbImpl::SignalStatus::Valid), daqInstance.getContext().getTypeManager()); @@ -747,7 +747,7 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) { daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME)); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStNotConnected); auto help = SignalHelper(); @@ -783,9 +783,9 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) { daq::FunctionBlockPtr fb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); auto help = SignalHelper(); auto signal0 = help.createSignal(DataDescriptorBuilder().setRule(LinearDataRule(2, 3)).setTickResolution(Ratio(1, 1000))); @@ -799,9 +799,9 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) { daq::FunctionBlockPtr fb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); auto help = SignalHelper(); auto signal0 = help.createSignal(DataDescriptorBuilder().setRule(LinearDataRule(1, 3)).setTickResolution(Ratio(1, 1000))); @@ -818,9 +818,9 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) { daq::FunctionBlockPtr fb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); auto help = SignalHelper(); auto signal0 = help.createSignal(DataDescriptorBuilder().setRule(LinearDataRule(2, 3)).setTickResolution(Ratio(1, 1000))); @@ -841,11 +841,11 @@ TEST_F(MqttPublisherFbTest, PreviewSignals) StartUp(); daq::FunctionBlockPtr fb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0); config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, False); config.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, False); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); auto help = SignalHelper(); ASSERT_EQ(fb.getSignals().getCount(), 0u); @@ -885,9 +885,9 @@ TEST_F(MqttPublisherFbTest, TopicsList) { daq::FunctionBlockPtr fb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); auto help = SignalHelper(); ASSERT_NO_THROW(fb.getInputPorts()[0].connect(help.signal0)); @@ -899,9 +899,9 @@ TEST_F(MqttPublisherFbTest, TopicsList) { daq::FunctionBlockPtr fb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); auto help = SignalHelper(); ASSERT_NO_THROW(fb.getInputPorts()[0].connect(help.signal0)); @@ -916,10 +916,10 @@ TEST_F(MqttPublisherFbTest, WrongConfig) { StartUp(); daq::FunctionBlockPtr fb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME, String("/test/#")); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SET_STATUS_NAME), diff --git a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp index 4a8dbaf..348743f 100644 --- a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp @@ -84,7 +84,7 @@ TEST_F(MqttSubscriberFbTest, DefaultConfig) daq::DictPtr fbTypes; daq::FunctionBlockTypePtr fbt; daq::PropertyObjectPtr defaultConfig; - ASSERT_NO_THROW(fbTypes = rootMqttFb.getAvailableFunctionBlockTypes()); + ASSERT_NO_THROW(fbTypes = clientMqttFb.getAvailableFunctionBlockTypes()); ASSERT_NO_THROW(fbt = fbTypes.get(SUB_FB_NAME)); ASSERT_NO_THROW(defaultConfig = fbt.createDefaultConfig()); @@ -116,11 +116,11 @@ TEST_F(MqttSubscriberFbTest, DefaultConfig) TEST_F(MqttSubscriberFbTest, Config) { StartUp(); - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, buildTopicName()); daq::FunctionBlockPtr subFb; - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); const auto allProperties = subFb.getAllProperties(); ASSERT_EQ(allProperties.getCount(), config.getAllProperties().getCount()); @@ -137,7 +137,7 @@ TEST_F(MqttSubscriberFbTest, CreationWithDefaultConfig) { StartUp(); daq::FunctionBlockPtr subFb; - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME)); EXPECT_EQ(subFb.getSignals().getCount(), 0u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Warning", daqInstance.getContext().getTypeManager())); @@ -150,7 +150,7 @@ TEST_F(MqttSubscriberFbTest, CreationWithPartialConfig) daq::FunctionBlockPtr subFb; auto config = PropertyObject(); config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, String(buildTopicName()))); - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(subFb.getSignals().getCount(), 0u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -161,10 +161,10 @@ TEST_F(MqttSubscriberFbTest, CreationWithCustomConfig) // If FB has only one property, partial config is equivalent to custom config StartUp(); daq::FunctionBlockPtr subFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, True); config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, buildTopicName()); - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(subFb.getSignals().getCount(), 1u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -174,11 +174,11 @@ TEST_F(MqttSubscriberFbTest, SubscriptionStatusWaitingForData) { StartUp(); - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, buildTopicName()); daq::FunctionBlockPtr subFb; - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -193,12 +193,12 @@ TEST_P(MqttSubscriberFbTopicPTest, CheckSubscriberFbTopic) auto [topic, result] = GetParam(); StartUp(); - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic); config.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, True); daq::FunctionBlockPtr fb; - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); auto signals = fb.getSignals(); ASSERT_EQ(signals.getCount(), 1); const auto expectedComponentStatus = result ? "Ok" : "Warning"; @@ -236,7 +236,7 @@ TEST_F(MqttSubscriberFbTest, RemovingNestedFunctionBlock) { auto config = PropertyObject(); config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, String(buildTopicName()))); - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); } @@ -259,7 +259,7 @@ TEST_F(MqttSubscriberFbTest, TwoFbCreation) daq::FunctionBlockPtr fb; auto config = PropertyObject(); config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, buildTopicName("0"))); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); } @@ -267,11 +267,11 @@ TEST_F(MqttSubscriberFbTest, TwoFbCreation) daq::FunctionBlockPtr fb; auto config = PropertyObject(); config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, buildTopicName("1"))); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); } - auto fbs = rootMqttFb.getFunctionBlocks(); + auto fbs = clientMqttFb.getFunctionBlocks(); ASSERT_EQ(fbs.getCount(), 2u); } @@ -283,7 +283,7 @@ TEST_F(MqttSubscriberFbTest, PropertyChanged) auto config = PropertyObject(); auto topic = buildTopicName("0"); config.addProperty(StringProperty(PROPERTY_NAME_SUB_TOPIC, topic)); - ASSERT_NO_THROW(fb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); auto subFb = reinterpret_cast(*fb); @@ -298,9 +298,9 @@ TEST_F(MqttSubscriberFbTest, JsonInit0) { StartUp(); daq::FunctionBlockPtr subFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG, String(VALID_JSON_1_TOPIC_0)); - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); ASSERT_EQ(subFb.getFunctionBlocks().getCount(), 3u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -324,9 +324,9 @@ TEST_F(MqttSubscriberFbTest, JsonInit1) { StartUp(); daq::FunctionBlockPtr subFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG, String(VALID_JSON_1_TOPIC_1)); - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); ASSERT_EQ(subFb.getFunctionBlocks().getCount(), 3u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); @@ -351,9 +351,9 @@ TEST_P(MqttSubscriberFbConfigPTest, JsonWrongInit) const auto configJson = GetParam(); StartUp(); daq::FunctionBlockPtr subFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG, String(configJson)); - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(subFb.getFunctionBlocks().getCount(), 0u); EXPECT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); @@ -375,9 +375,9 @@ TEST_P(MqttSubscriberFbConfigFilePTest, JsonInitFromFile) const auto configJson = GetParam(); StartUp(); daq::FunctionBlockPtr subFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG_FILE, String(configJson)); - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); } @@ -393,9 +393,9 @@ TEST_F(MqttSubscriberFbTest, JsonInitFromFileWithChecking) { StartUp(); daq::FunctionBlockPtr subFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG_FILE, String("data/public-example0.json")); - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); ASSERT_EQ(subFb.getFunctionBlocks().getCount(), 3u); @@ -419,9 +419,9 @@ TEST_F(MqttSubscriberFbTest, JsonInitFromFileWrongPath) { StartUp(); daq::FunctionBlockPtr subFb; - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_JSON_CONFIG_FILE, String("/justWrongPath/wrongFile.txt")); - ASSERT_NO_THROW(subFb = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); EXPECT_EQ(subFb.getFunctionBlocks().getCount(), 0u); EXPECT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); @@ -474,11 +474,11 @@ TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransfer) StartUp(); - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic); config.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, True); - auto singal = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config).getSignals()[0]; + auto singal = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config).getSignals()[0]; auto reader = daq::PacketReader(singal); MqttAsyncClientWrapper publisher("testPublisherId"); @@ -526,10 +526,10 @@ TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransferWithReconfiguring) StartUp(); - auto config = rootMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic0); config.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, True); - auto rawFB = rootMqttFb.addFunctionBlock(SUB_FB_NAME, config); + auto rawFB = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config); auto singal = rawFB.getSignals()[0]; auto reader = daq::PacketReader(singal); From 0172d86287541dffc8d740436482e5e6bf6148a0 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Mon, 19 Jan 2026 19:16:26 +0100 Subject: [PATCH 14/49] mqtt: status helper - check if status has been set --- .../include/mqtt_streaming_module/status_helper.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h b/mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h index a417a43..208008a 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h @@ -62,8 +62,12 @@ class StatusHelper void setStatus(T status, const std::string& message = "") { std::scoped_lock lock(statusMutex); - currentStatus = EnumerationWithIntValue(typeName, static_cast(status), typeManager); - statusContainer.template asPtr(true).setStatusWithMessage(statusName, currentStatus, message); + const auto newStatus = EnumerationWithIntValue(typeName, static_cast(status), typeManager); + if (newStatus != currentStatus || message != this->message) + { + currentStatus = newStatus; + statusContainer.template asPtr(true).setStatusWithMessage(statusName, currentStatus, message); + } } T getStatus() { @@ -74,6 +78,7 @@ class StatusHelper private: const std::string typeName; const std::string statusName; + std::string message; ComponentStatusContainerPtr statusContainer; const std::vector>& statusMap; EnumerationPtr currentStatus; From acfb7defc7c02f775d39904187f8892f10ae82b9 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Mon, 19 Jan 2026 19:18:25 +0100 Subject: [PATCH 15/49] mqtt: MqttPublisherFbImpl::readProperties fix --- mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index 82d8df4..4449c3e 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -400,6 +400,9 @@ void MqttPublisherFbImpl::readProperties() config.periodMs = readProperty(PROPERTY_NAME_PUB_READ_PERIOD, DEFAULT_PUB_READ_PERIOD); config.topicName = readProperty(PROPERTY_NAME_PUB_TOPIC_NAME, globalId.toStdString()); config.enablePreview = readProperty(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, false); + settingErrors.clear(); + hasSettingError = false; + if (tmpValueFieldName < static_cast(SignalValueJSONKey::_count) && tmpValueFieldName >= 0) { config.valueFieldName = static_cast(tmpValueFieldName); @@ -432,8 +435,6 @@ void MqttPublisherFbImpl::readProperties() hasEmptyTopic = false; } - settingErrors.clear(); - hasSettingError = false; if (config.topicMode == TopicMode::Single || config.sharedTs) { auto result = mqtt::MqttDataWrapper::validateTopic(config.topicName, loggerComponent); From 024aed4a1a05f40586bf6b96599c96ab0e70d584 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Mon, 19 Jan 2026 19:19:25 +0100 Subject: [PATCH 16/49] mqtt: fix for subscriber component status --- .../src/mqtt_subscriber_fb_impl.cpp | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp index 05c2f13..d43cabf 100644 --- a/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp @@ -165,16 +165,15 @@ void MqttSubscriberFbImpl::readProperties() { auto lock = this->getRecursiveConfigLock(); topicForSubscribing.clear(); - bool isPresent = false; + std::string topic; if (objPtr.hasProperty(PROPERTY_NAME_SUB_TOPIC)) { auto topicStr = objPtr.getPropertyValue(PROPERTY_NAME_SUB_TOPIC).asPtrOrNull(); if (topicStr.assigned()) - { - isPresent = true; - setTopic(topicStr.toStdString()); - } + topic = topicStr.toStdString(); } + setTopic(topic); + if (objPtr.hasProperty(PROPERTY_NAME_SUB_QOS)) { auto qosProp = objPtr.getPropertyValue(PROPERTY_NAME_SUB_QOS).asPtrOrNull(); @@ -192,12 +191,6 @@ void MqttSubscriberFbImpl::readProperties() this->enablePreview = previewProp.getValue(False); } } - if (!isPresent) - { - LOG_W("\'{}\' property is missing!", PROPERTY_NAME_SUB_TOPIC); - setComponentStatus(ComponentStatus::Warning); - subscriptionStatus.setStatus(SubscriptionStatus::InvalidTopicName, "The topic property is not set!"); - } } void MqttSubscriberFbImpl::readJsonConfig() @@ -331,7 +324,7 @@ bool MqttSubscriberFbImpl::setTopic(std::string topic) } else { - setComponentStatus(ComponentStatus::Warning); + setComponentStatusWithMessage(ComponentStatus::Warning, validationStatus.msg); subscriptionStatus.setStatus(SubscriptionStatus::InvalidTopicName, validationStatus.msg); } return validationStatus.success; @@ -362,7 +355,6 @@ FunctionBlockPtr MqttSubscriberFbImpl::onAddFunctionBlock(const StringPtr& typeI auto lock = this->getAcquisitionLock2(); nestedFunctionBlocks.push_back(nestedFunctionBlock); } - setComponentStatus(ComponentStatus::Ok); } else { From d7a884fae59690021233b32011ffa10e2cf9d545 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Tue, 20 Jan 2026 17:06:59 +0100 Subject: [PATCH 17/49] mqtt: memory leak fix --- .../group_signal_shared_ts_handler.h | 3 +++ .../src/group_signal_shared_ts_handler.cpp | 23 +++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h index e8c9b7b..c088d2e 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h @@ -34,6 +34,7 @@ class GroupSignalSharedTsHandler : public HandlerBase { public: explicit GroupSignalSharedTsHandler(SignalValueJSONKey signalNamesMode, std::string topic); + ~GroupSignalSharedTsHandler(); MqttData processSignalContexts(std::vector& signalContexts) override; ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const override; @@ -47,6 +48,7 @@ class GroupSignalSharedTsHandler : public HandlerBase const std::string topic; std::vector dataBuffers; daq::MultiReaderPtr reader; + std::mutex sync; template std::string toString(const std::string& valueFieldName, void* data, SizeT offset); @@ -55,6 +57,7 @@ class GroupSignalSharedTsHandler : public HandlerBase std::string buildTopicName(); void createReader(const std::vector& signalContexts); void allocateBuffers(const std::vector& signalContexts); + void deallocateBuffers(); static std::string messageFromFields(const std::vector& fields); static TimestampTickStruct domainToTs(const MultiReaderStatusPtr status); }; diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp index bff3642..2706635 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp @@ -18,9 +18,16 @@ GroupSignalSharedTsHandler::GroupSignalSharedTsHandler(SignalValueJSONKey signal { } +GroupSignalSharedTsHandler::~GroupSignalSharedTsHandler() +{ + std::scoped_lock lock(sync); + deallocateBuffers(); +} + MqttData GroupSignalSharedTsHandler::processSignalContexts(std::vector& signalContexts) { MqttData messages; + std::scoped_lock lock(sync); if (!reader.assigned()) return messages; auto dataAvailable = reader.getAvailableCount(); @@ -254,6 +261,7 @@ std::string GroupSignalSharedTsHandler::buildTopicName() void GroupSignalSharedTsHandler::createReader(const std::vector& signalContexts) { + std::scoped_lock lock(sync); // signalContexts always contain an unconnected input port if (signalContexts.size() <= 1) return; @@ -276,10 +284,7 @@ void GroupSignalSharedTsHandler::allocateBuffers(const std::vector(signalsCount, nullptr); @@ -289,6 +294,16 @@ void GroupSignalSharedTsHandler::allocateBuffers(const std::vector& fields) { std::ostringstream oss; From 2be52e5cfd614b675edd7c536ed6c26b4bebf567 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Tue, 20 Jan 2026 17:10:06 +0100 Subject: [PATCH 18/49] mqtt: publisher test --- .../tests/test_mqtt_publisher_fb.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp index 9df6ef4..3da139f 100644 --- a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp @@ -849,34 +849,53 @@ TEST_F(MqttPublisherFbTest, PreviewSignals) auto help = SignalHelper(); ASSERT_EQ(fb.getSignals().getCount(), 0u); + ASSERT_EQ(fb.getInputPorts().getCount(), 1u); fb.getInputPorts()[0].connect(help.signal0); ASSERT_EQ(fb.getSignals().getCount(), 0u); + ASSERT_EQ(fb.getInputPorts().getCount(), 2u); + fb.getInputPorts()[0].disconnect(); + ASSERT_EQ(fb.getSignals().getCount(), 0u); + ASSERT_EQ(fb.getInputPorts().getCount(), 1u); + fb.getInputPorts()[0].connect(help.signal0); + ASSERT_EQ(fb.getSignals().getCount(), 0u); + ASSERT_EQ(fb.getInputPorts().getCount(), 2u); fb.getInputPorts()[1].connect(help.signal0); ASSERT_EQ(fb.getSignals().getCount(), 0u); + ASSERT_EQ(fb.getInputPorts().getCount(), 3u); ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, True)); ASSERT_EQ(fb.getSignals().getCount(), 2u); + ASSERT_EQ(fb.getInputPorts().getCount(), 3u); ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1)); ASSERT_EQ(fb.getSignals().getCount(), 1u); + ASSERT_EQ(fb.getInputPorts().getCount(), 3u); ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0)); ASSERT_EQ(fb.getSignals().getCount(), 2u); + ASSERT_EQ(fb.getInputPorts().getCount(), 3u); ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1)); // disconnection fb.getInputPorts()[1].disconnect(); ASSERT_EQ(fb.getSignals().getCount(), 1u); + ASSERT_EQ(fb.getInputPorts().getCount(), 2u); // disconnection fb.getInputPorts()[0].disconnect(); ASSERT_EQ(fb.getSignals().getCount(), 1u); + ASSERT_EQ(fb.getInputPorts().getCount(), 1u); ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, False)); ASSERT_EQ(fb.getSignals().getCount(), 0u); + ASSERT_EQ(fb.getInputPorts().getCount(), 1u); ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0)); fb.getInputPorts()[0].connect(help.signal0); + ASSERT_EQ(fb.getInputPorts().getCount(), 2u); fb.getInputPorts()[1].connect(help.signal1); + ASSERT_EQ(fb.getInputPorts().getCount(), 3u); fb.getInputPorts()[2].connect(help.signal0); + ASSERT_EQ(fb.getInputPorts().getCount(), 4u); ASSERT_EQ(fb.getSignals().getCount(), 0u); ASSERT_NO_THROW(fb.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, True)); ASSERT_EQ(fb.getSignals().getCount(), 3u); + ASSERT_EQ(fb.getInputPorts().getCount(), 4u); } TEST_F(MqttPublisherFbTest, TopicsList) From 5d9f6b3c591f0ed729805787cdadcce20f286428 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 21 Jan 2026 16:46:47 +0100 Subject: [PATCH 19/49] mqtt: SharedTs property has been removed from publisher; removing of SignalArrayAtomicSampleHandler handler --- .../include/mqtt_streaming_module/constants.h | 1 - .../mqtt_streaming_module/handler_factory.h | 7 +--- .../include/mqtt_streaming_module/types.h | 1 - .../src/mqtt_publisher_fb_impl.cpp | 34 +++++++--------- .../tests/test_mqtt_publisher_fb.cpp | 39 +++++++------------ 5 files changed, 28 insertions(+), 54 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index d66272d..dacfb94 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -41,7 +41,6 @@ static constexpr const char* PROPERTY_NAME_DEC_UNIT = "Unit"; static constexpr const char* PROPERTY_NAME_PUB_TOPIC_MODE = "TopicMode"; static constexpr const char* PROPERTY_NAME_PUB_TOPIC_NAME = "Topic"; -static constexpr const char* PROPERTY_NAME_PUB_SHARED_TS = "SharedTimestamp"; static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES = "GroupValues"; static constexpr const char* PROPERTY_NAME_PUB_VALUE_FIELD_NAME = "SignalValueJSONKey"; static constexpr const char* PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE = "SamplesPerMessage"; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h b/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h index 755945c..e95c0cb 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h @@ -29,7 +29,7 @@ class HandlerFactory public: static std::unique_ptr create(const PublisherFbConfig config, const std::string& publisherFbGlobalId) { - if (config.sharedTs) + if (config.topicMode == TopicMode::Single) { return std::make_unique(config.valueFieldName, config.topicName.empty() ? publisherFbGlobalId : config.topicName); @@ -41,11 +41,6 @@ class HandlerFactory else return std::make_unique(config.valueFieldName); } - else if (config.topicMode == TopicMode::Single) - { - return std::make_unique(config.valueFieldName, - config.topicName.empty() ? publisherFbGlobalId : config.topicName); - } return std::make_unique(config.valueFieldName); } diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/types.h b/mqtt_streaming_module/include/mqtt_streaming_module/types.h index bffd918..b1975f6 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/types.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/types.h @@ -31,7 +31,6 @@ enum class SignalValueJSONKey { struct PublisherFbConfig { TopicMode topicMode; std::string topicName; - bool sharedTs; bool groupValues; SignalValueJSONKey valueFieldName; size_t groupValuesPackSize; diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index 4449c3e..fff682e 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -83,13 +83,6 @@ FunctionBlockTypePtr MqttPublisherFbImpl::CreateType() .setVisible(EvalValue(std::string("$") + PROPERTY_NAME_PUB_TOPIC_MODE + " == 1")); defaultConfig.addProperty(builder.build()); } - { - auto builder = BoolPropertyBuilder(PROPERTY_NAME_PUB_SHARED_TS, False) - .setVisible(EvalValue(std::string("$") + PROPERTY_NAME_PUB_TOPIC_MODE + " == 1")) - .setDescription("Enables the use of a shared timestamp for all signals when publishing in SingleTopic mode. " - "By default it is set to false."); - defaultConfig.addProperty(builder.build()); - } { auto builder = BoolPropertyBuilder(PROPERTY_NAME_PUB_GROUP_VALUES, False) @@ -174,6 +167,18 @@ void MqttPublisherFbImpl::onDisconnected(const InputPortPtr& inputPort) updateStatuses(); } +void MqttPublisherFbImpl::propertyChanged() +{ + auto lock = this->getRecursiveConfigLock(); + readProperties(); + handler = HandlerFactory::create(this->config, globalId.toStdString()); + updatePortsAndSignals(false); + validateInputPorts(); + updateTopics(); + updateSchema(); + updateStatuses(); +} + void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) { if (reassignPorts) @@ -392,7 +397,6 @@ void MqttPublisherFbImpl::readProperties() auto lock = this->getRecursiveConfigLock(); int tmpTopicMode = readProperty(PROPERTY_NAME_PUB_TOPIC_MODE, 0); - config.sharedTs = readProperty(PROPERTY_NAME_PUB_SHARED_TS, false); config.groupValues = readProperty(PROPERTY_NAME_PUB_GROUP_VALUES, false); int tmpValueFieldName = (readProperty(PROPERTY_NAME_PUB_VALUE_FIELD_NAME, 0)); config.groupValuesPackSize = readProperty(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE, DEFAULT_PUB_PACK_SIZE); @@ -435,7 +439,7 @@ void MqttPublisherFbImpl::readProperties() hasEmptyTopic = false; } - if (config.topicMode == TopicMode::Single || config.sharedTs) + if (config.topicMode == TopicMode::Single) { auto result = mqtt::MqttDataWrapper::validateTopic(config.topicName, loggerComponent); hasSettingError = !result.success; @@ -455,18 +459,6 @@ void MqttPublisherFbImpl::readProperties() } } -void MqttPublisherFbImpl::propertyChanged() -{ - auto lock = this->getRecursiveConfigLock(); - readProperties(); - handler = HandlerFactory::create(this->config, globalId.toStdString()); - updatePortsAndSignals(false); - validateInputPorts(); - updateTopics(); - updateSchema(); - updateStatuses(); -} - template retT MqttPublisherFbImpl::readProperty(const std::string& propertyName, const retT defaultValue) { diff --git a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp index 3da139f..b0f7757 100644 --- a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp @@ -211,7 +211,6 @@ class MqttPublisherFbHelper : public DaqTestHelper } void CreatePublisherFB(bool multiTopic, - bool sharedTs, bool groupV, bool useSignalNames, const std::string& topicName, @@ -221,7 +220,6 @@ class MqttPublisherFbHelper : public DaqTestHelper { auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, multiTopic ? 1 : 0); - config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, sharedTs ? True : False); config.setPropertyValue(PROPERTY_NAME_PUB_GROUP_VALUES, groupV ? True : False); config.setPropertyValue(PROPERTY_NAME_PUB_VALUE_FIELD_NAME, useSignalNames ? 2 : 0); config.setPropertyValue(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE, valuePackSize); @@ -521,17 +519,12 @@ TEST_F(MqttPublisherFbTest, DefaultConfig) ASSERT_TRUE(defaultConfig.assigned()); - ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 9u); + ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 8u); ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_PUB_TOPIC_MODE)); ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_PUB_TOPIC_MODE).getValueType(), CoreType::ctInt); ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE).asPtr(), 0u); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_PUB_SHARED_TS)); - ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_PUB_SHARED_TS).getValueType(), CoreType::ctBool); - ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_PUB_SHARED_TS).asPtr(), False); - ASSERT_FALSE(defaultConfig.getProperty(PROPERTY_NAME_PUB_SHARED_TS).getVisible()); - ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_PUB_GROUP_VALUES)); ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_PUB_GROUP_VALUES).getValueType(), CoreType::ctBool); ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_PUB_GROUP_VALUES).asPtr(), False); @@ -569,10 +562,6 @@ TEST_F(MqttPublisherFbTest, PropertyVisibility) daq::FunctionBlockTypePtr fbt = MqttPublisherFbImpl::CreateType(); daq::PropertyObjectPtr defaultConfig = fbt.createDefaultConfig(); - ASSERT_FALSE(defaultConfig.getProperty(PROPERTY_NAME_PUB_SHARED_TS).getVisible()); - defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); // Set to Multi topic - ASSERT_TRUE(defaultConfig.getProperty(PROPERTY_NAME_PUB_SHARED_TS).getVisible()); - defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0); // Set to Single topic defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_GROUP_VALUES, True); ASSERT_TRUE(defaultConfig.getProperty(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE).getVisible()); @@ -599,7 +588,6 @@ TEST_F(MqttPublisherFbTest, Config) auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); - config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); config.setPropertyValue(PROPERTY_NAME_PUB_GROUP_VALUES, True); config.setPropertyValue(PROPERTY_NAME_PUB_VALUE_FIELD_NAME, 1); config.setPropertyValue(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE, 3); @@ -631,7 +619,6 @@ TEST_F(MqttPublisherFbTest, Config) MqttPublisherFbImpl* ptr = reinterpret_cast(fb.getObject()); ASSERT_TRUE(ptr != nullptr); EXPECT_EQ(ptr->getFbConfig().topicMode, TopicMode::Single); - EXPECT_TRUE(ptr->getFbConfig().sharedTs); EXPECT_TRUE(ptr->getFbConfig().groupValues); EXPECT_TRUE(ptr->getFbConfig().enablePreview); EXPECT_EQ(ptr->getFbConfig().valueFieldName, SignalValueJSONKey::LocalID); @@ -784,7 +771,8 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) { daq::FunctionBlockPtr fb; auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME, String(buildTopicName())); ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); auto help = SignalHelper(); @@ -800,7 +788,8 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) { daq::FunctionBlockPtr fb; auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME, String(buildTopicName())); ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); auto help = SignalHelper(); @@ -819,7 +808,8 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) { daq::FunctionBlockPtr fb; auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME, String(buildTopicName())); ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); auto help = SignalHelper(); @@ -843,7 +833,6 @@ TEST_F(MqttPublisherFbTest, PreviewSignals) daq::FunctionBlockPtr fb; auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0); - config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, False); config.setPropertyValue(PROPERTY_NAME_PUB_PREVIEW_SIGNAL, False); ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); auto help = SignalHelper(); @@ -936,7 +925,7 @@ TEST_F(MqttPublisherFbTest, WrongConfig) StartUp(); daq::FunctionBlockPtr fb; auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); - config.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME, String("/test/#")); ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), @@ -946,7 +935,7 @@ TEST_F(MqttPublisherFbTest, WrongConfig) static_cast(MqttPublisherFbImpl::SettingStatus::Invalid), daqInstance.getContext().getTypeManager())); - fb.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, False); + fb.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0); SignalHelper helper; fb.getInputPorts()[0].connect(helper.signal0); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), @@ -956,7 +945,7 @@ TEST_F(MqttPublisherFbTest, WrongConfig) static_cast(MqttPublisherFbImpl::SettingStatus::Valid), daqInstance.getContext().getTypeManager())); - fb.setPropertyValue(PROPERTY_NAME_PUB_SHARED_TS, True); + fb.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); fb.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME, String("/test/+/test")); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); @@ -1105,7 +1094,7 @@ TEST_P(MqttPublisherFbPTest, TransferSingleGroupValues) { StartUp(); - ASSERT_NO_THROW(CreatePublisherFB(false, false, true, false, buildTopicName(), packSize)); + ASSERT_NO_THROW(CreatePublisherFB(false, true, false, buildTopicName(), packSize)); fb.getInputPorts()[0].connect(help.signal0); @@ -1161,7 +1150,7 @@ TEST_P(MqttPublisherFbPTest, TransferSharedTs) { StartUp(); const std::string topic = buildTopicName(); - ASSERT_NO_THROW(CreatePublisherFB(true, true, false, false, topic)); + ASSERT_NO_THROW(CreatePublisherFB(true, false, false, topic)); fb.getInputPorts()[0].connect(help.signal0); fb.getInputPorts()[1].connect(help.signal1); @@ -1208,7 +1197,7 @@ TEST_P(MqttPublisherFbPTest, TransferSharedTs) param); } -TEST_P(MqttPublisherFbPTest, TransferMultimessage) +TEST_P(MqttPublisherFbPTest, DISABLED_TransferMultimessage) { constexpr size_t sampleCnt = 15; H param = GetParam(); @@ -1217,7 +1206,7 @@ TEST_P(MqttPublisherFbPTest, TransferMultimessage) { StartUp(); const std::string topic = buildTopicName(); - ASSERT_NO_THROW(CreatePublisherFB(true, false, false, false, topic)); + ASSERT_NO_THROW(CreatePublisherFB(true, false, false, topic)); fb.getInputPorts()[0].connect(help.signal0); fb.getInputPorts()[1].connect(help.signal1); From 445dd396b750a1574e7f18d190d35d2119b066d8 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 21 Jan 2026 16:48:19 +0100 Subject: [PATCH 20/49] mqtt: removed() for publisher FB --- .../mqtt_streaming_module/mqtt_publisher_fb_impl.h | 1 + mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h index 7b637fe..5d82d8f 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h @@ -112,6 +112,7 @@ class MqttPublisherFbImpl final : public FunctionBlock void runReaderThread(); void readerLoop(); void sendMessages(const MqttData& data); + void removed() override; }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index fff682e..7d64476 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -58,9 +58,21 @@ MqttPublisherFbImpl::MqttPublisherFbImpl(const ContextPtr& ctx, } MqttPublisherFbImpl::~MqttPublisherFbImpl() +{ + if (running) + { + running = false; + readerThread.join(); + } +} + +void MqttPublisherFbImpl::removed() { running = false; readerThread.join(); + auto lock = this->getRecursiveConfigLock(); + handler = nullptr; + FunctionBlock::removed(); } FunctionBlockTypePtr MqttPublisherFbImpl::CreateType() From 3b7ce49767b6eece74c5ab4eaa6c6e0c7bb3f878 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Thu, 22 Jan 2026 11:59:59 +0100 Subject: [PATCH 21/49] mqtt: parent for all publisher handlers; propper addInputPots for multiReader --- .../atomic_signal_atomic_sample_handler.h | 3 ++- .../atomic_signal_sample_arr_handler.h | 3 ++- .../group_signal_shared_ts_handler.h | 3 ++- .../include/mqtt_streaming_module/handler_base.h | 4 ++++ .../mqtt_streaming_module/handler_factory.h | 14 ++++++++------ .../signal_arr_atomic_sample_handler.h | 3 ++- .../src/atomic_signal_atomic_sample_handler.cpp | 5 +++-- .../src/atomic_signal_sample_arr_handler.cpp | 4 ++-- .../src/group_signal_shared_ts_handler.cpp | 16 +++++++++++++--- .../src/mqtt_publisher_fb_impl.cpp | 5 +++-- .../src/signal_arr_atomic_sample_handler.cpp | 5 +++-- 11 files changed, 44 insertions(+), 21 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h index 1a52bd9..be1d1d1 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h @@ -16,6 +16,7 @@ #pragma once +#include #include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE @@ -23,7 +24,7 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE class AtomicSignalAtomicSampleHandler : public HandlerBase { public: - explicit AtomicSignalAtomicSampleHandler(SignalValueJSONKey signalNamesMode); + explicit AtomicSignalAtomicSampleHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode); MqttData processSignalContexts(std::vector& signalContexts) override; ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const override; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h index 724a6cb..4084a9e 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h @@ -16,6 +16,7 @@ #pragma once +#include #include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE @@ -23,7 +24,7 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE class AtomicSignalSampleArrayHandler : public AtomicSignalAtomicSampleHandler { public: - explicit AtomicSignalSampleArrayHandler(SignalValueJSONKey signalNamesMode, size_t packSize); + explicit AtomicSignalSampleArrayHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode, size_t packSize); std::string getSchema() override; protected: diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h index c088d2e..dc5fd77 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include @@ -33,7 +34,7 @@ struct TimestampTickStruct class GroupSignalSharedTsHandler : public HandlerBase { public: - explicit GroupSignalSharedTsHandler(SignalValueJSONKey signalNamesMode, std::string topic); + explicit GroupSignalSharedTsHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode, std::string topic); ~GroupSignalSharedTsHandler(); MqttData processSignalContexts(std::vector& signalContexts) override; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h index f9fa8b9..36c6755 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h @@ -16,6 +16,7 @@ #pragma once +#include #include "mqtt_streaming_module/common.h" #include #include @@ -26,6 +27,7 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE class HandlerBase { public: + HandlerBase(WeakRefPtr parentFb) : parentFb(parentFb) {} virtual ~HandlerBase() = default; virtual MqttData processSignalContexts(std::vector& signalContexts) = 0; virtual ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const = 0; @@ -34,6 +36,8 @@ class HandlerBase virtual std::string getSchema() = 0; protected: + WeakRefPtr parentFb; + static std::pair calculateRatio(const DataDescriptorPtr descriptor) { const auto tickResolution = descriptor.getTickResolution().simplify(); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h b/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h index e95c0cb..769c0bf 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h @@ -27,22 +27,24 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE class HandlerFactory { public: - static std::unique_ptr create(const PublisherFbConfig config, const std::string& publisherFbGlobalId) + static std::unique_ptr + create(WeakRefPtr parentFb, const PublisherFbConfig config, const std::string& publisherFbGlobalId) { if (config.topicMode == TopicMode::Single) { - return std::make_unique(config.valueFieldName, - config.topicName.empty() ? publisherFbGlobalId : config.topicName); + return std::make_unique(parentFb, + config.valueFieldName, + config.topicName.empty() ? publisherFbGlobalId : config.topicName); } else if (config.topicMode == TopicMode::PerSignal) { if (config.groupValues) - return std::make_unique(config.valueFieldName, config.groupValuesPackSize); + return std::make_unique(parentFb, config.valueFieldName, config.groupValuesPackSize); else - return std::make_unique(config.valueFieldName); + return std::make_unique(parentFb, config.valueFieldName); } - return std::make_unique(config.valueFieldName); + return std::make_unique(parentFb, config.valueFieldName); } }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h index e0b9092..b45b984 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h @@ -16,6 +16,7 @@ #pragma once +#include #include #include @@ -24,7 +25,7 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE class SignalArrayAtomicSampleHandler : public HandlerBase { public: - explicit SignalArrayAtomicSampleHandler(SignalValueJSONKey signalNamesMode, std::string topic); + explicit SignalArrayAtomicSampleHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode, std::string topic); MqttData processSignalContexts(std::vector& signalContexts) override; ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const override; diff --git a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp index 830ad62..dfaded1 100644 --- a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp @@ -8,8 +8,9 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -AtomicSignalAtomicSampleHandler::AtomicSignalAtomicSampleHandler(SignalValueJSONKey signalNamesMode) - : signalNamesMode(signalNamesMode) +AtomicSignalAtomicSampleHandler::AtomicSignalAtomicSampleHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode) + : HandlerBase(parentFb), + signalNamesMode(signalNamesMode) { } diff --git a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp index af5907c..b964fd3 100644 --- a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp @@ -7,8 +7,8 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -AtomicSignalSampleArrayHandler::AtomicSignalSampleArrayHandler(SignalValueJSONKey signalNamesMode, size_t packSize) - : AtomicSignalAtomicSampleHandler(signalNamesMode), +AtomicSignalSampleArrayHandler::AtomicSignalSampleArrayHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode, size_t packSize) + : AtomicSignalAtomicSampleHandler(parentFb, signalNamesMode), packSize(packSize > 0 ? packSize : 1) { } diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp index 2706635..768d4ee 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp @@ -11,8 +11,9 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -GroupSignalSharedTsHandler::GroupSignalSharedTsHandler(SignalValueJSONKey signalNamesMode, std::string topic) - : signalNamesMode(signalNamesMode), +GroupSignalSharedTsHandler::GroupSignalSharedTsHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode, std::string topic) + : HandlerBase(parentFb), + signalNamesMode(signalNamesMode), buffersSize(1000), topic(topic) { @@ -22,6 +23,10 @@ GroupSignalSharedTsHandler::~GroupSignalSharedTsHandler() { std::scoped_lock lock(sync); deallocateBuffers(); + if (reader.assigned()) + { + reader.dispose(); + } } MqttData GroupSignalSharedTsHandler::processSignalContexts(std::vector& signalContexts) @@ -267,7 +272,9 @@ void GroupSignalSharedTsHandler::createReader(const std::vector& return; if (reader.assigned()) - reader.release(); + { + reader.dispose(); + } auto multiReaderBuilder = MultiReaderBuilder().setValueReadType(SampleType::Undefined).setDomainReadType(SampleType::UInt64); for (const auto& sContext : signalContexts) @@ -277,6 +284,9 @@ void GroupSignalSharedTsHandler::createReader(const std::vector& } reader = multiReaderBuilder.build(); + const auto parentFb = this->parentFb.getRef(); + if (parentFb.assigned()) + reader.setExternalListener(parentFb.asPtr()); allocateBuffers(signalContexts); } diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index 7d64476..4d23291 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -49,7 +49,7 @@ MqttPublisherFbImpl::MqttPublisherFbImpl(const ContextPtr& ctx, else initProperties(type.createDefaultConfig()); - handler = HandlerFactory::create(this->config, globalId.toStdString()); + handler = HandlerFactory::create(this->template getWeakRefInternal(), this->config, globalId.toStdString()); updatePortsAndSignals(true); validateInputPorts(); updateSchema(); @@ -183,7 +183,7 @@ void MqttPublisherFbImpl::propertyChanged() { auto lock = this->getRecursiveConfigLock(); readProperties(); - handler = HandlerFactory::create(this->config, globalId.toStdString()); + handler = HandlerFactory::create(this->template getWeakRefInternal(), this->config, globalId.toStdString()); updatePortsAndSignals(false); validateInputPorts(); updateTopics(); @@ -223,6 +223,7 @@ void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) for (auto it = signalContexts.begin(); it != signalContexts.end();) { + it->inputPort.setListener(this->template borrowPtr()); if (!it->inputPort.getSignal().assigned()) { ++it; diff --git a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp index c6cfe23..b1b91f6 100644 --- a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp @@ -10,8 +10,9 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE -SignalArrayAtomicSampleHandler::SignalArrayAtomicSampleHandler(SignalValueJSONKey signalNamesMode, std::string topic) - : signalNamesMode(signalNamesMode), +SignalArrayAtomicSampleHandler::SignalArrayAtomicSampleHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode, std::string topic) + : HandlerBase(parentFb), + signalNamesMode(signalNamesMode), topic(topic) { } From 627a4847d0e578202e666ee6fdb43f5f9adc189d Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 23 Jan 2026 12:23:07 +0100 Subject: [PATCH 22/49] mqtt: new checks for publisher FB signal validation --- .../atomic_signal_atomic_sample_handler.h | 2 - .../group_signal_shared_ts_handler.h | 1 - .../mqtt_streaming_module/handler_base.h | 26 ++++++++++++- .../signal_arr_atomic_sample_handler.h | 1 - .../include/mqtt_streaming_module/types.h | 6 +++ .../atomic_signal_atomic_sample_handler.cpp | 4 +- .../src/group_signal_shared_ts_handler.cpp | 26 ++++++++++++- .../src/signal_arr_atomic_sample_handler.cpp | 6 +-- .../tests/test_mqtt_publisher_fb.cpp | 38 ++++++++++++++++++- 9 files changed, 95 insertions(+), 15 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h index be1d1d1..14d4163 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h @@ -35,8 +35,6 @@ class AtomicSignalAtomicSampleHandler : public HandlerBase ListPtr getTopics(const std::vector& signalContexts) override; std::string getSchema() override; protected: - SignalValueJSONKey signalNamesMode; - virtual MqttData processSignalContext(SignalContext& signalContext); void processSignalDescriptorChanged(SignalContext& signalCtx, const DataDescriptorPtr& valueSigDesc, const DataDescriptorPtr& domainSigDesc); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h index dc5fd77..0590a80 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h @@ -44,7 +44,6 @@ class GroupSignalSharedTsHandler : public HandlerBase std::string getSchema() override; protected: - SignalValueJSONKey signalNamesMode; const size_t buffersSize; const std::string topic; std::vector dataBuffers; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h index 36c6755..4cf5b81 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h @@ -27,16 +27,38 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE class HandlerBase { public: - HandlerBase(WeakRefPtr parentFb) : parentFb(parentFb) {} + HandlerBase(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode) + : parentFb(parentFb), + signalNamesMode(signalNamesMode) + { + } virtual ~HandlerBase() = default; virtual MqttData processSignalContexts(std::vector& signalContexts) = 0; - virtual ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const = 0; + virtual ProcedureStatus validateSignalContexts(const std::vector& signalContexts) const + { + ProcedureStatus status{true, {}}; + std::unordered_set globalIdSet; + for (const auto& sigCtx : signalContexts) + { + auto signal = sigCtx.inputPort.getSignal(); + if (!signal.assigned()) + continue; + std::string globalId = signal.getGlobalId(); + if (globalIdSet.find(globalId) != globalIdSet.end()) + { + status.addError(fmt::format("Connected signals have non-unique GlobalIDs (\"{}\").", globalId)); + } + globalIdSet.insert(std::move(globalId)); + } + return status; + } virtual ProcedureStatus signalListChanged(std::vector& signalContexts) = 0; virtual ListPtr getTopics(const std::vector& signalContexts) = 0; virtual std::string getSchema() = 0; protected: WeakRefPtr parentFb; + SignalValueJSONKey signalNamesMode; static std::pair calculateRatio(const DataDescriptorPtr descriptor) { diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h index b45b984..a4bffc0 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h @@ -34,7 +34,6 @@ class SignalArrayAtomicSampleHandler : public HandlerBase std::string getSchema() override; protected: - SignalValueJSONKey signalNamesMode; const std::string topic; std::string toString(const std::string valueFieldName, daq::DataPacketPtr packet); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/types.h b/mqtt_streaming_module/include/mqtt_streaming_module/types.h index b1975f6..208b630 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/types.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/types.h @@ -57,6 +57,12 @@ struct ProcedureStatus success = false; messages.push_back(msg); } + + void merge(const ProcedureStatus& other) + { + success = success && other.success; + messages.insert(messages.end(), other.messages.begin(), other.messages.end()); + } }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp index dfaded1..2ae01b0 100644 --- a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp @@ -9,8 +9,7 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE AtomicSignalAtomicSampleHandler::AtomicSignalAtomicSampleHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode) - : HandlerBase(parentFb), - signalNamesMode(signalNamesMode) + : HandlerBase(parentFb, signalNamesMode) { } @@ -82,6 +81,7 @@ ProcedureStatus AtomicSignalAtomicSampleHandler::validateSignalContexts(const st } } } + status.merge(HandlerBase::validateSignalContexts(signalContexts)); return status; } diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp index 768d4ee..7e40e68 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp @@ -12,8 +12,7 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE GroupSignalSharedTsHandler::GroupSignalSharedTsHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode, std::string topic) - : HandlerBase(parentFb), - signalNamesMode(signalNamesMode), + : HandlerBase(parentFb, signalNamesMode), buffersSize(1000), topic(topic) { @@ -154,6 +153,29 @@ ProcedureStatus GroupSignalSharedTsHandler::validateSignalContexts(const std::ve { status.addError(fmt::format("Connected signals have incompatible sample rates. This is not allowed.")); } + + std::unordered_set namesSet; + for (const auto& sigCtx : signalContexts) + { + auto signal = sigCtx.inputPort.getSignal(); + if (!signal.assigned()) + continue; + std::string name = buildValueFieldName(signalNamesMode, signal); + if (namesSet.find(name) != namesSet.end()) + { + std::string key; + if (signalNamesMode == SignalValueJSONKey::GlobalID) + key = "GlobalID"; + else if (signalNamesMode == SignalValueJSONKey::LocalID) + key = "LocalID"; + else + key = "name"; + status.addError( + fmt::format("Connected signals have non-unique {} (\"{}\"). JSON field names cannot be built", key, name)); + } + namesSet.insert(std::move(name)); + } + status.merge(HandlerBase::validateSignalContexts(signalContexts)); return status; } diff --git a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp index b1b91f6..6f8fb63 100644 --- a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp @@ -11,8 +11,7 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE SignalArrayAtomicSampleHandler::SignalArrayAtomicSampleHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode, std::string topic) - : HandlerBase(parentFb), - signalNamesMode(signalNamesMode), + : HandlerBase(parentFb, signalNamesMode), topic(topic) { } @@ -61,8 +60,6 @@ ProcedureStatus SignalArrayAtomicSampleHandler::validateSignalContexts(const std static const std::set allowedSampleTypes{SampleType::Float64, SampleType::Float32, - SampleType::Float32, - SampleType::Float64, SampleType::UInt8, SampleType::Int8, SampleType::UInt16, @@ -115,6 +112,7 @@ ProcedureStatus SignalArrayAtomicSampleHandler::validateSignalContexts(const std } } } + status.merge(HandlerBase::validateSignalContexts(signalContexts)); return status; } diff --git a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp index b0f7757..35114bd 100644 --- a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp @@ -744,15 +744,26 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStOk); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStValid); - fb.getInputPorts()[1].connect(help.signal0); + fb.getInputPorts()[1].connect(help.signal1); ASSERT_EQ(fb.getInputPorts().getCount(), 3u); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStOk); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStValid); + + fb.getInputPorts()[2].connect(help.signal0); + ASSERT_EQ(fb.getInputPorts().getCount(), 4u); + ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStError); + ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStInvalid); // disconnection + fb.getInputPorts()[1].disconnect(); + ASSERT_EQ(fb.getInputPorts().getCount(), 3u); + ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStError); + ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStInvalid); + fb.getInputPorts()[1].disconnect(); ASSERT_EQ(fb.getInputPorts().getCount(), 2u); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStOk); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStValid); + // connection without a domain signal fb.getInputPorts()[1].connect(help.signalWithoutDomain); ASSERT_EQ(fb.getInputPorts().getCount(), 3u); @@ -824,6 +835,31 @@ TEST_F(MqttPublisherFbTest, ConnectToPort) ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStOk); ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStValid); } + + { + daq::FunctionBlockPtr fb; + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(PUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); + config.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_NAME, String(buildTopicName())); + ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(PUB_FB_NAME, config)); + auto help = SignalHelper(); + + auto signal0 = help.createSignal(DataDescriptorBuilder().setRule(LinearDataRule(1, 3)).setTickResolution(Ratio(1, 500))); + auto signal1 = help.createSignal(DataDescriptorBuilder().setRule(LinearDataRule(1, 3)).setTickResolution(Ratio(1, 500))); + signal0.setName("signal"); + signal1.setName("signal"); + fb.getInputPorts()[0].connect(signal0); + fb.getInputPorts()[1].connect(signal1); + ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStOk); + ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStValid); + fb.setPropertyValue(PROPERTY_NAME_PUB_VALUE_FIELD_NAME, 2); // Set to SignalName + ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStError); + ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStInvalid); + fb.getInputPorts()[0].disconnect(); + ASSERT_EQ(fb.getInputPorts().getCount(), 2u); + ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), comStOk); + ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SIG_STATUS_NAME), sigStValid); + } } TEST_F(MqttPublisherFbTest, PreviewSignals) From e78402484fb7033ccf3940a575704a4a1a80d515 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 23 Jan 2026 12:38:24 +0100 Subject: [PATCH 23/49] mqtt: publisher FB statuses improvement --- .../mqtt_publisher_fb_impl.h | 9 +- .../mqtt_streaming_module/status_helper.h | 10 +- .../src/mqtt_publisher_fb_impl.cpp | 133 +++++++++++------- 3 files changed, 90 insertions(+), 62 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h index 5d82d8f..0685d31 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h @@ -17,7 +17,6 @@ #pragma once #include "MqttAsyncClient.h" #include "MqttDataWrapper.h" -#include "mqtt_streaming_helper/timer.h" #include "mqtt_streaming_module/handler_base.h" #include "mqtt_streaming_module/status_helper.h" #include @@ -91,14 +90,14 @@ class MqttPublisherFbImpl final : public FunctionBlock StatusHelper signalStatus; StatusHelper publishingStatus; StatusHelper settingStatus; - std::atomic skippedMsgCnt; - std::atomic publishedMsgCnt; + std::atomic hasSkippedMsg; std::string lastSkippedReason; - helper::utils::Timer publishingStatusTimer; SignalConfigPtr commonPreviewSignal; static std::string generateLocalId(); - void updatePublishingStatus(bool force); + void updateComponentStatus(); + void updatePublishingStatus(); + std::string buildPublishingStatusMessage(); void initProperties(const PropertyObjectPtr& config); void readProperties(); void propertyChanged(); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h b/mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h index 208008a..2ce4a96 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h @@ -41,6 +41,7 @@ class StatusHelper { addTypesToTypeManager(typeName, statusName, statusMap, typeManager); currentStatus = EnumerationWithIntValue(typeName, static_cast(initState), typeManager); + currentMessage = ""; statusContainer.template asPtr(true).addStatus(statusName, currentStatus); } @@ -59,15 +60,18 @@ class StatusHelper } } - void setStatus(T status, const std::string& message = "") + bool setStatus(T status, const std::string& message = "") { std::scoped_lock lock(statusMutex); const auto newStatus = EnumerationWithIntValue(typeName, static_cast(status), typeManager); - if (newStatus != currentStatus || message != this->message) + bool changed = (newStatus != currentStatus || message != currentMessage); + if (changed) { currentStatus = newStatus; + currentMessage = message; statusContainer.template asPtr(true).setStatusWithMessage(statusName, currentStatus, message); } + return changed; } T getStatus() { @@ -78,7 +82,7 @@ class StatusHelper private: const std::string typeName; const std::string statusName; - std::string message; + std::string currentMessage; ComponentStatusContainerPtr statusContainer; const std::vector>& statusMap; EnumerationPtr currentStatus; diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index 4d23291..ba30a9a 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -40,8 +40,7 @@ MqttPublisherFbImpl::MqttPublisherFbImpl(const ContextPtr& ctx, settingStatusMap, SettingStatus::Valid, context.getTypeManager()), - skippedMsgCnt(0), - publishingStatusTimer(helper::utils::Timer(1000, false)) + hasSkippedMsg(false) { initComponentStatus(); if (config.assigned()) @@ -59,7 +58,7 @@ MqttPublisherFbImpl::MqttPublisherFbImpl(const ContextPtr& ctx, MqttPublisherFbImpl::~MqttPublisherFbImpl() { - if (running) + if (readerThread.joinable()) { running = false; readerThread.join(); @@ -68,10 +67,15 @@ MqttPublisherFbImpl::~MqttPublisherFbImpl() void MqttPublisherFbImpl::removed() { - running = false; - readerThread.join(); - auto lock = this->getRecursiveConfigLock(); - handler = nullptr; + if (readerThread.joinable()) + { + running = false; + readerThread.join(); + } + { + auto lock = this->getRecursiveConfigLock(); + handler = nullptr; + } FunctionBlock::removed(); } @@ -281,7 +285,6 @@ void MqttPublisherFbImpl::updateStatuses() std::string allMessages; for (const auto& msg : errors) { - LOG_E("{}", msg); allMessages += msg + "; "; } return allMessages; @@ -309,37 +312,20 @@ void MqttPublisherFbImpl::updateStatuses() settingStatus.setStatus(SettingStatus::Valid); } - if (hasSignalError) - { - setComponentStatusWithMessage(ComponentStatus::Error, "Some connected signals were invalidated!"); - } - else if (hasSettingError) - { - setComponentStatusWithMessage(ComponentStatus::Error, "Some property has wrong value!"); - } - else if (signalContexts.size() == 1) // no one input port is connected - { - setComponentStatusWithMessage(ComponentStatus::Warning, "No input ports are connected!"); - } - else if (hasEmptyTopic) + if (hasSkippedMsg) { - setComponentStatusWithMessage(ComponentStatus::Warning, "Topic property is empty! Using FB Global ID as topic name."); - } - else if (skippedMsgCnt != 0) - { - setComponentStatusWithMessage(ComponentStatus::Warning, "Some messages were not published!"); + publishingStatus.setStatus(PublishingStatus::SampleSkipped, buildPublishingStatusMessage()); } else { - setComponentStatus(ComponentStatus::Ok); + publishingStatus.setStatus(PublishingStatus::Ok); } - updatePublishingStatus(true); + updateComponentStatus(); } void MqttPublisherFbImpl::validateInputPorts() { - skippedMsgCnt = 0; - publishedMsgCnt = 0; + hasSkippedMsg = false; if (signalContexts.size() == 1) // no one input port is connected { hasSignalError = false; @@ -506,7 +492,7 @@ void MqttPublisherFbImpl::readerLoop() msgs = handler->processSignalContexts(signalContexts); } sendMessages(msgs); - updatePublishingStatus(false); + updatePublishingStatus(); } std::this_thread::sleep_for(std::chrono::milliseconds(config.periodMs)); } @@ -525,13 +511,8 @@ void MqttPublisherFbImpl::sendMessages(const MqttData& data) auto status = mqttClient->publish(topic, (void*)msg.c_str(), msg.length(), config.qos); if (!status.success) { - ++skippedMsgCnt; + hasSkippedMsg = true; lastSkippedReason = std::move(status.msg); - LOG_W("Failed to publish data to {}; reason - {}", topic, lastSkippedReason); - } - else - { - ++publishedMsgCnt; } } } @@ -541,25 +522,69 @@ std::string MqttPublisherFbImpl::generateLocalId() return std::string(MQTT_LOCAL_PUB_FB_ID_PREFIX + std::to_string(localIndex++)); } -void MqttPublisherFbImpl::updatePublishingStatus(bool force) +void MqttPublisherFbImpl::updateComponentStatus() { - if (publishingStatusTimer.expired() || force) + std::string componentMsg; + if (hasSignalError) { - publishingStatusTimer.restart(); - if (skippedMsgCnt != 0) - { - if (statusContainer.getStatus("ComponentStatus") == ComponentStatus::Ok) - setComponentStatusWithMessage(ComponentStatus::Warning, "Some messages were not published!"); - publishingStatus.setStatus(PublishingStatus::SampleSkipped, - fmt::format("Published: {}; Skipped: {}; last reason - {}", - publishedMsgCnt.load(), - skippedMsgCnt.load(), - lastSkippedReason)); - } - else - { - publishingStatus.setStatus(PublishingStatus::Ok, fmt::format("Published: {};", publishedMsgCnt.load())); - } + componentMsg.append("Signal status is invalid: "); + for (const auto& m : signalErrors) + componentMsg.append(m + "; "); + } + else if (signalContexts.size() == 1) // no one input port is connected + { + componentMsg.append("No input ports are connected! "); } + + if (hasSettingError) + { + componentMsg.append("Settings are invalid: "); + for (const auto& m : settingErrors) + componentMsg.append(m + "; "); + } + + if (hasEmptyTopic) + { + componentMsg.append("Topic property is empty! Using FB Global ID as topic name. "); + } + + if (hasSkippedMsg) + { + componentMsg.append("Publishing warning: "); + componentMsg.append(buildPublishingStatusMessage()); + } + + if (hasSignalError || hasSettingError) + { + setComponentStatusWithMessage(ComponentStatus::Error, componentMsg); + } + else if (signalContexts.size() == 1 || hasEmptyTopic || hasSkippedMsg) + { + setComponentStatusWithMessage(ComponentStatus::Warning, componentMsg); + } + else + { + setComponentStatus(ComponentStatus::Ok); + } +} + +void MqttPublisherFbImpl::updatePublishingStatus() +{ + bool changed = false; + if (hasSkippedMsg) + { + changed = publishingStatus.setStatus(PublishingStatus::SampleSkipped, buildPublishingStatusMessage()); + } + else + { + changed = publishingStatus.setStatus(PublishingStatus::Ok); + } + if (changed) + updateComponentStatus(); +} + +inline std::string MqttPublisherFbImpl::buildPublishingStatusMessage() +{ + return fmt::format("Some sample were not published! Last reason is \"{}\"", lastSkippedReason); } END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE From 0512e59240bf3c965172ad0acc72b10748306eb5 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 23 Jan 2026 13:06:24 +0100 Subject: [PATCH 24/49] gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 8de9dce..17b3a1f 100644 --- a/.gitignore +++ b/.gitignore @@ -53,7 +53,11 @@ include/* lib/* bin/* test/test_runner +docker/* +build*/* CMakeLists.txt.user .vscode .clang-format +.dockerignore + From eb867a4348ec2d1c03ac2312957ad1c8f703e231 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 23 Jan 2026 14:01:28 +0100 Subject: [PATCH 25/49] mqtt: removing SubscribingStatus from subscriber FB --- .../include/mqtt_streaming_module/constants.h | 2 - .../mqtt_subscriber_fb_impl.h | 12 +----- .../src/mqtt_subscriber_fb_impl.cpp | 40 +++++++----------- .../tests/test_mqtt_subscriber_fb.cpp | 42 ++++--------------- 4 files changed, 23 insertions(+), 73 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index dacfb94..9720e71 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -63,7 +63,6 @@ static const char* MQTT_LOCAL_JSON_DECODER_FB_ID_PREFIX = "MQTTJSONDecoderFB"; static const char* MQTT_CLIENT_FB_CON_STATUS_TYPE = "BrokerConnectionStatusType"; -static const char* MQTT_FB_SUB_STATUS_TYPE = "MQTTSubscriptionStatusType"; static const char* MQTT_PUB_FB_SIG_STATUS_TYPE = "MQTTSignalStatusType"; static const char* MQTT_PUB_FB_PUB_STATUS_TYPE = "MQTTPublishingStatusType"; static const char* MQTT_FB_PARSING_STATUS_TYPE = "MQTTParsingStatusType"; @@ -73,7 +72,6 @@ static const char* MQTT_PUB_FB_SET_STATUS_TYPE = "MQTTSettingStatusType"; static const char* MQTT_CLIENT_FB_CON_STATUS_NAME = "ConnectionStatus"; static const char* MQTT_PUB_FB_SIG_STATUS_NAME = "SignalStatus"; static const char* MQTT_PUB_FB_PUB_STATUS_NAME = "PublishingStatus"; -static const char* MQTT_FB_SUB_STATUS_NAME = "SubscriptionStatus"; static const char* MQTT_FB_PARSING_STATUS_NAME = "ParsingStatus"; static const char* MQTT_PUB_FB_SET_STATUS_NAME = "SettingStatus"; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h index 928f1b9..e9c39a6 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h @@ -20,7 +20,6 @@ #include #include #include "mqtt_streaming_module/constants.h" -#include "mqtt_streaming_module/status_helper.h" #include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE @@ -31,14 +30,6 @@ class MqttSubscriberFbImpl final : public FunctionBlock friend class MqttJsonDecoderFbHelper; public: - enum class SubscriptionStatus : EnumType - { - InvalidTopicName = 0, - SubscribingError, - WaitingForData, - HasData - }; - struct CmdResult { bool success = false; @@ -65,11 +56,9 @@ class MqttSubscriberFbImpl final : public FunctionBlock DAQ_MQTT_STREAM_MODULE_API std::string getSubscribedTopic() const; protected: - static std::vector> subscriptionStatusMap; static std::atomic localIndex; std::shared_ptr subscriber; - StatusHelper subscriptionStatus; int qos = DEFAULT_SUB_QOS; mqtt::MqttDataWrapper jsonDataWorker; std::string topicForSubscribing; @@ -77,6 +66,7 @@ class MqttSubscriberFbImpl final : public FunctionBlock std::vector nestedFunctionBlocks; SignalConfigPtr outputSignal; bool enablePreview; + std::atomic waitingForData; DAQ_MQTT_STREAM_MODULE_API void onSignalsMessage(const mqtt::MqttAsyncClient& subscriber, const mqtt::MqttMessage& msg); diff --git a/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp index d43cabf..aebc776 100644 --- a/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp @@ -12,11 +12,6 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE constexpr int MQTT_FB_UNSUBSCRIBE_TOUT = 3000; std::atomic MqttSubscriberFbImpl::localIndex = 0; -std::vector> MqttSubscriberFbImpl::subscriptionStatusMap = - {{SubscriptionStatus::InvalidTopicName, "InvalidTopicName"}, - {SubscriptionStatus::SubscribingError, "SubscribingError"}, - {SubscriptionStatus::WaitingForData, "WaitingForData"}, - {SubscriptionStatus::HasData, "HasData"}}; MqttSubscriberFbImpl::MqttSubscriberFbImpl(const ContextPtr& ctx, const ComponentPtr& parent, @@ -25,13 +20,8 @@ MqttSubscriberFbImpl::MqttSubscriberFbImpl(const ContextPtr& ctx, const PropertyObjectPtr& config) : FunctionBlock(type, ctx, parent, generateLocalId()), subscriber(subscriber), - subscriptionStatus(MQTT_FB_SUB_STATUS_TYPE, - MQTT_FB_SUB_STATUS_NAME, - statusContainer, - subscriptionStatusMap, - SubscriptionStatus::InvalidTopicName, - context.getTypeManager()), - jsonDataWorker(loggerComponent) + jsonDataWorker(loggerComponent), + waitingForData(false) { initComponentStatus(); initNestedFbTypes(); @@ -319,13 +309,13 @@ bool MqttSubscriberFbImpl::setTopic(std::string topic) { LOG_I("An MQTT topic: {}", topic); topicForSubscribing = std::move(topic); - setComponentStatus(ComponentStatus::Ok); - subscriptionStatus.setStatus(SubscriptionStatus::WaitingForData, "Subscribing to topic: " + topicForSubscribing); + setComponentStatusWithMessage(ComponentStatus::Ok, "Waiting for data for the topic: " + topicForSubscribing); + waitingForData = true; } else { - setComponentStatusWithMessage(ComponentStatus::Warning, validationStatus.msg); - subscriptionStatus.setStatus(SubscriptionStatus::InvalidTopicName, validationStatus.msg); + setComponentStatusWithMessage(ComponentStatus::Error, "Invalid topic name: " + validationStatus.msg); + waitingForData = false; } return validationStatus.success; } @@ -383,9 +373,10 @@ void MqttSubscriberFbImpl::processMessage(const mqtt::MqttMessage& msg) { if (topicForSubscribing == msg.getTopic()) { - if (subscriptionStatus.getStatus() == SubscriptionStatus::WaitingForData) + if (waitingForData) { - subscriptionStatus.setStatus(SubscriptionStatus::HasData); + setComponentStatusWithMessage(ComponentStatus::Ok, "Data has been received"); + waitingForData = false; } std::string jsonObjStr(msg.getData().begin(), msg.getData().end()); @@ -443,16 +434,16 @@ MqttSubscriberFbImpl::CmdResult MqttSubscriberFbImpl::subscribeToTopic() if (auto subRes = subscriber->subscribe(topic, qos); subRes.success == false) { LOG_W("Failed to subscribe to the topic: \"{}\"; reason: {}", topic, subRes.msg); - setComponentStatusWithMessage(ComponentStatus::Warning, "Some topics failed to subscribe!"); - subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, "The reason: " + subRes.msg); + setComponentStatusWithMessage(ComponentStatus::Error, "Some topics failed to subscribe! The reason: " + subRes.msg); + waitingForData = false; result = {false, "Failed to subscribe to the topic: \"" + topic + "\"; reason: " + subRes.msg}; } else { // subscriber->subscribe(...) is asynchronous. It puts command in queue and returns immediately. LOG_D("Trying to subscribe to the topic: {}", topic); - setComponentStatus(ComponentStatus::Ok); - subscriptionStatus.setStatus(SubscriptionStatus::WaitingForData, "Topic: " + topic); + setComponentStatusWithMessage(ComponentStatus::Ok, "Waiting for data for the topic: " + topicForSubscribing); + waitingForData = true; result = {true, "", result.token}; } } @@ -466,7 +457,6 @@ MqttSubscriberFbImpl::CmdResult MqttSubscriberFbImpl::subscribeToTopic() { const std::string msg = "MQTT subscriber client is not set!"; setComponentStatusWithMessage(ComponentStatus::Error, msg); - subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, msg); result = {false, msg}; } return result; @@ -495,8 +485,7 @@ MqttSubscriberFbImpl::CmdResult MqttSubscriberFbImpl::unsubscribeFromTopic() { const auto msg = fmt::format("Failed to unsubscribe from the topic \'{}\'; reason: {}", topic, unsubRes.msg); LOG_W("{}", msg); - setComponentStatus(ComponentStatus::Warning); - subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, msg); + setComponentStatusWithMessage(ComponentStatus::Error, msg); result = {false, msg}; } } @@ -505,7 +494,6 @@ MqttSubscriberFbImpl::CmdResult MqttSubscriberFbImpl::unsubscribeFromTopic() { const std::string msg = "MQTT subscriber client is not set!"; setComponentStatusWithMessage(ComponentStatus::Error, msg); - subscriptionStatus.setStatus(SubscriptionStatus::SubscribingError, msg); result = {false, msg}; } return result; diff --git a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp index 348743f..02d7a5c 100644 --- a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp @@ -140,7 +140,7 @@ TEST_F(MqttSubscriberFbTest, CreationWithDefaultConfig) ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME)); EXPECT_EQ(subFb.getSignals().getCount(), 0u); ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Warning", daqInstance.getContext().getTypeManager())); + Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); } TEST_F(MqttSubscriberFbTest, CreationWithPartialConfig) @@ -179,13 +179,8 @@ TEST_F(MqttSubscriberFbTest, SubscriptionStatusWaitingForData) config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, buildTopicName()); daq::FunctionBlockPtr subFb; ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); - EXPECT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), + ASSERT_EQ(subFb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - - EXPECT_EQ(subFb.getStatusContainer().getStatus("SubscriptionStatus"), - EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttSubscriberFbImpl::SubscriptionStatus::WaitingForData), - daqInstance.getContext().getTypeManager())); } TEST_P(MqttSubscriberFbTopicPTest, CheckSubscriberFbTopic) @@ -201,23 +196,9 @@ TEST_P(MqttSubscriberFbTopicPTest, CheckSubscriberFbTopic) ASSERT_NO_THROW(fb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); auto signals = fb.getSignals(); ASSERT_EQ(signals.getCount(), 1); - const auto expectedComponentStatus = result ? "Ok" : "Warning"; + const auto expectedComponentStatus = result ? "Ok" : "Error"; EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", expectedComponentStatus, daqInstance.getContext().getTypeManager())); - if (result) - { - EXPECT_NE(fb.getStatusContainer().getStatus("SubscriptionStatus"), - EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttSubscriberFbImpl::SubscriptionStatus::InvalidTopicName), - daqInstance.getContext().getTypeManager())); - } - else - { - EXPECT_EQ(fb.getStatusContainer().getStatus("SubscriptionStatus"), - EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttSubscriberFbImpl::SubscriptionStatus::InvalidTopicName), - daqInstance.getContext().getTypeManager())); - } } INSTANTIATE_TEST_SUITE_P(TopicTest, @@ -533,17 +514,9 @@ TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransferWithReconfiguring) auto singal = rawFB.getSignals()[0]; auto reader = daq::PacketReader(singal); - const auto stHasData = EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttSubscriberFbImpl::SubscriptionStatus::HasData), - daqInstance.getContext().getTypeManager()); - - const auto stWaitData = EnumerationWithIntValue(MQTT_FB_SUB_STATUS_TYPE, - static_cast(MqttSubscriberFbImpl::SubscriptionStatus::WaitingForData), - daqInstance.getContext().getTypeManager()); - MqttAsyncClientWrapper publisher("testPublisherId"); ASSERT_TRUE(publisher.connect("127.0.0.1")); - EXPECT_EQ(rawFB.getStatusContainer().getStatus("SubscriptionStatus"), stWaitData); + EXPECT_NE(rawFB.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Waiting for data"), std::string::npos); mqtt::MqttMessage msg = {topic0, dataToSend[0], 2, 0}; ASSERT_TRUE(publisher.publishMsg(msg)); @@ -569,7 +542,8 @@ TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransferWithReconfiguring) bool hasData = false; while (tmr.expired() == false && hasData == false) - hasData = rawFB.getStatusContainer().getStatus("SubscriptionStatus") == stHasData; + hasData = (rawFB.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Data has been received") != std::string::npos); + EXPECT_TRUE(hasData); @@ -582,7 +556,7 @@ TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransferWithReconfiguring) ASSERT_NO_THROW(rawFB.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, topic1)); EXPECT_EQ(rawFB.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - EXPECT_EQ(rawFB.getStatusContainer().getStatus("SubscriptionStatus"), stWaitData); + EXPECT_NE(rawFB.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Waiting for data"), std::string::npos); msg = {topic1, dataToSend[1], 2, 0}; ASSERT_TRUE(publisher.publishMsg(msg)); @@ -590,7 +564,7 @@ TEST_F(MqttSubscriberFbTest, CheckRawFbFullDataTransferWithReconfiguring) hasData = false; while (tmr.expired() == false && hasData == false) - hasData = rawFB.getStatusContainer().getStatus("SubscriptionStatus") == stHasData; + hasData = (rawFB.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Data has been received") != std::string::npos); EXPECT_TRUE(hasData); From ec7f92e6e3c0d89492563f8a787ae602016f5207 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 23 Jan 2026 15:41:32 +0100 Subject: [PATCH 26/49] mqtt: removing parsing status from JSON decoder FB; updating parsing error messages --- .../include/mqtt_streaming_module/constants.h | 2 - .../mqtt_json_decoder_fb_impl.h | 17 +-- .../src/mqtt_json_decoder_fb_impl.cpp | 70 +++++------ .../tests/test_mqtt_json_decoder_fb.cpp | 109 ++++-------------- .../src/MqttDataWrapper.cpp | 12 +- 5 files changed, 64 insertions(+), 146 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index 9720e71..9951ab5 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -65,14 +65,12 @@ static const char* MQTT_LOCAL_JSON_DECODER_FB_ID_PREFIX = "MQTTJSONDecoderFB"; static const char* MQTT_CLIENT_FB_CON_STATUS_TYPE = "BrokerConnectionStatusType"; static const char* MQTT_PUB_FB_SIG_STATUS_TYPE = "MQTTSignalStatusType"; static const char* MQTT_PUB_FB_PUB_STATUS_TYPE = "MQTTPublishingStatusType"; -static const char* MQTT_FB_PARSING_STATUS_TYPE = "MQTTParsingStatusType"; static const char* MQTT_PUB_FB_SET_STATUS_TYPE = "MQTTSettingStatusType"; static const char* MQTT_CLIENT_FB_CON_STATUS_NAME = "ConnectionStatus"; static const char* MQTT_PUB_FB_SIG_STATUS_NAME = "SignalStatus"; static const char* MQTT_PUB_FB_PUB_STATUS_NAME = "PublishingStatus"; -static const char* MQTT_FB_PARSING_STATUS_NAME = "ParsingStatus"; static const char* MQTT_PUB_FB_SET_STATUS_NAME = "SettingStatus"; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h index 492f099..b3f684d 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h @@ -16,7 +16,6 @@ #pragma once #include "MqttDataWrapper.h" -#include "mqtt_streaming_module/status_helper.h" #include #include @@ -48,23 +47,19 @@ class MqttJsonDecoderFbImpl final : public FunctionBlock std::string tsFieldName; std::string unitSymbol; }; - struct ConfigStatus { - bool configValid; - std::string configMsg; - bool waitingData; - bool parsingSucceeded; - std::string parsingMsg; - }; + static std::atomic localIndex; - static std::vector> parsingStatusMap; mqtt::MqttDataWrapper jsonDataWorker; SignalConfigPtr outputSignal; SignalConfigPtr outputDomainSignal; + std::atomic waitingData; + std::atomic configValid; + std::string configMsg; + std::atomic parsingSucceeded; + std::string parsingMsg; FbConfig config; - StatusHelper parsingStatus; - ConfigStatus configStatus; static std::string generateLocalId(); diff --git a/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp index 68b1f75..6842835 100644 --- a/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp @@ -7,24 +7,12 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE std::atomic MqttJsonDecoderFbImpl::localIndex = 0; -std::vector> MqttJsonDecoderFbImpl::parsingStatusMap = - {{ParsingStatus::InvalidParamaters, "InvalidParamaters"}, - {ParsingStatus::ParsingFailed, "ParsingFailed"}, - {ParsingStatus::WaitingForData, "WaitingForData"}, - {ParsingStatus::ParsingSuccedeed, "ParsingSuccedeed"}}; - MqttJsonDecoderFbImpl::MqttJsonDecoderFbImpl(const ContextPtr& ctx, const ComponentPtr& parent, const FunctionBlockTypePtr& type, const PropertyObjectPtr& config) : FunctionBlock(type, ctx, parent, generateLocalId()), - jsonDataWorker(loggerComponent), - parsingStatus(MQTT_FB_PARSING_STATUS_TYPE, - MQTT_FB_PARSING_STATUS_NAME, - statusContainer, - parsingStatusMap, - ParsingStatus::WaitingForData, - context.getTypeManager()) + jsonDataWorker(loggerComponent) { initComponentStatus(); if (config.assigned()) @@ -100,20 +88,20 @@ void MqttJsonDecoderFbImpl::initProperties(const PropertyObjectPtr& config) void MqttJsonDecoderFbImpl::readProperties() { auto lock = this->getRecursiveConfigLock(); - configStatus.configValid = true; - configStatus.configMsg.clear(); + configValid = true; + configMsg.clear(); config.valueFieldName = readProperty(PROPERTY_NAME_DEC_VALUE_NAME, ""); if (config.valueFieldName.empty()) { - configStatus.configMsg = fmt::format("\"{}\" property is empty!", PROPERTY_NAME_DEC_VALUE_NAME); - configStatus.configValid = false; + configMsg = fmt::format("\"{}\" property is empty!", PROPERTY_NAME_DEC_VALUE_NAME); + configValid = false; } config.tsFieldName = readProperty(PROPERTY_NAME_DEC_TS_NAME, ""); config.unitSymbol = readProperty(PROPERTY_NAME_DEC_UNIT, ""); jsonDataWorker.setValueFieldName(config.valueFieldName); jsonDataWorker.setTimestampFieldName(config.tsFieldName); - configStatus.waitingData = true; + waitingData = configValid.load(); updateStatuses(); } @@ -148,46 +136,42 @@ void MqttJsonDecoderFbImpl::propertyChanged() void MqttJsonDecoderFbImpl::updateStatuses() { - if (configStatus.configValid == false) + if (configValid == false) { - setComponentStatusWithMessage(ComponentStatus::Error, "Configuration is invalid!"); - parsingStatus.setStatus(ParsingStatus::InvalidParamaters, configStatus.configMsg); + setComponentStatusWithMessage(ComponentStatus::Error, "Configuration is invalid! " + configMsg); } - else if (configStatus.waitingData) + else if (waitingData) { - setComponentStatus(ComponentStatus::Ok); - parsingStatus.setStatus(ParsingStatus::WaitingForData); + setComponentStatusWithMessage(ComponentStatus::Ok, "Waiting for data"); } - else if (configStatus.parsingSucceeded) + else if (parsingSucceeded) { - setComponentStatus(ComponentStatus::Ok); - parsingStatus.setStatus(ParsingStatus::ParsingSuccedeed); + setComponentStatusWithMessage(ComponentStatus::Ok, "Parsing succeeded"); } else { - if (statusContainer.getStatus("ComponentStatus") == ComponentStatus::Ok) - { - setComponentStatus(ComponentStatus::Warning); - } - parsingStatus.setStatus(ParsingStatus::ParsingFailed, configStatus.parsingMsg); + setComponentStatusWithMessage(ComponentStatus::Error, "Parsing failed: " + parsingMsg); } } void MqttJsonDecoderFbImpl::processMessage(const std::string& json) { - auto lock = this->getRecursiveConfigLock(); - configStatus.waitingData = false; - auto status = jsonDataWorker.createAndSendDataPacket(json); - configStatus.parsingSucceeded = status.success; - if (status.success) - { - configStatus.parsingMsg.clear(); - } - else + if (configValid) { - configStatus.parsingMsg = status.msg; + auto lock = this->getRecursiveConfigLock(); + waitingData = false; + auto status = jsonDataWorker.createAndSendDataPacket(json); + parsingSucceeded = status.success; + if (status.success) + { + parsingMsg.clear(); + } + else + { + parsingMsg = status.msg; + } + updateStatuses(); } - updateStatuses(); } void MqttJsonDecoderFbImpl::createSignal() diff --git a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp index f01522e..9e56612 100644 --- a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp @@ -451,10 +451,7 @@ TEST_F(MqttJsonDecoderFbTest, Config) EXPECT_EQ(fb.getSignals().getCount(), 1u); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::WaitingForData), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(fb.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Waiting for data"), std::string::npos); const auto allProperties = fb.getAllProperties(); ASSERT_EQ(allProperties.getCount(), config.getAllProperties().getCount()); @@ -475,10 +472,7 @@ TEST_F(MqttJsonDecoderFbTest, CreationWithDefaultConfig) EXPECT_EQ(fb.getSignals().getCount(), 1u); EXPECT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::InvalidParamaters), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(fb.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Configuration is invalid"), std::string::npos); } TEST_F(MqttJsonDecoderFbTest, CreationWithPartialConfig) @@ -493,10 +487,7 @@ TEST_F(MqttJsonDecoderFbTest, CreationWithPartialConfig) EXPECT_EQ(fb.getSignals().getCount(), 1u); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::WaitingForData), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(fb.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Waiting for data"), std::string::npos); subMqttFb.removeFunctionBlock(fb); } { @@ -507,10 +498,7 @@ TEST_F(MqttJsonDecoderFbTest, CreationWithPartialConfig) EXPECT_EQ(fb.getSignals().getCount(), 1u); ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::InvalidParamaters), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(fb.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Configuration is invalid"), std::string::npos); subMqttFb.removeFunctionBlock(fb); } } @@ -523,10 +511,7 @@ TEST_P(MqttJsonFbDoubleDataPTest, DataTransferOneSignalDouble) ASSERT_TRUE(compareData(dataToSend, dataToReceive)); ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); - ASSERT_EQ(decoderObj.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } TEST_P(MqttJsonFbDoubleDataPTest, DataTransferOneSignalDoubleWithoutDomain) @@ -537,10 +522,7 @@ TEST_P(MqttJsonFbDoubleDataPTest, DataTransferOneSignalDoubleWithoutDomain) ASSERT_TRUE(compareData(dataToSend, dataToReceive)); ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); - ASSERT_EQ(decoderObj.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } INSTANTIATE_TEST_SUITE_P(DataTransferOneSignalDouble, @@ -555,10 +537,7 @@ TEST_P(MqttJsonFbIntDataPTest, DataTransferOneSignalInt) ASSERT_TRUE(compareData(dataToSend, dataToReceive)); ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); - ASSERT_EQ(decoderObj.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } TEST_P(MqttJsonFbIntDataPTest, DataTransferOneSignalIntWithoutDomain) @@ -569,10 +548,7 @@ TEST_P(MqttJsonFbIntDataPTest, DataTransferOneSignalIntWithoutDomain) ASSERT_TRUE(compareData(dataToSend, dataToReceive)); ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); - ASSERT_EQ(decoderObj.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } INSTANTIATE_TEST_SUITE_P(DataTransferOneSignalInt, @@ -587,10 +563,7 @@ TEST_P(MqttJsonFbStringDataPTest, DataTransferOneSignalString) ASSERT_TRUE(compareData(dataToSend, dataToReceive)); ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); - ASSERT_EQ(decoderObj.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } TEST_P(MqttJsonFbStringDataPTest, DataTransferOneSignalStringWithoutDomain) @@ -601,10 +574,7 @@ TEST_P(MqttJsonFbStringDataPTest, DataTransferOneSignalStringWithoutDomain) ASSERT_TRUE(compareData(dataToSend, dataToReceive)); ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); - ASSERT_EQ(decoderObj.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } INSTANTIATE_TEST_SUITE_P(DataTransferOneSignalString, @@ -619,10 +589,7 @@ TEST_P(MqttJsonFbStringTsPTest, DataTransferOneSignalIntDomainString) ASSERT_TRUE(compareData(dataToSend, dataToReceive)); ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); - ASSERT_EQ(decoderObj.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } INSTANTIATE_TEST_SUITE_P(DataTransferOneSignalInt, @@ -677,28 +644,19 @@ TEST_F(MqttJsonDecoderFbTest, DataTransferSeveralSignals) EXPECT_TRUE(compareData(DATA_DOUBLE_INT_0, dataToReceive[0])); ASSERT_EQ(decoderFb0.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(decoderFb0.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(decoderFb0.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); EXPECT_EQ(DATA_DOUBLE_INT_1.size(), dataToReceive[1].size()); EXPECT_TRUE(compareData(DATA_DOUBLE_INT_1, dataToReceive[1])); ASSERT_EQ(decoderFb1.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(decoderFb1.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(decoderFb1.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); EXPECT_EQ(DATA_DOUBLE_INT_2.size(), dataToReceive[2].size()); EXPECT_TRUE(compareData(DATA_DOUBLE_INT_2, dataToReceive[2], false)); ASSERT_EQ(decoderFb2.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(decoderFb2.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(decoderFb2.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } @@ -721,11 +679,8 @@ TEST_F(MqttJsonDecoderFbTest, DataTransferMissingFieldOneSignal) std::vector> dataToReceive = read>(reader, signal, 0); ASSERT_EQ(dataToReceive.size(), 0); ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Warning", decoderObj.getContext().getTypeManager())); - ASSERT_EQ(decoderObj.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingFailed), - decoderObj.getContext().getTypeManager())); + Enumeration("ComponentStatusType", "Error", decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing failed"), std::string::npos); } TEST_F(MqttJsonDecoderFbTest, DataTransferMissingFieldSeveralSignals) @@ -775,27 +730,18 @@ TEST_F(MqttJsonDecoderFbTest, DataTransferMissingFieldSeveralSignals) EXPECT_TRUE(compareData(DATA_DOUBLE_INT_0, dataToReceive[0])); ASSERT_EQ(decoderFb0.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(decoderFb0.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(decoderFb0.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); EXPECT_EQ(0u, dataToReceive[1].size()); ASSERT_EQ(decoderFb1.getStatusContainer().getStatus("ComponentStatus"), - Enumeration("ComponentStatusType", "Warning", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(decoderFb1.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingFailed), - daqInstance.getContext().getTypeManager())); + Enumeration("ComponentStatusType", "Error", daqInstance.getContext().getTypeManager())); + EXPECT_NE(decoderFb1.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing failed"), std::string::npos); EXPECT_EQ(DATA_DOUBLE_INT_2.size(), dataToReceive[2].size()); EXPECT_TRUE(compareData(DATA_DOUBLE_INT_2, dataToReceive[2], false)); ASSERT_EQ(decoderFb2.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(decoderFb2.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(decoderFb2.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } TEST_F(MqttJsonFbCommunicationTest, FullDataTransfer) @@ -816,10 +762,7 @@ TEST_F(MqttJsonFbCommunicationTest, FullDataTransfer) ASSERT_TRUE(compareData(DATA_DOUBLE_INT_0, result.dataReceived)); ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(decoderObj.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } TEST_F(MqttJsonFbCommunicationTest, FullDataTransferFor2MqttFbs) @@ -848,10 +791,7 @@ TEST_F(MqttJsonFbCommunicationTest, FullDataTransferFor2MqttFbs) EXPECT_TRUE(compareData(DATA_DOUBLE_INT_0, result0.dataReceived)); ASSERT_EQ(decoderFb0.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(decoderFb0.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(decoderFb0.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); ASSERT_FALSE(result1.mqttFbProblem); ASSERT_FALSE(result1.publishingProblem); @@ -859,10 +799,7 @@ TEST_F(MqttJsonFbCommunicationTest, FullDataTransferFor2MqttFbs) EXPECT_TRUE(compareData(DATA_DOUBLE_INT_1, result1.dataReceived)); ASSERT_EQ(decoderFb1.getStatusContainer().getStatus("ComponentStatus"), Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); - ASSERT_EQ(decoderFb1.getStatusContainer().getStatus(MQTT_FB_PARSING_STATUS_NAME), - EnumerationWithIntValue(MQTT_FB_PARSING_STATUS_TYPE, - static_cast(MqttJsonDecoderFbImpl::ParsingStatus::ParsingSuccedeed), - daqInstance.getContext().getTypeManager())); + EXPECT_NE(decoderFb1.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } TEST_F(MqttJsonDecoderFbTest, RemovingNestedFunctionBlock) diff --git a/mqtt_streaming_protocol/src/MqttDataWrapper.cpp b/mqtt_streaming_protocol/src/MqttDataWrapper.cpp index 7e940c2..6f5248b 100644 --- a/mqtt_streaming_protocol/src/MqttDataWrapper.cpp +++ b/mqtt_streaming_protocol/src/MqttDataWrapper.cpp @@ -236,7 +236,7 @@ std::pair> MqttDataWrapper: else { result.success = false; - result.msg = fmt::format("Unsupported value type for '{}'.", name); + result.msg += fmt::format("Unsupported value type for '{}'. ", name); hasValue = false; } } @@ -256,7 +256,7 @@ std::pair> MqttDataWrapper: else { result.success = false; - result.msg = "Timestamp value type is not supported."; + result.msg += "Timestamp value type is not supported. "; } } else @@ -269,12 +269,16 @@ std::pair> MqttDataWrapper: catch (...) { result.success = false; - result.msg = "Error deserializing MQTT payload"; + result.msg += "Error deserializing MQTT payload. "; } if (!hasValue || (!msgDescriptor.tsFieldName.empty() && !hasTS)) { result.success = false; - result.msg = "Not all required fields are present in the JSON message."; + result.msg += "Not all required fields are present in the JSON message. "; + if (!hasValue) + result.msg += fmt::format("Couldn't extract value field (\"{}\") from the JSON message. ", msgDescriptor.valueFieldName); + if (!msgDescriptor.tsFieldName.empty() && !hasTS) + result.msg += fmt::format("Couldn't extract timestamp field (\"{}\") from the JSON message. ", msgDescriptor.tsFieldName); } if (result.success) From 8aba45163c21a0eed96cf71139a86121e11b24ee Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Mon, 26 Jan 2026 10:43:17 +0100 Subject: [PATCH 27/49] mqtt: publisher FB deadlock fix --- .../mqtt_streaming_module/mqtt_publisher_fb_impl.h | 2 +- mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h index 0685d31..42c7f4a 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h @@ -86,7 +86,7 @@ class MqttPublisherFbImpl final : public FunctionBlock std::vector signalErrors; std::vector settingErrors; std::unique_ptr handler; - std::mutex statusMutex; + std::mutex processingMutex; StatusHelper signalStatus; StatusHelper publishingStatus; StatusHelper settingStatus; diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index ba30a9a..adbf641 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -74,6 +74,7 @@ void MqttPublisherFbImpl::removed() } { auto lock = this->getRecursiveConfigLock(); + auto lockProcessing = std::scoped_lock(processingMutex); handler = nullptr; } FunctionBlock::removed(); @@ -164,6 +165,7 @@ PublisherFbConfig MqttPublisherFbImpl::getFbConfig() const void MqttPublisherFbImpl::onConnected(const InputPortPtr& inputPort) { auto lock = this->getRecursiveConfigLock(); + auto lockProcessing = std::scoped_lock(processingMutex); updatePortsAndSignals(true); LOG_T("Connected to port {}", inputPort.getLocalId()); @@ -175,6 +177,7 @@ void MqttPublisherFbImpl::onConnected(const InputPortPtr& inputPort) void MqttPublisherFbImpl::onDisconnected(const InputPortPtr& inputPort) { auto lock = this->getRecursiveConfigLock(); + auto lockProcessing = std::scoped_lock(processingMutex); updatePortsAndSignals(true); LOG_T("Disconnected from port {}", inputPort.getLocalId()); @@ -186,6 +189,8 @@ void MqttPublisherFbImpl::onDisconnected(const InputPortPtr& inputPort) void MqttPublisherFbImpl::propertyChanged() { auto lock = this->getRecursiveConfigLock(); + auto lockProcessing = std::scoped_lock(processingMutex); + readProperties(); handler = HandlerFactory::create(this->template getWeakRefInternal(), this->config, globalId.toStdString()); updatePortsAndSignals(false); @@ -393,7 +398,6 @@ void MqttPublisherFbImpl::initProperties(const PropertyObjectPtr& config) void MqttPublisherFbImpl::readProperties() { - auto lock = this->getRecursiveConfigLock(); int tmpTopicMode = readProperty(PROPERTY_NAME_PUB_TOPIC_MODE, 0); config.groupValues = readProperty(PROPERTY_NAME_PUB_GROUP_VALUES, false); @@ -486,7 +490,7 @@ void MqttPublisherFbImpl::readerLoop() { { MqttData msgs; - auto lock = this->getRecursiveConfigLock(); + auto lockProcessing = std::scoped_lock(processingMutex); if (hasSignalError == false && hasSettingError == false) { msgs = handler->processSignalContexts(signalContexts); From 6e413eac7f2848d9ef1e70c491e7d37218721f75 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Mon, 26 Jan 2026 19:34:19 +0100 Subject: [PATCH 28/49] mqtt: array parsing for decoder FB --- .../tests/test_mqtt_json_decoder_fb.cpp | 605 ++++++++++++------ .../include/MqttDataWrapper.h | 46 +- .../src/MqttDataWrapper.cpp | 348 ++++++++-- 3 files changed, 718 insertions(+), 281 deletions(-) diff --git a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp index 9e56612..ca8d3e2 100644 --- a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp @@ -16,16 +16,110 @@ using namespace daq; using namespace daq::modules::mqtt_streaming_module; +namespace +{ template -struct is_pair : std::false_type {}; +struct is_pair : std::false_type +{ +}; template -struct is_pair> : std::true_type {}; +struct is_pair> : std::true_type +{ +}; bool almostEqual(double a, double b, double relEpsilon = 1e-9, double absEpsilon = 1e-12) { return std::fabs(a - b) <= std::max(absEpsilon, relEpsilon * std::max(std::fabs(a), std::fabs(b))); } +template +bool almostEqual(const std::vector& a, const std::vector& b, double relEpsilon = 1e-9, double absEpsilon = 1e-12) +{ + if (a.size() != b.size()) + return false; + for (size_t i = 0; i < a.size(); ++i) + { + if (!almostEqual(a[i], b[i], relEpsilon, absEpsilon)) + return false; + } + return true; +} + +template +bool equal(const std::vector& a, const std::vector& b) +{ + if (a.size() != b.size()) + return false; + if constexpr (std::is_same_v || std::is_same_v) + { + return almostEqual(a, b); + } + else + { + return a == b; + } +} + +template +bool equal(T a, T b) +{ + if constexpr (std::is_same_v || std::is_same_v) + { + return almostEqual(a, b); + } + else + { + return a == b; + } +} + +template +bool equalTs(T a, T b) +{ + const auto convertedA = mqtt::utils::numericToMicroseconds(a); + const auto convertedB = mqtt::utils::numericToMicroseconds(b); + return (convertedA == convertedB && convertedA != 0); +} + +template +bool equalTs(const std::vector& a, const std::vector& b) +{ + if (a.size() != b.size()) + return false; + for (size_t i = 0; i < a.size(); ++i) + { + if (!equalTs(a[i], b[i])) + return false; + } + return true; +} + +bool equalTs(const std::string& a, uint64_t b) +{ + return (mqtt::utils::toUnixTicks(a) == b && b != 0); +} + +bool equalTs(uint64_t b, const std::string& a) +{ + return equalTs(a, b); +} + +bool equalTs(const std::vector& a, const std::vector& b) +{ + if (a.size() != b.size()) + return false; + for (size_t i = 0; i < a.size(); ++i) + { + if (!equalTs(a[i], b[i])) + return false; + } + return true; +} + +bool equalTs(const std::vector& b, const std::vector& a) +{ + return equalTs(a, b); +} std::string doubleToString(double value, int precision = 12) { @@ -34,6 +128,225 @@ std::string doubleToString(double value, int precision = 12) return out.str(); } +template +void merge(const std::vector>& input, std::vector, std::vector>>& output) +{ + std::vector data; + std::vector ts; + for (const auto& [sData, sTs] : input) + { + data.push_back(sData); + ts.push_back(sTs); + } + output.emplace_back(std::pair(std::move(data), std::move(ts))); +} + +template +std::string replacePlaceholder(const std::string& jsonTemplate, const std::string& ph, const T& value) +{ + std::string result = jsonTemplate; + size_t pos = result.find(ph); + if (pos != std::string::npos) + { + std::string replacement; + if constexpr (std::is_same_v) + { + replacement = '"' + value + '"'; + } + else if constexpr (std::is_same_v || std::is_same_v) + { + replacement = doubleToString(value); + } + else if constexpr (mqtt::is_std_vector_v) + { + replacement = "["; + for (size_t i = 0; i < value.size(); ++i) + { + if (i > 0) + replacement += ", "; + if constexpr (std::is_same_v, double> || std::is_same_v, float>) + replacement += doubleToString(value[i]); + else if constexpr (std::is_same_v, std::string>) + replacement += '"' + value[i] + '"'; + else + replacement += std::to_string(value[i]); + } + replacement += "]"; + } + else + { + replacement = std::to_string(value); + } + result.replace(pos, ph.length(), replacement); + } + return result; +} + +template +std::vector replacePlaceholders(const std::vector>& data, const std::string& jsonTemplate) +{ + std::vector result; + for (const auto& [value, ts] : data) + { + auto str = replacePlaceholder(jsonTemplate, "", value); + str = replacePlaceholder(str, "", ts); + result.push_back(str); + } + return result; +} + +std::string extractFieldName(std::string jsonTemplate, const std::string& valuePh) +{ + std::string result; + size_t pos = jsonTemplate.find(valuePh); + if (pos == std::string::npos) + return ""; + size_t posEnd = jsonTemplate.rfind("\"", pos); + if (posEnd == std::string::npos) + return ""; + size_t posStart = jsonTemplate.rfind("\"", posEnd - 1); + if (posStart == std::string::npos) + return ""; + ++posStart; + result = jsonTemplate.substr(posStart, posEnd - posStart); + return result; +} + +template +bool compareData(const std::vector>& data0, const std::vector>& data1, bool compareTs = true) +{ + if (data0.size() != data1.size()) + return false; + for (std::size_t i = 0; i < data0.size(); ++i) + { + const auto& [value0, ts0] = data0[i]; + const auto& [value1, ts1] = data1[i]; + if (!equal(value0, value1)) + return false; + if (compareTs) + { + if (!equalTs(ts0, ts1)) + return false; + } + } + return true; +} + +template +bool compareData(const std::vector>& data0, const std::vector& data1) +{ + if (data0.size() != data1.size()) + return false; + for (std::size_t i = 0; i < data0.size(); ++i) + { + const auto& [value0, _] = data0[i]; + const auto& value1 = data1[i]; + + if (!equal(value0, value1)) + return false; + } + return true; +} + +template +bool copyData(T& destination, const DataPacketPtr source) +{ + auto checkType = [](SampleType type) -> bool + { + switch (type) + { + case SampleType::Float32: + case SampleType::Float64: + case SampleType::UInt8: + case SampleType::Int8: + case SampleType::UInt16: + case SampleType::Int16: + case SampleType::UInt32: + case SampleType::Int32: + case SampleType::UInt64: + case SampleType::Int64: + case SampleType::RangeInt64: + case SampleType::ComplexFloat32: + case SampleType::ComplexFloat64: + return true; + case SampleType::String: + case SampleType::Binary: + case SampleType::Struct: + case SampleType::Invalid: + case SampleType::Null: + case SampleType::_count: + return false; + } + return true; + }; + + const auto dataType = source.getDataDescriptor().getSampleType(); + if (checkType(dataType) && getSampleSize(dataType) != sizeof(mqtt::sample_type_t)) + return false; + if constexpr (std::is_same_v) + { + destination = std::string(static_cast(source.getData()), source.getDataSize()); + } + else if constexpr (mqtt::is_std_vector_v) + { + destination.resize(source.getSampleCount()); + memcpy(destination.data(), source.getRawData(), source.getSampleCount() * getSampleSize(dataType)); + } + else + { + memcpy(&destination, source.getData(), sizeof(destination)); + } + return true; +} + +template +std::vector read(PacketReaderPtr reader, const SignalPtr signal, int timeoutMs = 1000) +{ + std::vector result; + + auto timer = helper::utils::Timer(timeoutMs); + while (!reader.getEmpty() || !timer.expired()) + { + if (reader.getEmpty()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + continue; + } + auto packet = reader.read(); + if (packet.getType() == PacketType::Event) + { + continue; + } + + if (packet.getType() == PacketType::Data) + { + const auto dataPacket = packet.asPtr(); + if constexpr (is_pair::value) + { + T dataToReceiveEntry; + bool ok = true; + ok &= copyData(dataToReceiveEntry.first, dataPacket); + if (signal.getDomainSignal().assigned()) + ok &= copyData(dataToReceiveEntry.second, dataPacket.getDomainPacket()); + if (!ok) + break; + result.push_back(dataToReceiveEntry); + } + else + { + T dataToReceiveEntry; + bool ok = copyData(dataToReceiveEntry, dataPacket); + if (!ok) + break; + result.push_back(dataToReceiveEntry); + } + } + } + + return result; +} +} // namespace + namespace daq::modules::mqtt_streaming_module { class MqttJsonDecoderFbHelper : public DaqTestHelper @@ -90,59 +403,6 @@ class MqttJsonDecoderFbHelper : public DaqTestHelper return std::string(::testing::UnitTest::GetInstance()->current_test_info()->name()) + "_ClientId"; } - template std::string replacePlaceholder(const std::string& jsonTemplate, const std::string& ph, const T& value) - { - std::string result = jsonTemplate; - size_t pos = result.find(ph); - if (pos != std::string::npos) - { - std::string replacement; - if constexpr (std::is_same_v) - { - replacement = '"' + value + '"'; - } - else if constexpr (std::is_same_v || std::is_same_v) - { - replacement = doubleToString(value); - } - else - { - replacement = std::to_string(value); - } - result.replace(pos, ph.length(), replacement); - } - return result; - } - - template std::vector replacePlaceholders(const std::vector>& data, const std::string& jsonTemplate) - { - std::vector result; - for (const auto& [value, ts] : data) - { - auto str = replacePlaceholder(jsonTemplate, "", value); - str = replacePlaceholder(str, "", ts); - result.push_back(str); - } - return result; - } - - std::string extractFieldName(std::string jsonTemplate, const std::string& valuePh) - { - std::string result; - size_t pos = jsonTemplate.find(valuePh); - if (pos == std::string::npos) - return ""; - size_t posEnd = jsonTemplate.rfind("\"", pos); - if (posEnd == std::string::npos) - return ""; - size_t posStart = jsonTemplate.rfind("\"", posEnd - 1); - if (posStart == std::string::npos) - return ""; - ++posStart; - result = jsonTemplate.substr(posStart, posEnd - posStart); - return result; - } - template std::vector> transferData(const std::vector>& data, const std::string& jsonDataTemplate) { @@ -150,166 +410,16 @@ class MqttJsonDecoderFbHelper : public DaqTestHelper } template - std::vector transferDataWithoutDomain(const std::vector>& data, const std::string& jsonDataTemplate) + std::vector, std::vector>> + transferData(const std::vector, std::vector>>& data, const std::string& jsonDataTemplate) { - return transferData(data, jsonDataTemplate); + return transferData, std::vector, std::pair, std::vector>>(data, jsonDataTemplate); } template - bool compareData(const std::vector>& data0, const std::vector>& data1, bool compareTs = true) - { - if (data0.size() != data1.size()) - return false; - for (std::size_t i = 0; i < data0.size(); ++i) - { - const auto& [value0, ts0] = data0[i]; - const auto& [value1, ts1] = data1[i]; - if constexpr (std::is_same_v) - { - if (!almostEqual(static_cast(value0), static_cast(value1))) - return false; - } - else - { - if (value0 != value1) - return false; - } - if (compareTs) - { - if constexpr (std::is_same_v) - { - if (mqtt::utils::numericToMicroseconds(ts0) != ts1 && ts1 != 0) - return false; - } - else if constexpr (std::is_same_v) - { - if (mqtt::utils::toUnixTicks(ts0) != ts1 && ts1 != 0) - return false; - } - else - { - return false; - } - } - } - return true; - } - - template - bool compareData(const std::vector>& data0, const std::vector& data1) - { - if (data0.size() != data1.size()) - return false; - for (std::size_t i = 0; i < data0.size(); ++i) - { - const auto& [value0, ts0] = data0[i]; - const auto& value1 = data1[i]; - if constexpr (std::is_same_v) - { - if (!almostEqual(static_cast(value0), static_cast(value1))) - return false; - } - else - { - if (value0 != value1) - return false; - } - } - return true; - } - - template - bool copyData(T& destination, const DataPacketPtr source) - { - auto checkType = [](SampleType type) -> bool - { - switch (type) - { - case SampleType::Float32: - case SampleType::Float64: - case SampleType::UInt8: - case SampleType::Int8: - case SampleType::UInt16: - case SampleType::Int16: - case SampleType::UInt32: - case SampleType::Int32: - case SampleType::UInt64: - case SampleType::Int64: - case SampleType::RangeInt64: - case SampleType::ComplexFloat32: - case SampleType::ComplexFloat64: - return true; - case SampleType::String: - case SampleType::Binary: - case SampleType::Struct: - case SampleType::Invalid: - case SampleType::Null: - case SampleType::_count: - return false; - } - return true; - }; - - const auto dataType = source.getDataDescriptor().getSampleType(); - if (checkType(dataType) && getSampleSize(dataType) != sizeof(destination)) - return false; - if constexpr (std::is_same_v) - { - destination = std::string(static_cast(source.getData()), source.getDataSize()); - } - else - { - memcpy(&destination, source.getData(), sizeof(destination)); - } - return true; - } - - template - std::vector read(PacketReaderPtr reader, const SignalPtr signal, int timeoutMs = 1000) + std::vector transferDataWithoutDomain(const std::vector>& data, const std::string& jsonDataTemplate) { - std::vector result; - - auto timer = helper::utils::Timer(timeoutMs); - while (!reader.getEmpty() || !timer.expired()) - { - if (reader.getEmpty()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(20)); - continue; - } - auto packet = reader.read(); - if (packet.getType() == PacketType::Event) - { - continue; - } - - if (packet.getType() == PacketType::Data) - { - const auto dataPacket = packet.asPtr(); - if constexpr (is_pair::value) - { - T dataToReceiveEntry; - bool ok = true; - ok &= copyData(dataToReceiveEntry.first, dataPacket); - if (signal.getDomainSignal().assigned()) - ok &= copyData(dataToReceiveEntry.second, dataPacket.getDomainPacket()); - if (!ok) - break; - result.push_back(dataToReceiveEntry); - - } - else - { - T dataToReceiveEntry; - bool ok = copyData(dataToReceiveEntry, dataPacket); - if (!ok) - break; - result.push_back(dataToReceiveEntry); - } - } - } - - return result; + return transferData(data, jsonDataTemplate); } private: @@ -529,6 +639,81 @@ INSTANTIATE_TEST_SUITE_P(DataTransferOneSignalDouble, MqttJsonFbDoubleDataPTest, ::testing::Values(DATA_DOUBLE_INT_0, DATA_DOUBLE_INT_1, DATA_DOUBLE_INT_2)); +TEST_F(MqttJsonDecoderFbTest, DataTransferOneSignalDoubleArray) +{ + std::vector, std::vector>> dataToSend; + merge(DATA_DOUBLE_INT_0, dataToSend); + merge(DATA_DOUBLE_INT_1, dataToSend); + merge(DATA_DOUBLE_INT_2, dataToSend); + + auto dataToReceive = transferData(dataToSend, VALID_JSON_DATA_0); + ASSERT_EQ(dataToSend.size(), dataToReceive.size()); + ASSERT_TRUE(compareData(dataToSend, dataToReceive)); + ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); +} + +TEST_F(MqttJsonDecoderFbTest, DataTransferOneSignalDoubleArrayWithoutDomain) +{ + std::vector, std::vector>> dataToSend; + merge(DATA_DOUBLE_INT_0, dataToSend); + merge(DATA_DOUBLE_INT_1, dataToSend); + merge(DATA_DOUBLE_INT_2, dataToSend); + + auto dataToReceive = transferDataWithoutDomain(dataToSend, VALID_JSON_DATA_1); + ASSERT_EQ(dataToSend.size(), dataToReceive.size()); + ASSERT_TRUE(compareData(dataToSend, dataToReceive)); + ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); +} + +TEST_F(MqttJsonDecoderFbTest, DataTransferOneSignalIntArray) +{ + std::vector, std::vector>> dataToSend; + merge(DATA_INT_INT_0, dataToSend); + merge(DATA_INT_INT_1, dataToSend); + merge(DATA_INT_INT_2, dataToSend); + + auto dataToReceive = transferData(dataToSend, VALID_JSON_DATA_0); + ASSERT_EQ(dataToSend.size(), dataToReceive.size()); + ASSERT_TRUE(compareData(dataToSend, dataToReceive)); + ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); +} + +TEST_F(MqttJsonDecoderFbTest, DataTransferOneSignalIntArrayWithoutDomain) +{ + std::vector, std::vector>> dataToSend; + merge(DATA_INT_INT_0, dataToSend); + merge(DATA_INT_INT_1, dataToSend); + merge(DATA_INT_INT_2, dataToSend); + + auto dataToReceive = transferDataWithoutDomain(dataToSend, VALID_JSON_DATA_1); + ASSERT_EQ(dataToSend.size(), dataToReceive.size()); + ASSERT_TRUE(compareData(dataToSend, dataToReceive)); + ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); +} + +TEST_F(MqttJsonDecoderFbTest, DataTransferOneSignalDoubleArrayDomainString) +{ + std::vector, std::vector>> dataToSend; + merge(DATA_DOUBLE_STR_0, dataToSend); + merge(DATA_DOUBLE_STR_1, dataToSend); + merge(DATA_DOUBLE_STR_2, dataToSend); + + auto dataToReceive = transferData<>(dataToSend, VALID_JSON_DATA_0); + ASSERT_EQ(dataToSend.size(), dataToReceive.size()); + ASSERT_TRUE(compareData(dataToSend, dataToReceive)); + ASSERT_EQ(decoderObj.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", decoderObj.getContext().getTypeManager())); + EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); +} + TEST_P(MqttJsonFbIntDataPTest, DataTransferOneSignalInt) { const auto dataToSend = GetParam(); diff --git a/mqtt_streaming_protocol/include/MqttDataWrapper.h b/mqtt_streaming_protocol/include/MqttDataWrapper.h index d39239c..54aaef7 100644 --- a/mqtt_streaming_protocol/include/MqttDataWrapper.h +++ b/mqtt_streaming_protocol/include/MqttDataWrapper.h @@ -14,6 +14,26 @@ namespace mqtt { +template +struct container_traits +{ + static constexpr bool is_vector = false; + using value_type = T; +}; + +template +struct container_traits> +{ + static constexpr bool is_vector = true; + using value_type = U; +}; + +template +inline constexpr bool is_std_vector_v = container_traits::is_vector; + +template +using sample_type_t = typename container_traits::value_type; + struct SampleData { double value; @@ -62,6 +82,19 @@ class MqttDataWrapper final msg(msg) { } + + CmdResult addError(const std::string& newmsg) + { + success = false; + msg += newmsg; + return *this; + } + CmdResult merge(const CmdResult& other) + { + success = success && other.success; + msg += other.msg; + return *this; + } }; MqttDataWrapper(daq::LoggerComponentPtr loggerComponent); @@ -90,18 +123,21 @@ class MqttDataWrapper final std::pair> extractDataSamples(const MqttMsgDescriptor& msgDescriptor, const std::string& json); void sendDataSamples(const DataPackets& dataPackets); template - DataPackets buildDataPackets(T value, uint64_t timestamp); + DataPackets buildDataPackets(const T& value, uint64_t timestamp); + template + DataPackets buildDataPackets(const T& value, const std::vector& timestamp); template - DataPackets buildDataPackets(T value); + DataPackets buildDataPackets(const T& value); daq::DataPacketPtr buildDomainDataPacket(daq::GenericSignalConfigPtr<> signalConfig, uint64_t timestamp); + daq::DataPacketPtr buildDomainDataPacket(daq::GenericSignalConfigPtr<> signalConfig, const std::vector& timestamp); template daq::DataPacketPtr buildDataPacket(daq::GenericSignalConfigPtr<> signalConfig, - T value, + const T& value, const daq::DataPacketPtr domainPacket); template daq::DataPacketPtr createEmptyDataPacket(const daq::GenericSignalConfigPtr<> signalConfig, - const daq::DataPacketPtr domainPacket, T value); - template void copyDataIntoPacket(daq::DataPacketPtr dataPacket, T value); + const daq::DataPacketPtr domainPacket, const T& value); + template void copyDataIntoPacket(daq::DataPacketPtr dataPacket, const T& value); daq::UnitPtr extractSignalUnit(const rapidjson::Value& signalObj); std::string extractValueFieldName(const rapidjson::Value& signalObj); std::string extractTimestampFieldName(const rapidjson::Value& signalObj); diff --git a/mqtt_streaming_protocol/src/MqttDataWrapper.cpp b/mqtt_streaming_protocol/src/MqttDataWrapper.cpp index 6f5248b..6bfc917 100644 --- a/mqtt_streaming_protocol/src/MqttDataWrapper.cpp +++ b/mqtt_streaming_protocol/src/MqttDataWrapper.cpp @@ -1,6 +1,5 @@ #include "MqttDataWrapper.h" -#include "rapidjson/writer.h" #include #include #include @@ -14,10 +13,101 @@ #include #include -namespace mqtt +namespace{ + +template +std::vector parseHomogeneousArrayInternal( + const rapidjson::Value::ConstArray& arr, + IsFn isValid, + GetFn getValue) +{ + std::vector out; + out.reserve(arr.Size()); + + for (const auto& x : arr) + { + if (!isValid(x)) + return std::vector{}; + + out.push_back(getValue(x)); + } + return out; +} + +template +std::pair> parseHomogeneousArray( + const rapidjson::Value::ConstArray& arr) +{ + return std::pair{mqtt::MqttDataWrapper::CmdResult{false, {}}, std::vector{}}; +} + +template <> +std::pair> parseHomogeneousArray( + const rapidjson::Value::ConstArray& arr) { + std::pair> result{{true, {}}, {}}; + result.second = parseHomogeneousArrayInternal( + arr, + [](const auto& x) { return x.IsInt64() || x.IsUint64(); }, + [](const auto& x) { return x.GetInt64(); }); + if (result.second.empty()) + { + result.first.addError("Mixed types in value array (expected integers). "); + } + return result; +} -static const char* TOPIC_ALL_SIGNALS_PREFIX = "openDAQ"; +template <> +std::pair> parseHomogeneousArray( + const rapidjson::Value::ConstArray& arr) +{ + std::pair> result{{true, {}}, {}}; + result.second = parseHomogeneousArrayInternal( + arr, + [](const auto& x) { return x.IsUint64(); }, + [](const auto& x) { return x.GetUint64(); }); + if (result.second.empty()) + { + result.first.addError("Mixed types in value array (expected unsigned integers). "); + } + return result; +} + +template <> +std::pair> parseHomogeneousArray( + const rapidjson::Value::ConstArray& arr) +{ + std::pair> result{{true, {}}, {}}; + result.second = parseHomogeneousArrayInternal( + arr, + [](const auto& x) { return x.IsDouble(); }, + [](const auto& x) { return x.GetDouble(); }); + if (result.second.empty()) + { + result.first.addError("Mixed types in value array (expected doubles). "); + } + return result; +} + +template <> +std::pair> parseHomogeneousArray( + const rapidjson::Value::ConstArray& arr) +{ + std::pair> result{{true, {}}, {}}; + result.second = parseHomogeneousArrayInternal( + arr, + [](const auto& x) { return x.IsString(); }, + [](const auto& x) { return std::string{x.GetString()}; }); + if (result.second.empty()) + { + result.first.addError("Mixed types in value array (expected strings). "); + } + return result; +} +} + +namespace mqtt +{ MqttDataWrapper::MqttDataWrapper(daq::LoggerComponentPtr loggerComponent) : loggerComponent(loggerComponent) @@ -183,11 +273,6 @@ MqttDataWrapper::CmdResult MqttDataWrapper::createAndSendDataPacket(const std::s return status; } -// bool MqttDataWrapper::hasDomainSignal(const SignalId& signalId) const -// { -// return (msgDescriptor.signalName == signalId.signalName) ? !msgDescriptor.tsFieldName.empty() : false; -// } - void MqttDataWrapper::setValueFieldName(std::string valueFieldName) { msgDescriptor.valueFieldName = std::move(valueFieldName); @@ -198,22 +283,24 @@ void MqttDataWrapper::setTimestampFieldName(std::string tsFieldName) msgDescriptor.tsFieldName = std::move(tsFieldName); } -std::pair> MqttDataWrapper::extractDataSamples(const MqttMsgDescriptor& msgDescriptor, const std::string& json) +std::pair> MqttDataWrapper::extractDataSamples(const MqttMsgDescriptor& msgDescriptor, + const std::string& json) { - using ValueVariant = std::variant; + using ValueVariant = std::variant, std::vector, std::vector>; ValueVariant value{}; std::vector outputData; - uint64_t ts = 0; + std::vector ts; bool hasTS = false; bool hasValue = false; CmdResult result(true); + try { rapidjson::Document jsonDocument; jsonDocument.Parse(json); if (jsonDocument.HasParseError()) { - return std::pair{CmdResult(false, "Error parsing mqtt payload as JSON"), outputData}; + return std::pair{result.addError("Error parsing mqtt payload as JSON"), outputData}; } if (jsonDocument.IsObject()) @@ -224,39 +311,115 @@ std::pair> MqttDataWrapper: if (!msgDescriptor.valueFieldName.empty() && name == msgDescriptor.valueFieldName) { const auto& v = jsonDocument[name]; - hasValue = true; - if (v.IsInt64()) - value = v.GetInt64(); - else if (v.IsUint64()) - value = static_cast(v.GetUint64()); - else if (v.IsDouble()) - value = v.GetDouble(); - else if (v.IsString()) - value = std::string(v.GetString()); + if (v.IsArray()) + { + const auto& arr = v.GetArray(); + if (arr.Empty()) + { + result.addError("Value field is an array but it is empty. "); + } + else if (arr[0].IsInt64() || arr[0].IsUint64()) + { + auto [parsingStatus, out] = parseHomogeneousArray(arr); + result.merge(parsingStatus); + hasValue = parsingStatus.success; + if (parsingStatus.success) + value = std::move(out); + } + else if (arr[0].IsDouble()) + { + auto [parsingStatus, out] = parseHomogeneousArray(arr); + result.merge(parsingStatus); + hasValue = parsingStatus.success; + if (parsingStatus.success) + value = std::move(out); + } + else if (arr[0].IsString()) + { + auto [parsingStatus, out] = parseHomogeneousArray(arr); + result.merge(parsingStatus); + hasValue = parsingStatus.success; + if (parsingStatus.success) + value = std::move(out); + } + else + { + result.addError(fmt::format("Unsupported value type for '{}' array. ", name)); + } + } else { - result.success = false; - result.msg += fmt::format("Unsupported value type for '{}'. ", name); - hasValue = false; + hasValue = true; + if (v.IsInt64()) + value = std::vector{v.GetInt64()}; + else if (v.IsUint64()) + value = std::vector{static_cast(v.GetUint64())}; + else if (v.IsDouble()) + value = std::vector{v.GetDouble()}; + else if (v.IsString()) + value = std::vector{std::string(v.GetString())}; + else + { + result.addError(fmt::format("Unsupported value type for '{}'. ", name)); + hasValue = false; + } } } else if (!msgDescriptor.tsFieldName.empty() && name == msgDescriptor.tsFieldName) { - if (jsonDocument[name].IsInt() || jsonDocument[name].IsUint64() || - jsonDocument[name].IsInt64()) + const auto& tsField = jsonDocument[name]; + if (tsField.IsArray()) { - ts = mqtt::utils::numericToMicroseconds(jsonDocument[name].GetUint64()); - hasTS = true; - } - else if (jsonDocument[name].IsString()) - { - ts = utils::toUnixTicks(jsonDocument[name].GetString()); - hasTS = true; + const auto& arr = tsField.GetArray(); + if (arr.Empty()) + { + result.addError("Timestamp field is an array but it is empty. "); + } + else if (arr[0].IsInt() || arr[0].IsUint64() || arr[0].IsInt64()) + { + auto [parsingStatus, out] = parseHomogeneousArray(arr); + result.merge(parsingStatus); + hasTS = parsingStatus.success; + if (parsingStatus.success) + ts = std::move(out); + + std::for_each(ts.begin(), ts.end(), [](auto& val) { val = mqtt::utils::numericToMicroseconds(val); }); + } + else if (arr[0].IsString()) + { + std::vector stringTs; + auto [parsingStatus, out] = parseHomogeneousArray(arr); + result.merge(parsingStatus); + hasTS = parsingStatus.success; + if (parsingStatus.success) + stringTs = std::move(out); + ts.reserve(stringTs.size()); + std::for_each(stringTs.cbegin(), + stringTs.cend(), + [&ts](const auto& val) { ts.push_back(utils::toUnixTicks(val)); }); + hasTS = true; + } + else + { + result.addError("Timestamp value type is not supported. "); + } } else { - result.success = false; - result.msg += "Timestamp value type is not supported. "; + if (tsField.IsInt() || tsField.IsUint64() || tsField.IsInt64()) + { + ts.push_back(mqtt::utils::numericToMicroseconds(tsField.GetUint64())); + hasTS = true; + } + else if (tsField.IsString()) + { + ts.push_back(utils::toUnixTicks(tsField.GetString())); + hasTS = true; + } + else + { + result.addError("Timestamp value type is not supported. "); + } } } else @@ -268,35 +431,47 @@ std::pair> MqttDataWrapper: } catch (...) { - result.success = false; - result.msg += "Error deserializing MQTT payload. "; + result.addError("Error deserializing MQTT payload. "); } if (!hasValue || (!msgDescriptor.tsFieldName.empty() && !hasTS)) { - result.success = false; - result.msg += "Not all required fields are present in the JSON message. "; + result.addError("Not all required fields are present in the JSON message. "); if (!hasValue) - result.msg += fmt::format("Couldn't extract value field (\"{}\") from the JSON message. ", msgDescriptor.valueFieldName); + result.addError(fmt::format("Couldn't extract value field (\"{}\") from the JSON message. ", msgDescriptor.valueFieldName)); if (!msgDescriptor.tsFieldName.empty() && !hasTS) - result.msg += fmt::format("Couldn't extract timestamp field (\"{}\") from the JSON message. ", msgDescriptor.tsFieldName); + result.addError(fmt::format("Couldn't extract timestamp field (\"{}\") from the JSON message. ", msgDescriptor.tsFieldName)); } if (result.success) { - // TODO : value [1, 2, 3, ...] support - DataPackets dataPackets; std::visit( - [&](auto&& val) + [&](auto&& values) { - using T = std::decay_t; - if (hasTS) - dataPackets = buildDataPackets(val, ts); + using T = std::decay_t; + if (hasTS && ts.size() != values.size()) + { + result.addError("Timestamp and value array sizes do not match. "); + return; + } + if constexpr (std::is_same_v>) + { + for (size_t i = 0; i < values.size(); ++i) + { + DataPackets dp = hasTS ? buildDataPackets(values[i], ts[i]) : buildDataPackets(values[i]); + + if (dp.dataPacket.assigned()) + outputData.push_back(std::move(dp)); + } + } else - dataPackets = buildDataPackets(val); + { + DataPackets dp = hasTS ? buildDataPackets(values, ts) : buildDataPackets(values); + + if (dp.dataPacket.assigned()) + outputData.push_back(std::move(dp)); + } }, value); - if (dataPackets.dataPacket.assigned()) - outputData.push_back(std::move(dataPackets)); } return std::pair{result, outputData}; } @@ -312,7 +487,19 @@ void MqttDataWrapper::sendDataSamples(const DataPackets& dataPackets) template DataPackets -MqttDataWrapper::buildDataPackets(T value, uint64_t timestamp) +MqttDataWrapper::buildDataPackets(const T& value, uint64_t timestamp) +{ + DataPackets dataPackets; + + dataPackets.domainDataPacket = buildDomainDataPacket(outputSignal, timestamp); + dataPackets.dataPacket = buildDataPacket(outputSignal, value, dataPackets.domainDataPacket); + + return dataPackets; +} + +template +DataPackets +MqttDataWrapper::buildDataPackets(const T& value, const std::vector& timestamp) { DataPackets dataPackets; @@ -324,7 +511,7 @@ MqttDataWrapper::buildDataPackets(T value, uint64_t timestamp) template DataPackets -MqttDataWrapper::buildDataPackets(T value) +MqttDataWrapper::buildDataPackets(const T& value) { DataPackets dataPackets; dataPackets.dataPacket = buildDataPacket(outputSignal, value, daq::DataPacketPtr()); @@ -376,11 +563,12 @@ bool MqttDataWrapper::isTypeTheSame(daq::SampleType sampleType) return false; } -template -daq::DataPacketPtr MqttDataWrapper::buildDataPacket(daq::GenericSignalConfigPtr<> signalConfig, T value, const daq::DataPacketPtr domainPacket) +template +daq::DataPacketPtr MqttDataWrapper::buildDataPacket(daq::GenericSignalConfigPtr<> signalConfig, const T& value, const daq::DataPacketPtr domainPacket) { const auto curType = signalConfig.getDescriptor().getSampleType(); - if (isTypeTheSame(curType) == false) + using ActualType = sample_type_t; + if (!isTypeTheSame(curType)) { if constexpr (std::is_same_v) { @@ -391,47 +579,63 @@ daq::DataPacketPtr MqttDataWrapper::buildDataPacket(daq::GenericSignalConfigPtr< } else { - auto descriptor = DataDescriptorBuilderCopy(signalConfig.getDescriptor()).setSampleType(daq::SampleTypeFromType::SampleType).build(); + auto descriptor = + DataDescriptorBuilderCopy(signalConfig.getDescriptor()).setSampleType(daq::SampleTypeFromType::SampleType).build(); signalConfig.setDescriptor(descriptor); } } - daq::DataPacketPtr dataPacket = createEmptyDataPacket(signalConfig, domainPacket, value); + + auto dataPacket = createEmptyDataPacket(signalConfig, domainPacket, value); copyDataIntoPacket(dataPacket, value); return dataPacket; } template -daq::DataPacketPtr MqttDataWrapper::createEmptyDataPacket(const daq::GenericSignalConfigPtr<> signalConfig, const daq::DataPacketPtr domainPacket, T value) +daq::DataPacketPtr MqttDataWrapper::createEmptyDataPacket(const daq::GenericSignalConfigPtr<> signalConfig, const daq::DataPacketPtr domainPacket, const T& value) { daq::DataPacketPtr dataPacket; - if constexpr (std::is_same_v) + uint64_t size = 1; + using ActualType = sample_type_t; + if constexpr (is_std_vector_v || std::is_same_v) { - dataPacket = daq::BinaryDataPacket(domainPacket, signalConfig.getDescriptor(), value.size()); + size = value.size(); + } + + if constexpr (std::is_same_v) + { + dataPacket = daq::BinaryDataPacket(domainPacket, signalConfig.getDescriptor(), size); } else { if (signalConfig.getDomainSignal().assigned() && domainPacket.assigned()) { - dataPacket = DataPacketWithDomain(domainPacket, signalConfig.getDescriptor(), 1); + dataPacket = DataPacketWithDomain(domainPacket, signalConfig.getDescriptor(), size); } else { - dataPacket = DataPacket(signalConfig.getDescriptor(), 1); + dataPacket = DataPacket(signalConfig.getDescriptor(), size); } } + return dataPacket; } template -void MqttDataWrapper::copyDataIntoPacket(daq::DataPacketPtr dataPacket, T value) +void MqttDataWrapper::copyDataIntoPacket(daq::DataPacketPtr dataPacket, const T& value) { if (!dataPacket.assigned()) return; - if constexpr (std::is_same_v) + + using ActualType = sample_type_t; + if constexpr (is_std_vector_v && !std::is_same_v) { - memcpy(dataPacket.getData(), value.c_str(), value.size()); + std::memcpy(dataPacket.getRawData(), value.data(), value.size() * sizeof(ActualType)); } - else + else if constexpr (!is_std_vector_v && std::is_same_v) + { + std::memcpy(dataPacket.getData(), value.c_str(), value.size()); + } + else if constexpr (!is_std_vector_v && !std::is_same_v) { auto outputData = reinterpret_cast(dataPacket.getRawData()); *outputData = value; @@ -450,6 +654,18 @@ daq::DataPacketPtr MqttDataWrapper::buildDomainDataPacket(daq::GenericSignalConf return dataPacket; } +daq::DataPacketPtr MqttDataWrapper::buildDomainDataPacket(daq::GenericSignalConfigPtr<> signalConfig, const std::vector& timestamp) +{ + daq::DataPacketPtr dataPacket; + if (signalConfig.getDomainSignal().assigned()) + { + dataPacket = daq::DataPacket(signalConfig.getDomainSignal().getDescriptor(), timestamp.size()); + std::memcpy(dataPacket.getRawData(), timestamp.data(), timestamp.size() * sizeof(uint64_t)); + } + + return dataPacket; +} + daq::UnitPtr MqttDataWrapper::extractSignalUnit(const rapidjson::Value& signalObj) { daq::UnitPtr unit; From ebf5c0128371359dd802ee7a6cc6cfb50a23e904 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Mon, 26 Jan 2026 21:31:37 +0100 Subject: [PATCH 29/49] mqtt: publisher fix - processing SampleCount() to extract all samples from DataPacket --- .../atomic_signal_atomic_sample_handler.h | 4 +- .../atomic_signal_sample_arr_handler.h | 5 +- .../mqtt_streaming_module/handler_base.h | 28 +++++----- .../include/mqtt_streaming_module/types.h | 5 +- .../atomic_signal_atomic_sample_handler.cpp | 14 ++--- .../src/atomic_signal_sample_arr_handler.cpp | 48 +++++++++++------ .../src/mqtt_publisher_fb_impl.cpp | 2 +- .../src/signal_arr_atomic_sample_handler.cpp | 4 +- .../tests/test_mqtt_publisher_fb.cpp | 52 ++++++++++++++----- 9 files changed, 106 insertions(+), 56 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h index 14d4163..96faa42 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h @@ -38,8 +38,8 @@ class AtomicSignalAtomicSampleHandler : public HandlerBase virtual MqttData processSignalContext(SignalContext& signalContext); void processSignalDescriptorChanged(SignalContext& signalCtx, const DataDescriptorPtr& valueSigDesc, const DataDescriptorPtr& domainSigDesc); - MqttDataSample processDataPacket(SignalContext& signalContext, const DataPacketPtr& dataPacket); - std::string toString(const std::string valueFieldName, daq::DataPacketPtr packet); + MqttDataSample processDataPacket(SignalContext& signalContext, const DataPacketPtr& dataPacket, size_t offset); + std::string toString(const std::string valueFieldName, daq::DataPacketPtr packet, size_t offset); std::string buildTopicName(const SignalContext& signalContext); }; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h index 4084a9e..f18f510 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h @@ -31,8 +31,9 @@ class AtomicSignalSampleArrayHandler : public AtomicSignalAtomicSampleHandler size_t packSize; MqttData processSignalContext(SignalContext& signalContext) override; - MqttDataSample processDataPackets(SignalContext& signalContext, const std::vector& dataPacket); - std::string toString(const std::string valueFieldName, const std::vector& dataPackets); + MqttDataSample processDataPackets(SignalContext& signalContext); + std::string toString(const std::string valueFieldName, SignalContext& signalContext); + std::pair getSample(SignalContext& signalContext); }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h index 4cf5b81..c68fe5a 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h @@ -72,7 +72,7 @@ class HandlerBase return std::pair{num, den}; } - static uint64_t convertToEpoch(const DataPacketPtr domainPacket) + static uint64_t convertToEpoch(const DataPacketPtr domainPacket, size_t offset) { constexpr uint64_t US_IN_S = 1'000'000; // amount microseconds in a second const auto tickResolution = domainPacket.getDataDescriptor().getTickResolution().simplify(); @@ -84,14 +84,14 @@ class HandlerBase uint64_t ts = 0; if (domainPacket.getDataDescriptor().getSampleType() == SampleType::UInt64) - ts = *(static_cast(domainPacket.getData())); + ts = *(static_cast(domainPacket.getData()) + offset); else if (domainPacket.getDataDescriptor().getSampleType() == SampleType::Int64) - ts = *(static_cast(domainPacket.getData())); + ts = *(static_cast(domainPacket.getData()) + offset); ts = ts * num / den; // us return ts; } - static std::string toString(const DataPacketPtr& dataPacket) + static std::string toString(const DataPacketPtr& dataPacket, size_t offset) { auto sampleType = dataPacket.getDataDescriptor().getSampleType(); std::string data("unsupported"); @@ -99,34 +99,34 @@ class HandlerBase switch (sampleType) { case SampleType::Float64: - data = std::to_string(*(static_cast::Type*>(dataPacket.getData()))); + data = std::to_string(*(static_cast::Type*>(dataPacket.getData()) + offset)); break; case SampleType::Float32: - data = std::to_string(*(static_cast::Type*>(dataPacket.getData()))); + data = std::to_string(*(static_cast::Type*>(dataPacket.getData()) + offset)); break; case SampleType::UInt64: - data = std::to_string(*(static_cast::Type*>(dataPacket.getData()))); + data = std::to_string(*(static_cast::Type*>(dataPacket.getData()) + offset)); break; case SampleType::Int64: - data = std::to_string(*(static_cast::Type*>(dataPacket.getData()))); + data = std::to_string(*(static_cast::Type*>(dataPacket.getData()) + offset)); break; case SampleType::UInt32: - data = std::to_string(*(static_cast::Type*>(dataPacket.getData()))); + data = std::to_string(*(static_cast::Type*>(dataPacket.getData()) + offset)); break; case SampleType::Int32: - data = std::to_string(*(static_cast::Type*>(dataPacket.getData()))); + data = std::to_string(*(static_cast::Type*>(dataPacket.getData()) + offset)); break; case SampleType::UInt16: - data = std::to_string(*(static_cast::Type*>(dataPacket.getData()))); + data = std::to_string(*(static_cast::Type*>(dataPacket.getData()) + offset)); break; case SampleType::Int16: - data = std::to_string(*(static_cast::Type*>(dataPacket.getData()))); + data = std::to_string(*(static_cast::Type*>(dataPacket.getData()) + offset)); break; case SampleType::UInt8: - data = std::to_string(*(static_cast::Type*>(dataPacket.getData()))); + data = std::to_string(*(static_cast::Type*>(dataPacket.getData()) + offset)); break; case SampleType::Int8: - data = std::to_string(*(static_cast::Type*>(dataPacket.getData()))); + data = std::to_string(*(static_cast::Type*>(dataPacket.getData()) + offset)); break; case SampleType::String: case SampleType::Binary: diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/types.h b/mqtt_streaming_module/include/mqtt_streaming_module/types.h index 208b630..0fb7c2a 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/types.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/types.h @@ -4,6 +4,7 @@ #include #include #include +#include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE @@ -43,7 +44,9 @@ struct SignalContext { size_t index; InputPortConfigPtr inputPort; - std::vector data; + std::list data; + size_t dataSize = 0; + size_t offset = 0; SignalConfigPtr previewSignal; }; diff --git a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp index 2ae01b0..fbbd9ea 100644 --- a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp @@ -108,7 +108,9 @@ MqttData AtomicSignalAtomicSampleHandler::processSignalContext(SignalContext& si } else if (packet.getType() == PacketType::Data) { - messages.emplace_back(processDataPacket(signalContext, packet.asPtr())); + auto dataPacket = packet.asPtr(); + for (size_t i = 0; i < dataPacket.getSampleCount(); ++i) + messages.emplace_back(processDataPacket(signalContext, dataPacket, i)); } packet = conn.dequeue(); @@ -122,13 +124,13 @@ void AtomicSignalAtomicSampleHandler::processSignalDescriptorChanged(SignalConte { } -std::string AtomicSignalAtomicSampleHandler::toString(const std::string valueFieldName, daq::DataPacketPtr packet) +std::string AtomicSignalAtomicSampleHandler::toString(const std::string valueFieldName, daq::DataPacketPtr packet, size_t offset) { std::string result; - std::string data = HandlerBase::toString(packet); + std::string data = HandlerBase::toString(packet, offset); if (auto domainPacket = packet.getDomainPacket(); domainPacket.assigned()) { - uint64_t ts = convertToEpoch(domainPacket); + uint64_t ts = convertToEpoch(domainPacket, offset); result = fmt::format("{{\"{}\" : {}, \"timestamp\": {}}}", valueFieldName, data, ts); } else @@ -144,11 +146,11 @@ std::string AtomicSignalAtomicSampleHandler::buildTopicName(const SignalContext& return signalContext.inputPort.getSignal().getGlobalId().toStdString(); } -MqttDataSample AtomicSignalAtomicSampleHandler::processDataPacket(SignalContext& signalContext, const DataPacketPtr& dataPacket) +MqttDataSample AtomicSignalAtomicSampleHandler::processDataPacket(SignalContext& signalContext, const DataPacketPtr& dataPacket, size_t offset) { const auto signal = signalContext.inputPort.getSignal(); std::string valueFieldName = buildValueFieldName(signalNamesMode, signal); - auto msg = toString(valueFieldName, dataPacket); + auto msg = toString(valueFieldName, dataPacket, offset); std::string topic = buildTopicName(signalContext); return MqttDataSample{signalContext.previewSignal, topic, msg}; } diff --git a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp index b964fd3..bfbad32 100644 --- a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp @@ -52,12 +52,12 @@ MqttData AtomicSignalSampleArrayHandler::processSignalContext(SignalContext& sig } else if (packet.getType() == PacketType::Data) { - signalContext.data.push_back(packet.asPtr()); - if (signalContext.data.size() >= packSize) - { - messages.emplace_back(processDataPackets(signalContext, signalContext.data)); - signalContext.data.clear(); - } + auto dataPacket = packet.asPtr(); + signalContext.data.push_back(dataPacket); + signalContext.dataSize += dataPacket.getSampleCount(); + while (signalContext.dataSize >= packSize) + messages.emplace_back(processDataPackets(signalContext)); + } packet = conn.dequeue(); @@ -65,32 +65,50 @@ MqttData AtomicSignalSampleArrayHandler::processSignalContext(SignalContext& sig return messages; } -std::string AtomicSignalSampleArrayHandler::toString(const std::string valueFieldName, const std::vector& dataPackets) +std::pair AtomicSignalSampleArrayHandler::getSample(SignalContext& signalContext) +{ + if (signalContext.data.empty()) + return {nullptr, 0}; + auto dataPacket = signalContext.data.front(); + size_t offset = signalContext.offset++; + signalContext.dataSize--; + if (signalContext.offset == dataPacket.getSampleCount()) + { + signalContext.data.pop_front(); + signalContext.offset = 0; + } + return {dataPacket, offset}; +} + +std::string AtomicSignalSampleArrayHandler::toString(const std::string valueFieldName, SignalContext& signalContext) { std::ostringstream dataOss; std::ostringstream tsOss; bool hasDomain = true; dataOss << "["; tsOss << "["; - for (size_t i = 0; i < dataPackets.size(); ++i) + size_t commonCnt = 0; + while (commonCnt < packSize) { - if (i > 0) + auto [dataPacket, offset] = getSample(signalContext); + if (commonCnt > 0) { dataOss << ", "; tsOss << ", "; } - dataOss << HandlerBase::toString(dataPackets[i]); + dataOss << HandlerBase::toString(dataPacket, offset); - if (auto domainPacket = dataPackets[i].getDomainPacket(); domainPacket.assigned()) + if (auto domainPacket = dataPacket.getDomainPacket(); domainPacket.assigned()) { - uint64_t ts = convertToEpoch(domainPacket); + uint64_t ts = convertToEpoch(domainPacket, offset); tsOss << std::to_string(ts); } else { hasDomain = false; } + commonCnt++; } dataOss << "]"; tsOss << "]"; @@ -103,13 +121,13 @@ std::string AtomicSignalSampleArrayHandler::toString(const std::string valueFiel return result; } -MqttDataSample AtomicSignalSampleArrayHandler::processDataPackets(SignalContext& signalContext, const std::vector& dataPacket) +MqttDataSample AtomicSignalSampleArrayHandler::processDataPackets(SignalContext& signalContext) { - if (dataPacket.empty()) + if (signalContext.data.empty()) return MqttDataSample{nullptr, "", ""}; const auto signal = signalContext.inputPort.getSignal(); std::string valueFieldName = buildValueFieldName(signalNamesMode, signal); - auto msg = toString(valueFieldName, dataPacket); + auto msg = toString(valueFieldName, signalContext); std::string topic = buildTopicName(signalContext); return MqttDataSample{signalContext.previewSignal, topic, msg}; } diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index adbf641..07f8d44 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -279,7 +279,7 @@ void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) if (reassignPorts) { const auto inputPort = createAndAddInputPort(fmt::format("Input{}", size_t(inputPortCount)), PacketReadyNotification::SameThread); - signalContexts.emplace_back(SignalContext{size_t(inputPortCount++), inputPort, {}, nullptr}); + signalContexts.emplace_back(SignalContext{size_t(inputPortCount++), inputPort, {}, 0, 0, nullptr}); } } diff --git a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp index 6f8fb63..35d10f4 100644 --- a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp @@ -135,10 +135,10 @@ std::string SignalArrayAtomicSampleHandler::getSchema() std::string SignalArrayAtomicSampleHandler::toString(const std::string valueFieldName, daq::DataPacketPtr packet) { std::string result; - std::string data = HandlerBase::toString(packet); + std::string data = HandlerBase::toString(packet, 0); if (auto domainPacket = packet.getDomainPacket(); domainPacket.assigned()) { - uint64_t ts = convertToEpoch(domainPacket); + uint64_t ts = convertToEpoch(domainPacket, 0); result = fmt::format("{{\"{}\" : {}, \"timestamp\": {}}}", valueFieldName, data, ts); } else diff --git a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp index 35114bd..4ada713 100644 --- a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp @@ -73,22 +73,32 @@ class SignalHelper return data; } - void send(const std::vector>& data) const + void send(const std::vector>& data, size_t packSize = 3) const { - for (size_t i = 0; i < data.size(); i++) + if (packSize == 0) + packSize = 1; + auto sendPacket = [this](SignalConfigPtr signal, const std::vector& data, DataPacketPtr domainPacket) { - auto sendPacket = [this](SignalConfigPtr signal, T data, DataPacketPtr domainPacket) + auto dataPacket = DataPacketWithDomain(domainPacket, signal0.getDescriptor(), data.size()); + copyData(dataPacket, data); + signal.sendPacket(dataPacket); + }; + for (size_t i = 0; i < data.size(); i += packSize) + { + std::vector dataPack; + std::vector tsPack; + for (size_t j = 0; j < packSize && (j + i) < data.size(); j++) { - auto dataPacket = DataPacketWithDomain(domainPacket, signal0.getDescriptor(), 1); - copyData(dataPacket, data); - signal.sendPacket(dataPacket); - }; - auto domainPacket = DataPacket(signal0.getDomainSignal().getDescriptor(), 1, i); - memcpy(domainPacket.getData(), &(data[i].second), sizeof(uint64_t)); + dataPack.push_back(data[j + i].first); + tsPack.push_back(data[j + i].second); + } + + auto domainPacket = DataPacket(signal0.getDomainSignal().getDescriptor(), tsPack.size(), i); + memcpy(domainPacket.getData(), tsPack.data(), sizeof(uint64_t) * tsPack.size()); SignalConfigPtr dSignal = signal0.getDomainSignal(); dSignal.sendPacket(domainPacket); - sendPacket(signal0, data[i].first, domainPacket); - sendPacket(signal1, data[i].first, domainPacket); + sendPacket(signal0, dataPack, domainPacket); + sendPacket(signal1, dataPack, domainPacket); } } @@ -140,7 +150,7 @@ class SignalHelper bool copyData(DataPacketPtr destination, const T& source) const { const auto dataType = destination.getDataDescriptor().getSampleType(); - if (checkType(dataType) && getSampleSize(dataType) != sizeof(source)) + if (checkType(dataType) && getSampleSize(dataType) != sizeof(mqtt::sample_type_t)) return false; if constexpr (std::is_same_v) { @@ -153,6 +163,22 @@ class SignalHelper return true; } + bool copyData(DataPacketPtr destination, const std::vector& source) const + { + const auto dataType = destination.getDataDescriptor().getSampleType(); + if (checkType(dataType) && getSampleSize(dataType) != sizeof(mqtt::sample_type_t)) + return false; + if constexpr (std::is_same_v, std::string>) + { + return false; + } + else + { + memcpy(destination.getRawData(), source.data(), source.size() * sizeof(T)); + } + return true; + } + T generateData(size_t i) const { T sampleData; @@ -1001,7 +1027,7 @@ TEST_F(MqttPublisherFbTest, WrongConfig) TEST_F(MqttPublisherFbTest, TransferSingle0) { - const size_t sampleCnt0 = 15; + const size_t sampleCnt0 = 14; const size_t sampleCnt1 = sampleCnt0 * 2; SignalHelper helpDouble{}; SignalHelper helpUint{}; From 4c740d283b0c918c43022e21226815297d79beef Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 28 Jan 2026 10:12:04 +0100 Subject: [PATCH 30/49] mqtt: new publisher mode with shared ts arrays --- .../group_signal_shared_ts_arr_handler.h | 181 ++++++++++++++++++ .../group_signal_shared_ts_handler.h | 8 + .../mqtt_streaming_module/handler_factory.h | 9 +- mqtt_streaming_module/src/CMakeLists.txt | 4 + .../group_signal_shared_ts_arr_handler.cpp | 100 ++++++++++ .../src/group_signal_shared_ts_handler.cpp | 12 +- .../src/mqtt_publisher_fb_impl.cpp | 27 ++- .../tests/test_mqtt_publisher_fb.cpp | 176 +++++++++++++---- 8 files changed, 455 insertions(+), 62 deletions(-) create mode 100644 mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h create mode 100644 mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h new file mode 100644 index 0000000..758076c --- /dev/null +++ b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h @@ -0,0 +1,181 @@ +/* + * Copyright 2022-2025 openDAQ d.o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "group_signal_shared_ts_handler.h" + +#include +#include +#include + +BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE + +class StringDataBuilder +{ +public: + StringDataBuilder(size_t packSize) + : packSize(packSize) + { + reset(); + } + + void append(const SampleType sampleType, void* data, size_t size) + { + switch (sampleType) + { + case SampleType::Float64: + toString::Type>(data, size); + break; + case SampleType::Float32: + toString::Type>(data, size); + break; + case SampleType::UInt64: + toString::Type>(data, size); + break; + case SampleType::Int64: + toString::Type>(data, size); + break; + case SampleType::UInt32: + toString::Type>(data, size); + break; + case SampleType::Int32: + toString::Type>(data, size); + break; + case SampleType::UInt16: + toString::Type>(data, size); + break; + case SampleType::Int16: + toString::Type>(data, size); + break; + case SampleType::UInt8: + toString::Type>(data, size); + break; + case SampleType::Int8: + toString::Type>(data, size); + break; + default: + break; + } + } + + std::string getPack(const std::string& valueFieldName) + { + std::string result; + if (!values.empty()) + { + result = fmt::format("\"{}\" : {}", valueFieldName, values.front()); + values.pop_front(); + } + + return result; + } + + bool empty() const + { + return values.empty(); + } + +protected: + const size_t packSize; + std::list values; + size_t offset = 0; + std::ostringstream oss; + + template + void toString(void* data, size_t size) + { + for (size_t dataOffset = 0; dataOffset < size; ++dataOffset) + { + prefix(); + oss << std::to_string(*(static_cast(data) + dataOffset)); + ++offset; + postfix(); + } + } + + void prefix() + { + if (offset > 0) + oss << ", "; + } + + void postfix() + { + if (offset == packSize) + { + offset = 0; + oss << "]"; + values.push_back(std::move(oss).str()); + reset(); + } + } + + void reset() + { + oss.clear(); + oss.str(""); + oss << "["; + } +}; + +class StringTsBuilder : public StringDataBuilder +{ +public: + StringTsBuilder(size_t packSize) + : StringDataBuilder(packSize) + { + } + + void append(const TimestampTickStruct tsStruct, size_t size) + { + for (size_t dataOffset = 0; dataOffset < size; ++dataOffset) + { + prefix(); + oss << std::to_string(tsStruct.tsToTicks(dataOffset)); + ++offset; + postfix(); + } + } + + std::string getPack() + { + return StringDataBuilder::getPack("timestamp"); + } +}; + +class GroupSignalSharedTsArrHandler : public GroupSignalSharedTsHandler +{ +public: + explicit GroupSignalSharedTsArrHandler(WeakRefPtr parentFb, + SignalValueJSONKey signalNamesMode, + std::string topic, + size_t packSize); + ~GroupSignalSharedTsArrHandler() = default; + + MqttData processSignalContexts(std::vector& signalContexts) override; + ProcedureStatus signalListChanged(std::vector& signalContexts) override; + std::string getSchema() override; + +protected: + const size_t packSize; + std::vector dataBuilders; + StringTsBuilder tsBuilder; + + void initDataBuilders(const size_t size); +}; + +END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h index 0590a80..3efd203 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h @@ -29,6 +29,13 @@ struct TimestampTickStruct uint64_t ratioNum; uint64_t ratioDen; uint64_t multiplier; + + uint64_t tsToTicks(size_t offset) const + { + // const uint64_t epochTime = (firstTick + delta * offset) * ratioNum * US_IN_S / ratioDen; // us + uint64_t res = ((firstTick + delta * offset) * ratioNum * multiplier) / ratioDen; + return res; + } }; class GroupSignalSharedTsHandler : public HandlerBase @@ -56,6 +63,7 @@ class GroupSignalSharedTsHandler : public HandlerBase std::string tsToString(TimestampTickStruct tsStruct, SizeT offset); std::string buildTopicName(); void createReader(const std::vector& signalContexts); + void createReaderInternal(const std::vector& signalContexts); void allocateBuffers(const std::vector& signalContexts); void deallocateBuffers(); static std::string messageFromFields(const std::vector& fields); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h b/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h index 769c0bf..af790b7 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h @@ -16,6 +16,7 @@ #pragma once +#include "mqtt_streaming_module/group_signal_shared_ts_arr_handler.h" #include "mqtt_streaming_module/types.h" #include #include @@ -32,9 +33,11 @@ class HandlerFactory { if (config.topicMode == TopicMode::Single) { - return std::make_unique(parentFb, - config.valueFieldName, - config.topicName.empty() ? publisherFbGlobalId : config.topicName); + const auto topic = config.topicName.empty() ? publisherFbGlobalId : config.topicName; + if (config.groupValues) + return std::make_unique(parentFb, config.valueFieldName, topic, config.groupValuesPackSize); + else + return std::make_unique(parentFb, config.valueFieldName, topic); } else if (config.topicMode == TopicMode::PerSignal) { diff --git a/mqtt_streaming_module/src/CMakeLists.txt b/mqtt_streaming_module/src/CMakeLists.txt index 805fc29..78b3f60 100644 --- a/mqtt_streaming_module/src/CMakeLists.txt +++ b/mqtt_streaming_module/src/CMakeLists.txt @@ -15,6 +15,7 @@ set(SRC_Include common.h atomic_signal_atomic_sample_handler.h atomic_signal_sample_arr_handler.h group_signal_shared_ts_handler.h + group_signal_shared_ts_arr_handler.h signal_arr_atomic_sample_handler.h types.h status_helper.h @@ -30,6 +31,7 @@ set(SRC_Srcs module_dll.cpp atomic_signal_atomic_sample_handler.cpp atomic_signal_sample_arr_handler.cpp group_signal_shared_ts_handler.cpp + group_signal_shared_ts_arr_handler.cpp signal_arr_atomic_sample_handler.cpp ) @@ -64,10 +66,12 @@ source_group("handlers" FILES ${MODULE_HEADERS_DIR}/handler_base.h ${MODULE_HEADERS_DIR}/atomic_signal_atomic_sample_handler.h ${MODULE_HEADERS_DIR}/atomic_signal_sample_arr_handler.h ${MODULE_HEADERS_DIR}/group_signal_shared_ts_handler.h + ${MODULE_HEADERS_DIR}/group_signal_shared_ts_arr_handler.h ${MODULE_HEADERS_DIR}/signal_arr_atomic_sample_handler.h atomic_signal_atomic_sample_handler.cpp atomic_signal_sample_arr_handler.cpp group_signal_shared_ts_handler.cpp + group_signal_shared_ts_arr_handler.cpp signal_arr_atomic_sample_handler.cpp ) diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp new file mode 100644 index 0000000..59ed734 --- /dev/null +++ b/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE + +GroupSignalSharedTsArrHandler::GroupSignalSharedTsArrHandler(WeakRefPtr parentFb, + SignalValueJSONKey signalNamesMode, + std::string topic, + size_t packSize) + : GroupSignalSharedTsHandler(parentFb, signalNamesMode, topic), + packSize(packSize), + tsBuilder(packSize) +{ +} + +MqttData GroupSignalSharedTsArrHandler::processSignalContexts(std::vector& signalContexts) +{ + MqttData messages; + std::scoped_lock lock(sync); + if (!reader.assigned()) + return messages; + + const auto dataAvailable = reader.getAvailableCount(); + SizeT count = std::min(SizeT{buffersSize}, dataAvailable); + auto status = reader.read(dataBuffers.data(), &count); + if (count > 0) + { + const auto tsStruct = domainToTs(status); + for (size_t signalCnt = 0; signalCnt < signalContexts.size() - 1; ++signalCnt) + { + const auto signal = signalContexts[signalCnt].inputPort.getSignal(); + dataBuilders[signalCnt].append(signal.getDescriptor().getSampleType(), dataBuffers[signalCnt], count); + } + tsBuilder.append(tsStruct, count); + } + + while (!tsBuilder.empty()) + { + std::vector fields; + for (size_t signalCnt = 0; signalCnt < signalContexts.size() - 1; ++signalCnt) + { + std::string valueFieldName = buildValueFieldName(signalNamesMode, signalContexts[signalCnt].inputPort.getSignal()); + fields.emplace_back(dataBuilders[signalCnt].getPack(valueFieldName)); + } + fields.emplace_back(tsBuilder.getPack()); + messages.emplace_back(MqttDataSample{signalContexts[0].previewSignal, buildTopicName(), messageFromFields(fields)}); + } + return messages; +} + +ProcedureStatus GroupSignalSharedTsArrHandler::signalListChanged(std::vector& signalContexts) +{ + auto status = GroupSignalSharedTsHandler::signalListChanged(signalContexts); + initDataBuilders(signalContexts.size() - 1); + return status; +} + +std::string GroupSignalSharedTsArrHandler::getSchema() +{ + if (packSize == 1) + { + return fmt::format("{{\"{}\" : [], ..., \"{}\" : [], \"timestamp\": []}}", + buildValueFieldNameForSchema(signalNamesMode, "_0"), + buildValueFieldNameForSchema(signalNamesMode, "_N")); + } + else if (packSize == 2) + { + return fmt::format("{{\"{}\" : [, ], ..., \"{}\" : [, ], " + "\"timestamp\": [, ]}}", + buildValueFieldNameForSchema(signalNamesMode, "_0"), + buildValueFieldNameForSchema(signalNamesMode, "_N")); + } + else + { + return fmt::format("{{\"{}\" : [, ..., ], ..., \"timestamp\": [, ..., " + "]}}", + buildValueFieldNameForSchema(signalNamesMode), + packSize - 1, + packSize - 1); + } +} + +void GroupSignalSharedTsArrHandler::initDataBuilders(const size_t size) +{ + if (dataBuilders.empty() || dataBuilders.size() != size) + { + dataBuilders.clear(); + dataBuilders.reserve(size); + for (size_t i = 0; i < size; ++i) + dataBuilders.emplace_back(packSize); + } +} + +END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp index 7e40e68..4fd9bd5 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp @@ -193,6 +193,10 @@ TimestampTickStruct GroupSignalSharedTsHandler::domainToTs(const MultiReaderStat res.ratioDen = descriptor.getTickResolution().getDenominator(); res.multiplier = 1'000'000; // amount of us in a second res.delta = descriptor.getRule().getParameters().get("delta").getValue(0); + + const uint64_t g = std::gcd(res.multiplier, res.ratioDen); + res.multiplier /= g; + res.ratioDen /= g; return res; } @@ -273,9 +277,6 @@ std::string GroupSignalSharedTsHandler::toString(const std::string& valueFieldNa std::string GroupSignalSharedTsHandler::tsToString(TimestampTickStruct tsStruct, SizeT offset) { // const uint64_t epochTime = (firstTick + delta * offset) * ratioNum * US_IN_S / ratioDen; // us - const uint64_t g = std::gcd(tsStruct.multiplier, tsStruct.ratioDen); - tsStruct.multiplier /= g; - tsStruct.ratioDen /= g; return fmt::format("\"timestamp\" : {}", std::to_string(((tsStruct.firstTick + tsStruct.delta * offset) * tsStruct.ratioNum * tsStruct.multiplier) / tsStruct.ratioDen)); @@ -289,6 +290,11 @@ std::string GroupSignalSharedTsHandler::buildTopicName() void GroupSignalSharedTsHandler::createReader(const std::vector& signalContexts) { std::scoped_lock lock(sync); + createReaderInternal(signalContexts); +} + +void GroupSignalSharedTsHandler::createReaderInternal(const std::vector& signalContexts) +{ // signalContexts always contain an unconnected input port if (signalContexts.size() <= 1) return; diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index 07f8d44..423529f 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -101,27 +101,24 @@ FunctionBlockTypePtr MqttPublisherFbImpl::CreateType() defaultConfig.addProperty(builder.build()); } { - auto builder = - BoolPropertyBuilder(PROPERTY_NAME_PUB_GROUP_VALUES, False) - .setVisible(EvalValue(std::string("$") + PROPERTY_NAME_PUB_TOPIC_MODE + " == 0")) - .setDescription( - "Enables the use of a sample pack for a signal when publishing in TopicPerSignal mode. By default it is set to false."); + auto builder = SelectionPropertyBuilder(PROPERTY_NAME_PUB_VALUE_FIELD_NAME, List("GlobalID", "LocalID", "Name"), 0) + .setDescription("Describes how to name a JSON value field. By default it is set to GlobalID."); defaultConfig.addProperty(builder.build()); } { - auto builder = SelectionPropertyBuilder(PROPERTY_NAME_PUB_VALUE_FIELD_NAME, List("GlobalID", "LocalID", "Name"), 0) - .setDescription("Describes how to name a JSON value field. By default it is set to GlobalID."); + auto builder = + BoolPropertyBuilder(PROPERTY_NAME_PUB_GROUP_VALUES, False) + .setDescription( + "Enables the use of a sample pack for a signal. By default it is set to false."); defaultConfig.addProperty(builder.build()); } { - auto builder = - IntPropertyBuilder(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE, DEFAULT_PUB_PACK_SIZE) - .setMinValue(1) - .setVisible(EvalValue(std::string("($") + PROPERTY_NAME_PUB_TOPIC_MODE + " == 0) && " + std::string("($") + - PROPERTY_NAME_PUB_GROUP_VALUES + ")")) - .setDescription(fmt::format("Set the size of the sample pack when publishing grouped values in TopicPerSignal mode. " - "By default it is set to {}.", - DEFAULT_PUB_PACK_SIZE)); + auto builder = IntPropertyBuilder(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE, DEFAULT_PUB_PACK_SIZE) + .setMinValue(1) + .setVisible(EvalValue(std::string("($") + PROPERTY_NAME_PUB_GROUP_VALUES + ")")) + .setDescription(fmt::format("Set the size of the sample pack when publishing grouped values. " + "By default it is set to {}.", + DEFAULT_PUB_PACK_SIZE)); defaultConfig.addProperty(builder.build()); } { diff --git a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp index 4ada713..5cf30a3 100644 --- a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp @@ -345,6 +345,58 @@ class MqttPublisherFbHelper : public DaqTestHelper return messages; } + template + std::vector + expectedMsgsForSharedTsArr(const std::string& signalName0, const std::string& signalName1, std::vector> data, size_t packSize) + { + std::vector msgs; + const std::string PUBLISHER_SINGLE_MSG = "{ : , : " + ", \"timestamp\" : }"; + + std::vector values; + std::vector tss; + + + for (size_t j = 0; j < data.size(); j += packSize) + { + std::ostringstream ossData; + std::ostringstream ossTs; + ossData << "["; + ossTs << "["; + for (size_t i = 0; i < packSize; ++i) + { + if (i > 0) + { + ossData << ", "; + ossTs << ", "; + } + const auto& [value, ts] = data[i + j]; + + ossData << valueToString(value, false); + ossTs << valueToString(ts * 1000, false); + } + ossData << "]"; + ossTs << "]"; + values.push_back(std::move(ossData).str()); + tss.push_back(std::move(ossTs).str()); + } + + std::vector messages; + { + for (size_t i = 0; i < values.size(); ++i) + { + auto msg = PUBLISHER_SINGLE_MSG; + msg = replacePlaceholder(msg, "", signalName0); + msg = replacePlaceholder(msg, "", signalName1); + msg = replacePlaceholder(msg, "", values[i], false); + msg = replacePlaceholder(msg, "", values[i], false); + msg = replacePlaceholder(msg, "", tss[i], false); + messages.push_back(std::move(msg)); + } + } + return messages; + } + template std::vector expectedMsgsForMultimsg(const std::string& signalName0, const std::string& signalName1, std::vector> data) @@ -380,32 +432,42 @@ class MqttPublisherFbHelper : public DaqTestHelper } template - static std::string replacePlaceholder(const std::string& jsonTemplate, const std::string& ph, const vT& value) + static std::string valueToString(const vT& value, bool quoteString = true) + { + std::string result; + if constexpr (std::is_same_v) + { + if (quoteString) + result = '"' + value + '"'; + else + result = value; + } + else if constexpr (std::is_same_v || std::is_same_v) + { + result = doubleToString(value, 6); + } + else + { + result = std::to_string(value); + } + return result; + } + + template + static std::string replacePlaceholder(const std::string& jsonTemplate, const std::string& ph, const vT& value, bool quoteString = true) { std::string result = jsonTemplate; size_t pos = result.find(ph); if (pos != std::string::npos) { - std::string replacement; - if constexpr (std::is_same_v) - { - replacement = '"' + value + '"'; - } - else if constexpr (std::is_same_v || std::is_same_v) - { - replacement = doubleToString(value, 6); - } - else - { - replacement = std::to_string(value); - } + std::string replacement = valueToString(value, quoteString); result.replace(pos, ph.length(), replacement); } return result; } template - static std::string replacePlaceholder(const std::string& jsonTemplate, const std::string& ph, const std::vector& values) + static std::string replacePlaceholder(const std::string& jsonTemplate, const std::string& ph, const std::vector& values, bool quoteString = true) { std::string result = jsonTemplate; size_t pos = result.find(ph); @@ -417,18 +479,7 @@ class MqttPublisherFbHelper : public DaqTestHelper { if (i > 0) replacement += ", "; - if constexpr (std::is_same_v) - { - replacement += '"' + values[i] + '"'; - } - else if constexpr (std::is_same_v || std::is_same_v) - { - replacement += doubleToString(values[i], 6); - } - else - { - replacement += std::to_string(values[i]); - } + replacement += valueToString(values[i], quoteString); } replacement += "]"; @@ -588,20 +639,6 @@ TEST_F(MqttPublisherFbTest, PropertyVisibility) daq::FunctionBlockTypePtr fbt = MqttPublisherFbImpl::CreateType(); daq::PropertyObjectPtr defaultConfig = fbt.createDefaultConfig(); - defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0); // Set to Single topic - defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_GROUP_VALUES, True); - ASSERT_TRUE(defaultConfig.getProperty(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE).getVisible()); - defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); // Set to Multi topic - ASSERT_FALSE(defaultConfig.getProperty(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE).getVisible()); - defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); // Set to Single topic - defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_GROUP_VALUES, True); - ASSERT_FALSE(defaultConfig.getProperty(PROPERTY_NAME_PUB_GROUP_VALUES_PACK_SIZE).getVisible()); - - defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0); // Set to Single topic - ASSERT_TRUE(defaultConfig.getProperty(PROPERTY_NAME_PUB_GROUP_VALUES).getVisible()); - defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); // Set to Multi topic - ASSERT_FALSE(defaultConfig.getProperty(PROPERTY_NAME_PUB_GROUP_VALUES).getVisible()); - defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 0); // Set to Single topic ASSERT_FALSE(defaultConfig.getProperty(PROPERTY_NAME_PUB_TOPIC_NAME).getVisible()); defaultConfig.setPropertyValue(PROPERTY_NAME_PUB_TOPIC_MODE, 1); // Set to Multi topic @@ -1259,6 +1296,63 @@ TEST_P(MqttPublisherFbPTest, TransferSharedTs) param); } +TEST_P(MqttPublisherFbPTest, TransferSharedTsArr) +{ + constexpr size_t sampleCnt = 15; + constexpr size_t packSize = 3; + H param = GetParam(); + std::visit( + [&](auto& help) + { + StartUp(); + const std::string topic = buildTopicName(); + ASSERT_NO_THROW(CreatePublisherFB(true, true, false, topic, packSize)); + + fb.getInputPorts()[0].connect(help.signal0); + fb.getInputPorts()[1].connect(help.signal1); + + auto signalList = List(); + fb->getSignals(&signalList); + auto reader = daq::PacketReader(signalList[0]); + std::vector dataToReceive; + ASSERT_TRUE(CreateSubscriber()); + + const auto data = help.generateTestData(sampleCnt); + const std::vector messages = + expectedMsgsForSharedTsArr(help.signal0.getGlobalId().toStdString(), help.signal1.getGlobalId().toStdString(), data, packSize); + auto ok = transfer(topic, messages, help, data); + ASSERT_TRUE(ok); + ASSERT_EQ(fb.getStatusContainer().getStatus("ComponentStatus"), + Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); + ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_SET_STATUS_NAME), + EnumerationWithIntValue(MQTT_PUB_FB_SET_STATUS_TYPE, + static_cast(MqttPublisherFbImpl::SettingStatus::Valid), + daqInstance.getContext().getTypeManager())); + ASSERT_EQ(fb.getStatusContainer().getStatus(MQTT_PUB_FB_PUB_STATUS_NAME), + EnumerationWithIntValue(MQTT_PUB_FB_PUB_STATUS_TYPE, + static_cast(MqttPublisherFbImpl::PublishingStatus::Ok), + daqInstance.getContext().getTypeManager())); + + while (!reader.getEmpty()) + { + auto packet = reader.read(); + if (const auto eventPacket = packet.asPtrOrNull(); eventPacket.assigned()) + { + continue; + } + if (const auto dataPacket = packet.asPtrOrNull(); dataPacket.assigned()) + { + std::vector readData(dataPacket.getDataSize()); + memcpy(readData.data(), dataPacket.getData(), dataPacket.getDataSize()); + dataToReceive.emplace_back(readData.cbegin(), readData.cend()); + } + } + ASSERT_EQ(messages.size(), dataToReceive.size()); + ASSERT_EQ(messages, dataToReceive); + }, + param); +} + TEST_P(MqttPublisherFbPTest, DISABLED_TransferMultimessage) { constexpr size_t sampleCnt = 15; From 70bcd9563f68a0fb8cc3499aa7a4619b86de5eec Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 28 Jan 2026 13:30:17 +0100 Subject: [PATCH 31/49] mqtt: SampleType::String for preview signals --- external/opendaq/CMakeLists.txt | 2 +- .../include/mqtt_streaming_module/constants.h | 1 + .../mqtt_subscriber_fb_impl.h | 1 + .../src/mqtt_publisher_fb_impl.cpp | 4 +-- .../src/mqtt_subscriber_fb_impl.cpp | 26 ++++++++++++++- .../tests/test_mqtt_subscriber_fb.cpp | 33 ++++++++++++++++++- .../src/MqttDataWrapper.cpp | 19 +++-------- 7 files changed, 67 insertions(+), 19 deletions(-) diff --git a/external/opendaq/CMakeLists.txt b/external/opendaq/CMakeLists.txt index cddbe03..56d9d00 100644 --- a/external/opendaq/CMakeLists.txt +++ b/external/opendaq/CMakeLists.txt @@ -3,7 +3,7 @@ set(OPENDAQ_ENABLE_TESTS false) FetchContent_Declare( opendaq GIT_REPOSITORY https://github.com/openDAQ/openDAQ.git - GIT_TAG origin/main + GIT_TAG origin/string-signals GIT_PROGRESS ON EXCLUDE_FROM_ALL SYSTEM diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index 9951ab5..ce22c16 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -34,6 +34,7 @@ static constexpr const char* PROPERTY_NAME_SUB_JSON_CONFIG_FILE = "JSONConfigFil static constexpr const char* PROPERTY_NAME_SUB_QOS = "QoS"; static constexpr const char* PROPERTY_NAME_SUB_TOPIC = "Topic"; static constexpr const char* PROPERTY_NAME_SUB_PREVIEW_SIGNAL = "EnablePreviewSignal"; +static constexpr const char* PROPERTY_NAME_SUB_PREVIEW_SIGNAL_IS_STRING = "MessageIsString"; static constexpr const char* PROPERTY_NAME_DEC_VALUE_NAME = "ValueKey"; static constexpr const char* PROPERTY_NAME_DEC_TS_NAME = "DomainKey"; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h index e9c39a6..7570432 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h @@ -66,6 +66,7 @@ class MqttSubscriberFbImpl final : public FunctionBlock std::vector nestedFunctionBlocks; SignalConfigPtr outputSignal; bool enablePreview; + bool previewIsString; std::atomic waitingForData; DAQ_MQTT_STREAM_MODULE_API void onSignalsMessage(const mqtt::MqttAsyncClient& subscriber, const mqtt::MqttMessage& msg); diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index 423529f..9e9fd91 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -222,7 +222,7 @@ void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) { if (config.topicMode == TopicMode::Single && !commonPreviewSignal.assigned()) { - const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::Binary).build(); + const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::String).build(); commonPreviewSignal = createAndAddSignal(fmt::format("{}{}", PUB_PREVIEW_SIGNAL_NAME, "Common"), signalDsc); } } @@ -250,7 +250,7 @@ void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) { if (!it->previewSignal.assigned() || (commonPreviewSignal.assigned() && commonPreviewSignal == it->previewSignal)) { - const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::Binary).build(); + const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::String).build(); it->previewSignal = createAndAddSignal(fmt::format("{}{}", PUB_PREVIEW_SIGNAL_NAME, size_t(it->index)), signalDsc); } } diff --git a/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp index aebc776..527b6fa 100644 --- a/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp @@ -116,6 +116,13 @@ FunctionBlockTypePtr MqttSubscriberFbImpl::CreateType() "By default it is set to false."); defaultConfig.addProperty(builder.build()); } + { + auto builder = BoolPropertyBuilder(PROPERTY_NAME_SUB_PREVIEW_SIGNAL_IS_STRING, False) + .setVisible(EvalValue(std::string("$") + PROPERTY_NAME_SUB_PREVIEW_SIGNAL)) + .setDescription("Specifies whether the preview signal data type is string. " + "By default it is set to false."); + defaultConfig.addProperty(builder.build()); + } { auto builder = StringPropertyBuilder(PROPERTY_NAME_SUB_JSON_CONFIG, String("")) @@ -181,6 +188,15 @@ void MqttSubscriberFbImpl::readProperties() this->enablePreview = previewProp.getValue(False); } } + + if (objPtr.hasProperty(PROPERTY_NAME_SUB_PREVIEW_SIGNAL_IS_STRING)) + { + auto isStringProp = objPtr.getPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL_IS_STRING).asPtrOrNull(); + if (isStringProp.assigned()) + { + this->previewIsString = isStringProp.getValue(False); + } + } } void MqttSubscriberFbImpl::readJsonConfig() @@ -289,7 +305,15 @@ void MqttSubscriberFbImpl::propertyChanged() if (enablePreview) { if (!outputSignal.assigned()) + { createSignals(); + } + else if ((outputSignal.getDescriptor().getSampleType() == SampleType::String) != previewIsString) + { + outputSignal.setDescriptor(DataDescriptorBuilderCopy(outputSignal.getDescriptor()) + .setSampleType(previewIsString ? SampleType::String : SampleType::Binary) + .build()); + } } else { @@ -405,7 +429,7 @@ void MqttSubscriberFbImpl::createSignals() auto lock = this->getRecursiveConfigLock(); // ??? if (enablePreview) { - const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::Binary).build(); + const auto signalDsc = DataDescriptorBuilder().setSampleType(previewIsString ? SampleType::String : SampleType::Binary).build(); outputSignal = createAndAddSignal(DEFAULT_VALUE_SIGNAL_LOCAL_ID, signalDsc); } } diff --git a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp index 02d7a5c..c0b1028 100644 --- a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp +++ b/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp @@ -90,7 +90,7 @@ TEST_F(MqttSubscriberFbTest, DefaultConfig) ASSERT_TRUE(defaultConfig.assigned()); - ASSERT_EQ(defaultConfig.getAllProperties().getCount(), 5u); + EXPECT_EQ(defaultConfig.getAllProperties().getCount(), 6u); ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_JSON_CONFIG)); ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_JSON_CONFIG).getValueType(), CoreType::ctString); @@ -111,6 +111,22 @@ TEST_F(MqttSubscriberFbTest, DefaultConfig) ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_PREVIEW_SIGNAL)); ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_PREVIEW_SIGNAL).getValueType(), CoreType::ctBool); ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL).asPtr().getValue(False), False); + + ASSERT_TRUE(defaultConfig.hasProperty(PROPERTY_NAME_SUB_PREVIEW_SIGNAL_IS_STRING)); + ASSERT_EQ(defaultConfig.getProperty(PROPERTY_NAME_SUB_PREVIEW_SIGNAL_IS_STRING).getValueType(), CoreType::ctBool); + ASSERT_EQ(defaultConfig.getPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL_IS_STRING).asPtr().getValue(False), False); +} + +TEST_F(MqttSubscriberFbTest, PropertyVisibility) +{ + daq::DictPtr fbTypes; + daq::FunctionBlockTypePtr fbt = MqttSubscriberFbImpl::CreateType(); + daq::PropertyObjectPtr defaultConfig = fbt.createDefaultConfig(); + + defaultConfig.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, True); + ASSERT_TRUE(defaultConfig.getProperty(PROPERTY_NAME_SUB_PREVIEW_SIGNAL_IS_STRING).getVisible()); + defaultConfig.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, False); + ASSERT_FALSE(defaultConfig.getProperty(PROPERTY_NAME_SUB_PREVIEW_SIGNAL_IS_STRING).getVisible()); } TEST_F(MqttSubscriberFbTest, Config) @@ -170,6 +186,21 @@ TEST_F(MqttSubscriberFbTest, CreationWithCustomConfig) Enumeration("ComponentStatusType", "Ok", daqInstance.getContext().getTypeManager())); } +TEST_F(MqttSubscriberFbTest, PreviewSignal) +{ + StartUp(); + daq::FunctionBlockPtr subFb; + auto config = clientMqttFb.getAvailableFunctionBlockTypes().get(SUB_FB_NAME).createDefaultConfig(); + config.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL, True); + config.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL_IS_STRING, False); + config.setPropertyValue(PROPERTY_NAME_SUB_TOPIC, buildTopicName()); + ASSERT_NO_THROW(subFb = clientMqttFb.addFunctionBlock(SUB_FB_NAME, config)); + ASSERT_EQ(subFb.getSignals().getCount(), 1u); + EXPECT_EQ(subFb.getSignals()[0].getDescriptor().getSampleType(), daq::SampleType::Binary); + subFb.setPropertyValue(PROPERTY_NAME_SUB_PREVIEW_SIGNAL_IS_STRING, True); + EXPECT_EQ(subFb.getSignals()[0].getDescriptor().getSampleType(), daq::SampleType::String); +} + TEST_F(MqttSubscriberFbTest, SubscriptionStatusWaitingForData) { StartUp(); diff --git a/mqtt_streaming_protocol/src/MqttDataWrapper.cpp b/mqtt_streaming_protocol/src/MqttDataWrapper.cpp index 6bfc917..5701dd4 100644 --- a/mqtt_streaming_protocol/src/MqttDataWrapper.cpp +++ b/mqtt_streaming_protocol/src/MqttDataWrapper.cpp @@ -564,25 +564,16 @@ bool MqttDataWrapper::isTypeTheSame(daq::SampleType sampleType) } template -daq::DataPacketPtr MqttDataWrapper::buildDataPacket(daq::GenericSignalConfigPtr<> signalConfig, const T& value, const daq::DataPacketPtr domainPacket) +daq::DataPacketPtr +MqttDataWrapper::buildDataPacket(daq::GenericSignalConfigPtr<> signalConfig, const T& value, const daq::DataPacketPtr domainPacket) { const auto curType = signalConfig.getDescriptor().getSampleType(); using ActualType = sample_type_t; if (!isTypeTheSame(curType)) { - if constexpr (std::is_same_v) - { - // because daq::SampleType::String is not implemented properly, we use Binary type for string data - // daq::SampleType::BinaryData != daq::SampleType::String - auto descriptor = DataDescriptorBuilderCopy(signalConfig.getDescriptor()).setSampleType(daq::SampleType::Binary).build(); - signalConfig.setDescriptor(descriptor); - } - else - { - auto descriptor = - DataDescriptorBuilderCopy(signalConfig.getDescriptor()).setSampleType(daq::SampleTypeFromType::SampleType).build(); - signalConfig.setDescriptor(descriptor); - } + auto descriptor = + DataDescriptorBuilderCopy(signalConfig.getDescriptor()).setSampleType(daq::SampleTypeFromType::SampleType).build(); + signalConfig.setDescriptor(descriptor); } auto dataPacket = createEmptyDataPacket(signalConfig, domainPacket, value); From 49a255f85cb5849bb881852849eafbc1c5a88b5d Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 28 Jan 2026 15:04:26 +0100 Subject: [PATCH 32/49] mqtt: use existing ComponentStatusType for client FB --- .../include/mqtt_streaming_module/constants.h | 8 +-- .../mqtt_client_fb_impl.h | 12 +--- .../mqtt_streaming_module/status_adaptor.h | 72 +++++++++++++++++++ mqtt_streaming_module/src/CMakeLists.txt | 2 + .../src/mqtt_client_fb_impl.cpp | 15 ++-- 5 files changed, 85 insertions(+), 24 deletions(-) create mode 100644 mqtt_streaming_module/include/mqtt_streaming_module/status_adaptor.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h index ce22c16..c241b8e 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/constants.h @@ -63,10 +63,10 @@ static const char* MQTT_LOCAL_SUB_FB_ID_PREFIX = "MQTTSubscriberFB"; static const char* MQTT_LOCAL_JSON_DECODER_FB_ID_PREFIX = "MQTTJSONDecoderFB"; -static const char* MQTT_CLIENT_FB_CON_STATUS_TYPE = "BrokerConnectionStatusType"; -static const char* MQTT_PUB_FB_SIG_STATUS_TYPE = "MQTTSignalStatusType"; -static const char* MQTT_PUB_FB_PUB_STATUS_TYPE = "MQTTPublishingStatusType"; -static const char* MQTT_PUB_FB_SET_STATUS_TYPE = "MQTTSettingStatusType"; +static const char* MQTT_CLIENT_FB_CON_STATUS_TYPE = "DAQ_MQTT_ConnectionStatusType"; +static const char* MQTT_PUB_FB_SIG_STATUS_TYPE = "DAQ_MQTT_SignalStatusType"; +static const char* MQTT_PUB_FB_PUB_STATUS_TYPE = "DAQ_MQTT_PublishingStatusType"; +static const char* MQTT_PUB_FB_SET_STATUS_TYPE = "DAQ_MQTT_SettingStatusType"; static const char* MQTT_CLIENT_FB_CON_STATUS_NAME = "ConnectionStatus"; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_client_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_client_fb_impl.h index 76f93cd..2cc8e52 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_client_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_client_fb_impl.h @@ -21,20 +21,13 @@ #include #include #include -#include +#include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE class MqttClientFbImpl : public FunctionBlock { - enum class ConnectionStatus : EnumType - { - Connected = 0, - Reconnecting, - Disconnected - }; - public: explicit MqttClientFbImpl(const ContextPtr& ctx, const ComponentPtr& parent, @@ -45,7 +38,6 @@ class MqttClientFbImpl : public FunctionBlock protected: static std::atomic localIndex; static std::string generateLocalId(); - static std::vector> connectionStatusMap; void removed() override; @@ -61,7 +53,7 @@ class MqttClientFbImpl : public FunctionBlock DictObjectPtr nestedFbTypes; - StatusHelper connectionStatus; + StatusAdaptor connectionStatus; std::shared_ptr subscriber; Mqtt::Utils::Settings::MqttConnectionSettings connectionSettings; diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/status_adaptor.h b/mqtt_streaming_module/include/mqtt_streaming_module/status_adaptor.h new file mode 100644 index 0000000..2c0a4fe --- /dev/null +++ b/mqtt_streaming_module/include/mqtt_streaming_module/status_adaptor.h @@ -0,0 +1,72 @@ +/* + * Copyright 2022-2025 openDAQ d.o.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +#include +#include +#include + +BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE + +class StatusAdaptor +{ +public: + StatusAdaptor(const std::string typeName, + const std::string statusName, + ComponentStatusContainerPtr statusContainer, + std::string initState, + TypeManagerPtr typeManager) + : typeName(typeName), + statusName(statusName), + statusContainer(statusContainer), + typeManager(typeManager) + { + currentStatus = Enumeration(typeName, initState, typeManager); + currentMessage = ""; + statusContainer.template asPtr(true).addStatus(statusName, currentStatus); + } + + bool setStatus(const std::string& status, const std::string& message = "") + { + std::scoped_lock lock(statusMutex); + const auto newStatus = Enumeration(typeName, String(status), typeManager); + bool changed = (newStatus != currentStatus || message != currentMessage); + if (changed) + { + currentStatus = newStatus; + currentMessage = message; + statusContainer.template asPtr(true).setStatusWithMessage(statusName, currentStatus, message); + } + return changed; + } + std::string getStatus() + { + std::scoped_lock lock(statusMutex); + return currentStatus.getValue().toStdString(); + } + +private: + const std::string typeName; + const std::string statusName; + std::string currentMessage; + ComponentStatusContainerPtr statusContainer; + EnumerationPtr currentStatus; + TypeManagerPtr typeManager; + std::mutex statusMutex; +}; + +END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/src/CMakeLists.txt b/mqtt_streaming_module/src/CMakeLists.txt index 78b3f60..b289ddd 100644 --- a/mqtt_streaming_module/src/CMakeLists.txt +++ b/mqtt_streaming_module/src/CMakeLists.txt @@ -19,6 +19,7 @@ set(SRC_Include common.h signal_arr_atomic_sample_handler.h types.h status_helper.h + status_adaptor.h ) set(SRC_Srcs module_dll.cpp @@ -42,6 +43,7 @@ source_group("common" FILES ${MODULE_HEADERS_DIR}/common.h ${MODULE_HEADERS_DIR}/helper.h ${MODULE_HEADERS_DIR}/types.h ${MODULE_HEADERS_DIR}/status_helper.h + ${MODULE_HEADERS_DIR}/status_adaptor.h helper.cpp ) diff --git a/mqtt_streaming_module/src/mqtt_client_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_client_fb_impl.cpp index bc49d14..103f6df 100644 --- a/mqtt_streaming_module/src/mqtt_client_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_client_fb_impl.cpp @@ -10,20 +10,15 @@ BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE constexpr int MQTT_CLIENT_SYNC_DISCONNECT_TOUT = 3000; std::atomic MqttClientFbImpl::localIndex = 0; -std::vector> MqttClientFbImpl::connectionStatusMap = - {{ConnectionStatus::Connected, "Connected"}, - {ConnectionStatus::Reconnecting, "Reconnecting"}, - {ConnectionStatus::Disconnected, "Disconnected"}}; MqttClientFbImpl::MqttClientFbImpl(const ContextPtr& ctx, const ComponentPtr& parent, const PropertyObjectPtr& config) : FunctionBlock(CreateType(), ctx, parent, generateLocalId()), subscriber(std::make_shared()), connectTimeout(0), - connectionStatus(MQTT_CLIENT_FB_CON_STATUS_TYPE, + connectionStatus("ConnectionStatusType", MQTT_CLIENT_FB_CON_STATUS_NAME, statusContainer, - connectionStatusMap, - ConnectionStatus::Disconnected, + "Reconnecting", context.getTypeManager()) { initComponentStatus(); @@ -89,7 +84,7 @@ void MqttClientFbImpl::initMqttSubscriber() bool expected = false; if (connectedDone.compare_exchange_strong(expected, true)) { - connectionStatus.setStatus(ConnectionStatus::Connected); + connectionStatus.setStatus("Connected"); connectedPromise.set_value(true); std::scoped_lock lock(componentStatusSync); setComponentStatus(ComponentStatus::Ok); @@ -105,7 +100,7 @@ void MqttClientFbImpl::initConnectionStatus() subscriber->setConnectionLostCb( [this](std::string msg) { - connectionStatus.setStatus(ConnectionStatus::Reconnecting, msg); + connectionStatus.setStatus("Reconnecting", msg); std::scoped_lock lock(componentStatusSync); setComponentStatusWithMessage(ComponentStatus::Error, "Connection lost"); }); @@ -166,7 +161,7 @@ bool MqttClientFbImpl::waitForConnection(const int timeoutMs) subscriber->setConnectedCb( [this] { - connectionStatus.setStatus(ConnectionStatus::Connected); + connectionStatus.setStatus("Connected"); std::scoped_lock lock(componentStatusSync); setComponentStatus(ComponentStatus::Ok); }); From 56d308a3e5f731fbeb8cf97317f01ea0a86e3c53 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 28 Jan 2026 18:05:19 +0100 Subject: [PATCH 33/49] mqtt: internal buffer for AtomicSignalSampleArrayHandler --- .../atomic_signal_sample_arr_handler.h | 18 +++++++ .../group_signal_shared_ts_arr_handler.h | 1 + .../include/mqtt_streaming_module/types.h | 8 +-- .../src/atomic_signal_sample_arr_handler.cpp | 53 +++++++++++++++---- .../src/group_signal_shared_ts_handler.cpp | 2 +- .../src/mqtt_publisher_fb_impl.cpp | 2 +- 6 files changed, 67 insertions(+), 17 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h index f18f510..d017957 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h @@ -18,22 +18,40 @@ #include #include +#include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE class AtomicSignalSampleArrayHandler : public AtomicSignalAtomicSampleHandler { public: + + struct SignalBuffer + { + std::list data; + size_t dataSize = 0; + size_t offset = 0; + void clear() + { + data.clear(); + dataSize = 0; + offset = 0; + } + }; explicit AtomicSignalSampleArrayHandler(WeakRefPtr parentFb, SignalValueJSONKey signalNamesMode, size_t packSize); + ProcedureStatus signalListChanged(std::vector& signalContexts) override; std::string getSchema() override; protected: size_t packSize; + std::unordered_map signalBuffers; MqttData processSignalContext(SignalContext& signalContext) override; MqttDataSample processDataPackets(SignalContext& signalContext); std::string toString(const std::string valueFieldName, SignalContext& signalContext); std::pair getSample(SignalContext& signalContext); + void + processSignalDescriptorChanged(SignalContext& signalCtx, const DataDescriptorPtr& valueSigDesc, const DataDescriptorPtr& domainSigDesc); }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h index 758076c..d06d7c8 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h @@ -21,6 +21,7 @@ #include #include #include +#include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/types.h b/mqtt_streaming_module/include/mqtt_streaming_module/types.h index 0fb7c2a..b940917 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/types.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/types.h @@ -4,7 +4,6 @@ #include #include #include -#include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE @@ -44,10 +43,11 @@ struct SignalContext { size_t index; InputPortConfigPtr inputPort; - std::list data; - size_t dataSize = 0; - size_t offset = 0; SignalConfigPtr previewSignal; + SignalContext(size_t index, InputPortConfigPtr inputPort, SignalConfigPtr previewSignal) + : index(index), inputPort(inputPort), previewSignal(previewSignal) + { + } }; struct ProcedureStatus diff --git a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp index bfbad32..19db49c 100644 --- a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp @@ -4,6 +4,7 @@ #include #include #include +#include BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE @@ -13,6 +14,27 @@ AtomicSignalSampleArrayHandler::AtomicSignalSampleArrayHandler(WeakRefPtr& signalContexts) +{ + std::set set; + for (const auto& buf : signalBuffers) + set.insert(buf.first); + + for (auto& sigCtx : signalContexts) + { + const auto signal = sigCtx.inputPort.getSignal(); + if (!signal.assigned()) + continue; + auto& buffer = signalBuffers[signal.getGlobalId().toStdString()]; + buffer.clear(); + set.erase(signal.getGlobalId().toStdString()); + } + for (const auto& el : set) + signalBuffers.erase(el); + + return ProcedureStatus{true, {}}; +} + std::string AtomicSignalSampleArrayHandler::getSchema() { if (packSize == 1) @@ -53,9 +75,10 @@ MqttData AtomicSignalSampleArrayHandler::processSignalContext(SignalContext& sig else if (packet.getType() == PacketType::Data) { auto dataPacket = packet.asPtr(); - signalContext.data.push_back(dataPacket); - signalContext.dataSize += dataPacket.getSampleCount(); - while (signalContext.dataSize >= packSize) + const auto sigGlobalId = signalContext.inputPort.getSignal().getGlobalId().toStdString(); + signalBuffers[sigGlobalId].data.push_back(dataPacket); + signalBuffers[sigGlobalId].dataSize += dataPacket.getSampleCount(); + while (signalBuffers[sigGlobalId].dataSize >= packSize) messages.emplace_back(processDataPackets(signalContext)); } @@ -67,19 +90,27 @@ MqttData AtomicSignalSampleArrayHandler::processSignalContext(SignalContext& sig std::pair AtomicSignalSampleArrayHandler::getSample(SignalContext& signalContext) { - if (signalContext.data.empty()) + const auto sigGlobalId = signalContext.inputPort.getSignal().getGlobalId().toStdString(); + if (signalBuffers[sigGlobalId].data.empty()) return {nullptr, 0}; - auto dataPacket = signalContext.data.front(); - size_t offset = signalContext.offset++; - signalContext.dataSize--; - if (signalContext.offset == dataPacket.getSampleCount()) + auto dataPacket = signalBuffers[sigGlobalId].data.front(); + size_t offset = signalBuffers[sigGlobalId].offset++; + signalBuffers[sigGlobalId].dataSize--; + if (signalBuffers[sigGlobalId].offset == dataPacket.getSampleCount()) { - signalContext.data.pop_front(); - signalContext.offset = 0; + signalBuffers[sigGlobalId].data.pop_front(); + signalBuffers[sigGlobalId].offset = 0; } return {dataPacket, offset}; } +void AtomicSignalSampleArrayHandler::processSignalDescriptorChanged(SignalContext& signalCtx, + const DataDescriptorPtr& valueSigDesc, + const DataDescriptorPtr& domainSigDesc) +{ + signalBuffers[signalCtx.inputPort.getSignal().getGlobalId().toStdString()].clear(); +} + std::string AtomicSignalSampleArrayHandler::toString(const std::string valueFieldName, SignalContext& signalContext) { std::ostringstream dataOss; @@ -123,7 +154,7 @@ std::string AtomicSignalSampleArrayHandler::toString(const std::string valueFiel MqttDataSample AtomicSignalSampleArrayHandler::processDataPackets(SignalContext& signalContext) { - if (signalContext.data.empty()) + if (signalBuffers[signalContext.inputPort.getSignal().getGlobalId().toStdString()].data.empty()) return MqttDataSample{nullptr, "", ""}; const auto signal = signalContext.inputPort.getSignal(); std::string valueFieldName = buildValueFieldName(signalNamesMode, signal); diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp index 4fd9bd5..061c624 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp @@ -37,7 +37,7 @@ MqttData GroupSignalSharedTsHandler::processSignalContexts(std::vector 0) + if (count > 0) { const auto tsStruct = domainToTs(status); for (SizeT sampleCnt = 0; sampleCnt < count; ++sampleCnt) diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index 9e9fd91..2ff4f73 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -276,7 +276,7 @@ void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) if (reassignPorts) { const auto inputPort = createAndAddInputPort(fmt::format("Input{}", size_t(inputPortCount)), PacketReadyNotification::SameThread); - signalContexts.emplace_back(SignalContext{size_t(inputPortCount++), inputPort, {}, 0, 0, nullptr}); + signalContexts.emplace_back(size_t(inputPortCount++), inputPort, nullptr); } } From 1ce672b1c557305c155542f684b5ee70a692048a Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 28 Jan 2026 18:07:16 +0100 Subject: [PATCH 34/49] mqtt: template for tracking descriptor changes in publisher handlers --- .../src/atomic_signal_atomic_sample_handler.cpp | 1 + .../src/atomic_signal_sample_arr_handler.cpp | 1 + .../src/group_signal_shared_ts_arr_handler.cpp | 14 ++++++++++++++ .../src/group_signal_shared_ts_handler.cpp | 14 ++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp index fbbd9ea..0f6e19e 100644 --- a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp @@ -104,6 +104,7 @@ MqttData AtomicSignalAtomicSampleHandler::processSignalContext(SignalContext& si DataDescriptorPtr valueSignalDescriptor = eventPacket.getParameters().get(event_packet_param::DATA_DESCRIPTOR); DataDescriptorPtr domainSignalDescriptor = eventPacket.getParameters().get(event_packet_param::DOMAIN_DATA_DESCRIPTOR); processSignalDescriptorChanged(signalContext, valueSignalDescriptor, domainSignalDescriptor); + break; } } else if (packet.getType() == PacketType::Data) diff --git a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp index 19db49c..e0ca563 100644 --- a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp @@ -70,6 +70,7 @@ MqttData AtomicSignalSampleArrayHandler::processSignalContext(SignalContext& sig DataDescriptorPtr valueSignalDescriptor = eventPacket.getParameters().get(event_packet_param::DATA_DESCRIPTOR); DataDescriptorPtr domainSignalDescriptor = eventPacket.getParameters().get(event_packet_param::DOMAIN_DATA_DESCRIPTOR); processSignalDescriptorChanged(signalContext, valueSignalDescriptor, domainSignalDescriptor); + break; } } else if (packet.getType() == PacketType::Data) diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp index 59ed734..e5f93a8 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp @@ -40,6 +40,20 @@ MqttData GroupSignalSharedTsArrHandler::processSignalContexts(std::vector fields; diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp index 061c624..267b479 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp @@ -57,6 +57,20 @@ MqttData GroupSignalSharedTsHandler::processSignalContexts(std::vector Date: Thu, 29 Jan 2026 10:42:42 +0100 Subject: [PATCH 35/49] mqtt: tracking descriptor changes in publisher handlers --- .../group_signal_shared_ts_handler.h | 2 + .../include/mqtt_streaming_module/types.h | 12 +++++- .../atomic_signal_atomic_sample_handler.cpp | 6 +-- .../src/atomic_signal_sample_arr_handler.cpp | 3 +- .../group_signal_shared_ts_arr_handler.cpp | 16 +------- .../src/group_signal_shared_ts_handler.cpp | 37 +++++++++++-------- .../src/mqtt_publisher_fb_impl.cpp | 7 +++- .../src/signal_arr_atomic_sample_handler.cpp | 2 +- 8 files changed, 49 insertions(+), 36 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h index 3efd203..f335b6f 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h @@ -54,6 +54,7 @@ class GroupSignalSharedTsHandler : public HandlerBase const size_t buffersSize; const std::string topic; std::vector dataBuffers; + bool firstDescriptorChange; daq::MultiReaderPtr reader; std::mutex sync; @@ -68,6 +69,7 @@ class GroupSignalSharedTsHandler : public HandlerBase void deallocateBuffers(); static std::string messageFromFields(const std::vector& fields); static TimestampTickStruct domainToTs(const MultiReaderStatusPtr status); + bool processEvents(const daq::MultiReaderStatusPtr& status); // true if descriptor changed or invalid reader }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/types.h b/mqtt_streaming_module/include/mqtt_streaming_module/types.h index b940917..35cd77c 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/types.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/types.h @@ -13,7 +13,17 @@ struct MqttDataSample { std::string message; }; -using MqttData = std::vector; +struct MqttData { + std::vector data; + bool needRevalidation = false; + + void merge(MqttData&& other) + { + data.reserve(data.size() + other.data.size()); + data.insert(data.end(), std::make_move_iterator(other.data.begin()), std::make_move_iterator(other.data.end())); + needRevalidation = needRevalidation || other.needRevalidation; + } +}; enum class TopicMode { PerSignal = 0, diff --git a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp index 0f6e19e..ea163d8 100644 --- a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp @@ -19,8 +19,7 @@ MqttData AtomicSignalAtomicSampleHandler::processSignalContexts(std::vector(); for (size_t i = 0; i < dataPacket.getSampleCount(); ++i) - messages.emplace_back(processDataPacket(signalContext, dataPacket, i)); + messages.data.emplace_back(processDataPacket(signalContext, dataPacket, i)); } packet = conn.dequeue(); diff --git a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp index e0ca563..65b433b 100644 --- a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp +++ b/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp @@ -70,6 +70,7 @@ MqttData AtomicSignalSampleArrayHandler::processSignalContext(SignalContext& sig DataDescriptorPtr valueSignalDescriptor = eventPacket.getParameters().get(event_packet_param::DATA_DESCRIPTOR); DataDescriptorPtr domainSignalDescriptor = eventPacket.getParameters().get(event_packet_param::DOMAIN_DATA_DESCRIPTOR); processSignalDescriptorChanged(signalContext, valueSignalDescriptor, domainSignalDescriptor); + messages.needRevalidation = true; break; } } @@ -80,7 +81,7 @@ MqttData AtomicSignalSampleArrayHandler::processSignalContext(SignalContext& sig signalBuffers[sigGlobalId].data.push_back(dataPacket); signalBuffers[sigGlobalId].dataSize += dataPacket.getSampleCount(); while (signalBuffers[sigGlobalId].dataSize >= packSize) - messages.emplace_back(processDataPackets(signalContext)); + messages.data.emplace_back(processDataPackets(signalContext)); } diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp index e5f93a8..f335888 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp @@ -40,19 +40,7 @@ MqttData GroupSignalSharedTsArrHandler::processSignalContexts(std::vector& signalContexts) { ProcedureStatus status{true, {}}; @@ -324,7 +331,7 @@ void GroupSignalSharedTsHandler::createReaderInternal(const std::vectorparentFb.getRef(); if (parentFb.assigned()) diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index 2ff4f73..92a13c7 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -491,6 +491,11 @@ void MqttPublisherFbImpl::readerLoop() if (hasSignalError == false && hasSettingError == false) { msgs = handler->processSignalContexts(signalContexts); + if (msgs.needRevalidation) + { + validateInputPorts(); + updateStatuses(); + } } sendMessages(msgs); updatePublishingStatus(); @@ -501,7 +506,7 @@ void MqttPublisherFbImpl::readerLoop() void MqttPublisherFbImpl::sendMessages(const MqttData& data) { - for (const auto& [signal, topic, msg] : data) + for (const auto& [signal, topic, msg] : data.data) { if (signal.assigned()) { diff --git a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp index 35d10f4..6e3a31d 100644 --- a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp +++ b/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp @@ -50,7 +50,7 @@ MqttData SignalArrayAtomicSampleHandler::processSignalContexts(std::vector Date: Fri, 30 Jan 2026 14:33:13 +0100 Subject: [PATCH 36/49] mqtt: CoreEvent checking for DataDescriptorChanged for connected signals --- .../mqtt_publisher_fb_impl.h | 6 ++ .../src/mqtt_publisher_fb_impl.cpp | 64 ++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h index 42c7f4a..9749ca1 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h @@ -77,10 +77,13 @@ class MqttPublisherFbImpl final : public FunctionBlock mqtt::MqttDataWrapper jsonDataWorker; PublisherFbConfig config; std::vector signalContexts; + std::unordered_map> signalMap; std::atomic inputPortCount; std::thread readerThread; std::atomic running; std::atomic hasSignalError; + std::atomic signalDescriptorChanged; + std::atomic signalAttributeChanged; std::atomic hasSettingError; std::atomic hasEmptyTopic; std::vector signalErrors; @@ -95,6 +98,7 @@ class MqttPublisherFbImpl final : public FunctionBlock SignalConfigPtr commonPreviewSignal; static std::string generateLocalId(); + void coreEventCallback(ComponentPtr& sender, CoreEventArgsPtr& eventArgs); void updateComponentStatus(); void updatePublishingStatus(); std::string buildPublishingStatusMessage(); @@ -102,6 +106,8 @@ class MqttPublisherFbImpl final : public FunctionBlock void readProperties(); void propertyChanged(); void updatePortsAndSignals(bool reassignPorts); + void updateCoreEventCallbacks(); + void clearCoreEventCallbacks(const std::unordered_map>& signalMap); void updateStatuses(); void validateInputPorts(); void updateTopics(); diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index 92a13c7..b8088dd 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -21,6 +21,8 @@ MqttPublisherFbImpl::MqttPublisherFbImpl(const ContextPtr& ctx, inputPortCount(0), running(true), hasSignalError(false), + signalDescriptorChanged(false), + signalAttributeChanged(false), hasSettingError(false), signalStatus(MQTT_PUB_FB_SIG_STATUS_TYPE, MQTT_PUB_FB_SIG_STATUS_NAME, @@ -72,6 +74,8 @@ void MqttPublisherFbImpl::removed() running = false; readerThread.join(); } + clearCoreEventCallbacks(signalMap); + signalMap.clear(); { auto lock = this->getRecursiveConfigLock(); auto lockProcessing = std::scoped_lock(processingMutex); @@ -226,7 +230,6 @@ void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) commonPreviewSignal = createAndAddSignal(fmt::format("{}{}", PUB_PREVIEW_SIGNAL_NAME, "Common"), signalDsc); } } - for (auto it = signalContexts.begin(); it != signalContexts.end();) { it->inputPort.setListener(this->template borrowPtr()); @@ -278,6 +281,42 @@ void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) const auto inputPort = createAndAddInputPort(fmt::format("Input{}", size_t(inputPortCount)), PacketReadyNotification::SameThread); signalContexts.emplace_back(size_t(inputPortCount++), inputPort, nullptr); } + + updateCoreEventCallbacks(); +} + +void MqttPublisherFbImpl::updateCoreEventCallbacks() +{ + auto signalMapCopy = std::move(signalMap); + signalMap.clear(); + for (auto it = signalContexts.cbegin(); it != signalContexts.cend();) + { + auto sig = it->inputPort.getSignal(); + if (sig.assigned()) + { + if (signalMapCopy.find(sig.getGlobalId().toStdString()) == signalMapCopy.end()) + { + sig.asPtr().getOnComponentCoreEvent() += event(&MqttPublisherFbImpl::coreEventCallback); + signalMap.insert(std::pair(sig.getGlobalId().toStdString(), WeakRefPtr(sig))); + } + else + { + signalMap.insert(std::move(signalMapCopy.extract(sig.getGlobalId().toStdString()))); + } + } + ++it; + } + clearCoreEventCallbacks(signalMapCopy); +} + +void MqttPublisherFbImpl::clearCoreEventCallbacks(const std::unordered_map>& signalMap) +{ + for (const auto& [_, weakSig] : signalMap) + { + auto sig = weakSig.getRef(); + if (sig.assigned()) + sig.asPtr().getOnComponentCoreEvent() -= event(&MqttPublisherFbImpl::coreEventCallback); + } } void MqttPublisherFbImpl::updateStatuses() @@ -488,6 +527,13 @@ void MqttPublisherFbImpl::readerLoop() { MqttData msgs; auto lockProcessing = std::scoped_lock(processingMutex); + if ((hasSignalError && signalDescriptorChanged) || signalAttributeChanged) + { + validateInputPorts(); + updateStatuses(); + signalDescriptorChanged = false; + signalAttributeChanged = false; + } if (hasSignalError == false && hasSettingError == false) { msgs = handler->processSignalContexts(signalContexts); @@ -528,6 +574,22 @@ std::string MqttPublisherFbImpl::generateLocalId() return std::string(MQTT_LOCAL_PUB_FB_ID_PREFIX + std::to_string(localIndex++)); } +inline void MqttPublisherFbImpl::coreEventCallback(ComponentPtr& sender, CoreEventArgsPtr& eventArgs) +{ + const auto sig = sender.asPtrOrNull(); + if (sig == nullptr) + return; + auto lockProcessing = std::scoped_lock(processingMutex); + const auto it = signalMap.find(sig.getGlobalId()); + if (it != signalMap.end()) + { + if (eventArgs.getEventId() == static_cast(CoreEventId::DataDescriptorChanged) && hasSignalError) + signalDescriptorChanged = true; + else if (eventArgs.getEventId() == static_cast(CoreEventId::AttributeChanged) && eventArgs.getParameters().hasKey("Name")) + signalAttributeChanged = true; + } +}; + void MqttPublisherFbImpl::updateComponentStatus() { std::string componentMsg; From 0b27c2201e4247b281895f4f4907b6267c0ee022 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 30 Jan 2026 14:34:31 +0100 Subject: [PATCH 37/49] mqtt: proper initBuilders for GroupSignalSharedTsArrHandler --- .../group_signal_shared_ts_arr_handler.h | 9 ++++++++- .../src/group_signal_shared_ts_arr_handler.cpp | 5 +++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h index d06d7c8..1624c2f 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h @@ -90,6 +90,13 @@ class StringDataBuilder return values.empty(); } + void clear() + { + reset(); + values.clear(); + offset = 0; + } + protected: const size_t packSize; std::list values; @@ -176,7 +183,7 @@ class GroupSignalSharedTsArrHandler : public GroupSignalSharedTsHandler std::vector dataBuilders; StringTsBuilder tsBuilder; - void initDataBuilders(const size_t size); + void initBuilders(const size_t size); }; END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp b/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp index f335888..9960381 100644 --- a/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp +++ b/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp @@ -59,7 +59,7 @@ MqttData GroupSignalSharedTsArrHandler::processSignalContexts(std::vector& signalContexts) { auto status = GroupSignalSharedTsHandler::signalListChanged(signalContexts); - initDataBuilders(signalContexts.size() - 1); + initBuilders(signalContexts.size() - 1); return status; } @@ -88,10 +88,11 @@ std::string GroupSignalSharedTsArrHandler::getSchema() } } -void GroupSignalSharedTsArrHandler::initDataBuilders(const size_t size) +void GroupSignalSharedTsArrHandler::initBuilders(const size_t size) { if (dataBuilders.empty() || dataBuilders.size() != size) { + tsBuilder.clear(); dataBuilders.clear(); dataBuilders.reserve(size); for (size_t i = 0; i < size; ++i) From 78f2dafd3aa8d50b08c8c16e7ad7e42da7109d95 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 30 Jan 2026 16:47:59 +0100 Subject: [PATCH 38/49] mqtt: improving port numeration for Publisher FB --- .../mqtt_publisher_fb_impl.h | 2 +- .../include/mqtt_streaming_module/types.h | 7 +- .../src/mqtt_publisher_fb_impl.cpp | 96 ++++++++++--------- 3 files changed, 58 insertions(+), 47 deletions(-) diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h index 9749ca1..15313d4 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h @@ -78,7 +78,6 @@ class MqttPublisherFbImpl final : public FunctionBlock PublisherFbConfig config; std::vector signalContexts; std::unordered_map> signalMap; - std::atomic inputPortCount; std::thread readerThread; std::atomic running; std::atomic hasSignalError; @@ -106,6 +105,7 @@ class MqttPublisherFbImpl final : public FunctionBlock void readProperties(); void propertyChanged(); void updatePortsAndSignals(bool reassignPorts); + void clearPorts(); void updateCoreEventCallbacks(); void clearCoreEventCallbacks(const std::unordered_map>& signalMap); void updateStatuses(); diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/types.h b/mqtt_streaming_module/include/mqtt_streaming_module/types.h index 35cd77c..dd52ee3 100644 --- a/mqtt_streaming_module/include/mqtt_streaming_module/types.h +++ b/mqtt_streaming_module/include/mqtt_streaming_module/types.h @@ -51,13 +51,18 @@ struct PublisherFbConfig { struct SignalContext { - size_t index; InputPortConfigPtr inputPort; SignalConfigPtr previewSignal; SignalContext(size_t index, InputPortConfigPtr inputPort, SignalConfigPtr previewSignal) : index(index), inputPort(inputPort), previewSignal(previewSignal) { } + size_t getIndex() const + { + return index; + } +private: + size_t index; }; struct ProcedureStatus diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp index b8088dd..b0d0b27 100644 --- a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp +++ b/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp @@ -18,7 +18,6 @@ MqttPublisherFbImpl::MqttPublisherFbImpl(const ContextPtr& ctx, : FunctionBlock(type, ctx, parent, generateLocalId()), mqttClient(mqttClient), jsonDataWorker(loggerComponent), - inputPortCount(0), running(true), hasSignalError(false), signalDescriptorChanged(false), @@ -205,22 +204,7 @@ void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) { if (reassignPorts) { - for (auto it = signalContexts.begin(); it != signalContexts.end();) - { - if (!it->inputPort.getSignal().assigned()) - { - removeInputPort(it->inputPort); - if (it->previewSignal.assigned() && (!commonPreviewSignal.assigned() || commonPreviewSignal != it->previewSignal)) - { - removeSignal(it->previewSignal); - } - it = signalContexts.erase(it); - } - else - { - ++it; - } - } + clearPorts(); } if (config.enablePreview) { @@ -233,40 +217,38 @@ void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) for (auto it = signalContexts.begin(); it != signalContexts.end();) { it->inputPort.setListener(this->template borrowPtr()); - if (!it->inputPort.getSignal().assigned()) + if (it->inputPort.getSignal().assigned()) { - ++it; - continue; - } - if (config.enablePreview) - { - if (config.topicMode == TopicMode::Single) + if (config.enablePreview) { - if (it->previewSignal != commonPreviewSignal) + if (config.topicMode == TopicMode::Single) { - if (it->previewSignal.assigned()) - removeSignal(it->previewSignal); - it->previewSignal = commonPreviewSignal; + if (it->previewSignal != commonPreviewSignal) + { + if (it->previewSignal.assigned()) + removeSignal(it->previewSignal); + it->previewSignal = commonPreviewSignal; + } } - } - else if (config.topicMode == TopicMode::PerSignal) - { - if (!it->previewSignal.assigned() || (commonPreviewSignal.assigned() && commonPreviewSignal == it->previewSignal)) + else if (config.topicMode == TopicMode::PerSignal) { - const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::String).build(); - it->previewSignal = createAndAddSignal(fmt::format("{}{}", PUB_PREVIEW_SIGNAL_NAME, size_t(it->index)), signalDsc); + if (!it->previewSignal.assigned() || (commonPreviewSignal.assigned() && commonPreviewSignal == it->previewSignal)) + { + const auto signalDsc = DataDescriptorBuilder().setSampleType(SampleType::String).build(); + it->previewSignal = createAndAddSignal(fmt::format("{}{}", PUB_PREVIEW_SIGNAL_NAME, size_t(it->getIndex())), signalDsc); + } } } - } - else - { - if (it->previewSignal.assigned()) + else { - if (!commonPreviewSignal.assigned() || commonPreviewSignal != it->previewSignal) + if (it->previewSignal.assigned()) { - removeSignal(it->previewSignal); + if (!commonPreviewSignal.assigned() || commonPreviewSignal != it->previewSignal) + { + removeSignal(it->previewSignal); + } + it->previewSignal = nullptr; } - it->previewSignal = nullptr; } } ++it; @@ -276,13 +258,37 @@ void MqttPublisherFbImpl::updatePortsAndSignals(bool reassignPorts) removeSignal(commonPreviewSignal); commonPreviewSignal = nullptr; } - if (reassignPorts) + + updateCoreEventCallbacks(); +} + +void MqttPublisherFbImpl::clearPorts() +{ + for (auto it = signalContexts.begin(); it != signalContexts.end();) { - const auto inputPort = createAndAddInputPort(fmt::format("Input{}", size_t(inputPortCount)), PacketReadyNotification::SameThread); - signalContexts.emplace_back(size_t(inputPortCount++), inputPort, nullptr); + if (!it->inputPort.getSignal().assigned()) + { + + if (it->previewSignal.assigned() && (!commonPreviewSignal.assigned() || commonPreviewSignal != it->previewSignal)) + { + removeSignal(it->previewSignal); + } + removeInputPort(it->inputPort); + it = signalContexts.erase(it); + } + else + { + ++it; + } } + size_t inputPortCount = 0; + auto it = signalContexts.crbegin(); + if (it != signalContexts.crend()) + inputPortCount = it->getIndex() + 1; + + const auto inputPort = createAndAddInputPort(fmt::format("Input{}", size_t(inputPortCount)), PacketReadyNotification::SameThread); + signalContexts.emplace_back(size_t(inputPortCount), inputPort, nullptr); - updateCoreEventCallbacks(); } void MqttPublisherFbImpl::updateCoreEventCallbacks() From d69bb2f5e7f730dbe5c5ea5639cc6aec79e16c73 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 30 Jan 2026 17:30:44 +0100 Subject: [PATCH 39/49] mqtt: updating app examples --- .../custom-mqtt-sub/src/custom-mqtt-sub.cpp | 1 + examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp | 2 + .../ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp | 65 ++++++++----------- 3 files changed, 31 insertions(+), 37 deletions(-) diff --git a/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp b/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp index 9558dfb..01678b8 100644 --- a/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp +++ b/examples/custom-mqtt-sub/src/custom-mqtt-sub.cpp @@ -46,6 +46,7 @@ std::string to_string(daq::DataPacketPtr packet) case SampleType::Int64: data = std::to_string(*(static_cast(packet.getData()))); break; + case SampleType::String: case SampleType::Binary: data = '\"' + std::string(static_cast(packet.getData()), packet.getDataSize()) + '\"'; break; diff --git a/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp b/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp index 21f0077..bc65123 100644 --- a/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp +++ b/examples/raw-mqtt-sub/src/raw-mqtt-sub.cpp @@ -67,6 +67,8 @@ int main(int argc, char* argv[]) // Create subscriber function block configuration auto config = availableFbs.get(fbName).createDefaultConfig(); config.setPropertyValue("Topic", appConfig.topic); + config.setPropertyValue("EnablePreviewSignal", True); + config.setPropertyValue("MessageIsString", True); // Add the subscriber function block to the broker FB daq::FunctionBlockPtr subFb = brokerFB.addFunctionBlock(fbName, config); diff --git a/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp b/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp index c4df6fd..b54579b 100644 --- a/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp +++ b/examples/ref-dev-mqtt-pub/src/ref-dev-mqtt-pub.cpp @@ -6,16 +6,16 @@ using namespace daq; enum class Mode { - ATOMIC_SIGNAL_ATOMIC_SAMPLE = 0, - ATOMIC_SIGNAL_SAMPLE_ARRAY, - SIGNAL_ARRAY_ATOMIC_SAMPLE, - GROUP_SIGNAL_ATOMIC_SAMPLE_SHARED_TS, + TopicPerSignal = 0, + SingleTopic, _COUNT }; struct ConfigStruct { std::string brokerAddress; Mode mode; + bool useArray = false; + size_t arraySize = 0; bool exit = true; int error = 0; }; @@ -27,12 +27,11 @@ ConfigStruct StartUp(int argc, char* argv[]) args.addArg("--help", "Show help message"); args.addArg("--address", "MQTT broker address", true); args.addArg("--mode", "publisher FB mode", true); + args.addArg("--array", "pablish samples as arrays with specified size", true); args.setUsageHelp(APP_NAME " [options]\n" "Available modes:\n" - " 0 - ATOMIC_SIGNAL_ATOMIC_SAMPLE\n" - " 1 - ATOMIC_SIGNAL_SAMPLE_ARRAY\n" - " 2 - SIGNAL_ARRAY_ATOMIC_SAMPLE\n" - " 3 - GROUP_SIGNAL_ATOMIC_SAMPLE_SHARED_TS"); + " 0 - Topic per signal\n" + " 1 - Single topic\n"); args.parse(argc, argv); if (args.hasArg("--help") || args.hasUnknownArgs()) @@ -54,6 +53,19 @@ ConfigStruct StartUp(int argc, char* argv[]) return config; } config.mode = static_cast(mode); + if (args.hasArg("--array")) + { + config.useArray = true; + config.arraySize = std::stoi(args.getArgValue("--array", "0")); + if (config.arraySize == 0) + { + std::cout << "Invalid array size value. It must be greater than 0." << std::endl; + args.printHelp(); + config.error = -1; + config.exit = true; + return config; + } + } return config; } @@ -73,18 +85,18 @@ int main(int argc, char* argv[]) // Configure channels const auto channels = refDevice.getChannelsRecursive(); - channels[0].setPropertyValue("UseGlobalSampleRate", appConfig.mode == Mode::GROUP_SIGNAL_ATOMIC_SAMPLE_SHARED_TS); + channels[0].setPropertyValue("UseGlobalSampleRate", appConfig.mode == Mode::SingleTopic); channels[0].setPropertyValue("SampleRate", 10); channels[0].setPropertyValue("Frequency", 1); channels[0].setPropertyValue("Waveform", 1); - channels[1].setPropertyValue("UseGlobalSampleRate", appConfig.mode == Mode::GROUP_SIGNAL_ATOMIC_SAMPLE_SHARED_TS); + channels[1].setPropertyValue("UseGlobalSampleRate", appConfig.mode == Mode::SingleTopic); channels[1].setPropertyValue("SampleRate", 20); channels[1].setPropertyValue("Frequency", 1); channels[1].setPropertyValue("Waveform", 3); - channels[2].setPropertyValue("UseGlobalSampleRate", appConfig.mode == Mode::GROUP_SIGNAL_ATOMIC_SAMPLE_SHARED_TS); + channels[2].setPropertyValue("UseGlobalSampleRate", appConfig.mode == Mode::SingleTopic); channels[2].setPropertyValue("SampleRate", 50); channels[2].setPropertyValue("Frequency", 4); - channels[3].setPropertyValue("UseGlobalSampleRate", appConfig.mode == Mode::GROUP_SIGNAL_ATOMIC_SAMPLE_SHARED_TS); + channels[3].setPropertyValue("UseGlobalSampleRate", appConfig.mode == Mode::SingleTopic); channels[3].setPropertyValue("SampleRate", 100); channels[3].setPropertyValue("Frequency", 20); @@ -101,31 +113,10 @@ int main(int argc, char* argv[]) config.setPropertyValue("QoS", 1); config.setPropertyValue("ReaderWaitPeriod", 20); config.setPropertyValue("SignalValueJSONKey", 2); - switch (appConfig.mode) { - case Mode::ATOMIC_SIGNAL_ATOMIC_SAMPLE: - config.setPropertyValue("SharedTimestamp", False); - config.setPropertyValue("TopicMode", 0); - config.setPropertyValue("GroupValues", False); - break; - case Mode::ATOMIC_SIGNAL_SAMPLE_ARRAY: - config.setPropertyValue("SharedTimestamp", False); - config.setPropertyValue("TopicMode", 0); - config.setPropertyValue("GroupValues", True); - config.setPropertyValue("SamplesPerMessage", 3); - break; - case Mode::SIGNAL_ARRAY_ATOMIC_SAMPLE: - config.setPropertyValue("SharedTimestamp", False); - config.setPropertyValue("TopicMode", 1); - config.setPropertyValue("GroupValues", False); - break; - case Mode::GROUP_SIGNAL_ATOMIC_SAMPLE_SHARED_TS: - config.setPropertyValue("SharedTimestamp", True); - config.setPropertyValue("TopicMode", 1); - config.setPropertyValue("GroupValues", False); - break; - default: - break; - } + config.setPropertyValue("TopicMode", (appConfig.mode == Mode::TopicPerSignal) ? 0 : 1); + config.setPropertyValue("GroupValues", (appConfig.useArray) ? True : False); + config.setPropertyValue("SamplesPerMessage", appConfig.arraySize); + config.setPropertyValue("Topic", "opendaq/test/values"); // Add the publisher function block to the broker device From 32424d737b20b124a85266071875f157e913509c Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 30 Jan 2026 17:35:54 +0100 Subject: [PATCH 40/49] mqtt: main branch for openDAQ --- external/opendaq/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/opendaq/CMakeLists.txt b/external/opendaq/CMakeLists.txt index 56d9d00..cddbe03 100644 --- a/external/opendaq/CMakeLists.txt +++ b/external/opendaq/CMakeLists.txt @@ -3,7 +3,7 @@ set(OPENDAQ_ENABLE_TESTS false) FetchContent_Declare( opendaq GIT_REPOSITORY https://github.com/openDAQ/openDAQ.git - GIT_TAG origin/string-signals + GIT_TAG origin/main GIT_PROGRESS ON EXCLUDE_FROM_ALL SYSTEM From 937f527f656abed63f98bbed8adfbb420d5e1498 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Fri, 30 Jan 2026 17:52:58 +0100 Subject: [PATCH 41/49] mqtt: README updating --- README.md | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a94327e..ebaec0e 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ MQTT module for the [OpenDAQ SDK](https://github.com/openDAQ/openDAQ). The modul 1) One MQTT message per signal / one message per sample / one topic per signal / one timestamp for each sample. Example: *{"AI0": 1.1, "timestamp": 1763716736100000}* 2) One MQTT message per signal / one message containing several samples / one topic per signal / one timestamp per sample (array of samples). Example: *{"AI0": [1.1, 2.2, 3.3], "timestamps": [1763716736100000, 1763716736200000, 1763716736300000]}* - - 3) One MQTT message for several signals (from 1 to N) / one message per sample for each signal / one topic for all signals / separate timestamps for each signal. Example: *[{"AI0": 1.1, "timestamp": 1763716736100000}, {"AI1": 2, "timestamp": 1763716736700000}]* - 4) One MQTT message for all signals / one message per sample containing all signals / one topic for all signals / one shared timestamp for all signals. Example: *{"AI0": 1.1, "AI1": 2, "timestamp": 1763716736100000}* + 3) One MQTT message for all signals / one message per sample containing all signals / one topic for all signals / one shared timestamp for all signals. Example: *{"AI0": 1.1, "AI1": 2, "timestamp": 1763716736100000}* + + 4) One MQTT message for all signals / one message containing several samples for all signals / one topic for all signals / one shared timestamp for all signals (array of samples). Example: *{"AI0": [1.1, 2.2, 3.3], "AI1": [4.1, 4.2, 4.3], "timestamp": [1763716736100000, 1763716736200000, 1763716736300000]}* The schemes are configured through combinations of properties. @@ -39,19 +39,18 @@ MQTT module for the [OpenDAQ SDK](https://github.com/openDAQ/openDAQ). The modul - *QoS* (list) — MQTT Quality of Service level. It can be *0* (at most once), *1* (at least once), or *2* (exactly once). By default, it is set to *1*. - *Topic* (string) — Topic name for publishing in *SingleTopic* mode. If left empty, the Publisher's *Global ID* is used as the topic name. - *Topics* (list of strings, read-only) — Contains a list of topics used for publishing data in the *TopicPerSignal* mode. The order in the list is the same as the input ports order. - - *SharedTimestamp* (bool) — Enables the use of a shared timestamp for all signals when publishing in *SingleTopic* mode. By default, it is set to *false*. - - *GroupValues* (bool) — Enables the use of a sample pack for a signal when publishing in *TopicPerSignal* mode. By default, it is set to *false*. + - *GroupValues* (bool) — Enables the use of a sample pack for a signal. By default, it is set to *false*. - *SignalValueJSONKey* (list) — Describes how to name a JSON value field. By default it is set to *GlobalID*. - - *SamplesPerMessage* (integer) — Sets the size of the sample pack when publishing grouped values in *TopicPerSignal* mode. By default, it is set to *1*. + - *SamplesPerMessage* (integer) — Sets the size of the sample pack when publishing grouped values. By default, it is set to *1*. - *ReaderWaitPeriod* (integer) — Polling period in milliseconds, specifying how often the server calls an internal reader to collect and publish the connected signals’ data to an MQTT broker. By default, it is set to *20 ms*. - *EnablePreviewSignal* (bool) — Enable the creation of preview signals: one signal in *SingleTopic* mode and one signal per connected input port in *TopicPerSignal* mode. These signals contain the same JSON string data that is published to MQTT topics. - *Schema* (string, read-only) - Describes the general representation of a JSON data packet according to the current function block settings. To configure the publishing schemes, set the properties as follows: - 1) *TopicMode(0), SharedTimestamp(false), GroupValues(false)*; - 2) *TopicMode(0), SharedTimestamp(false), GroupValues(true), SamplesPerMessage()*; - 3) *TopicMode(1), SharedTimestamp(false), GroupValues(false)*; - 4) *TopicMode(1), SharedTimestamp(true), GroupValues(false)*; + 1) *TopicMode(0), GroupValues(false)*; + 2) *TopicMode(0), GroupValues(true), SamplesPerMessage()*; + 3) *TopicMode(1), GroupValues(false)*; + 4) *TopicMode(1), GroupValues(true), SamplesPerMessage()*; 3) **MQTT subscriber Function Block (MQTTSubscriberFB)**: @@ -62,6 +61,7 @@ MQTT module for the [OpenDAQ SDK](https://github.com/openDAQ/openDAQ). The modul - *Topic* (string) — MQTT topic to subscribe to for receiving raw binary data. - *QoS* (list) — MQTT Quality of Service level. It can be *0* (at most once), *1* (at least once), or *2* (exactly once). By default, it is set to *1*. - *EnablePreviewSignal* (bool) — Enable the creation of a preview signal. This signal contains the raw binary data received from an MQTT topic. + - *MessageIsString* (bool) — Interpret a received message as a string. - *JSONConfigFile* (string) — path to file with **JSON configuration string**. See the *JSONConfig* property for more details. This property could be set only at creation. It is not visible. - *JSONConfig* (string) — **JSON configuration string** that defines the MQTT topic and the corresponding signals to subscribe to. This property could be set only at creation. It is not visible. A typical string structure: ```json @@ -183,13 +183,11 @@ There are 3 example C++ application: ```bash ./raw-mqtt-sub --address broker.emqx.io /mirip/UNet3AC2/sensor/data ``` - - **ref-dev-mqtt-pub** - demonstrates how to work with the *MQTTJSONPublisherFB*. The application creates an *openDAQ ref-device* with four channels, an *MQTTClientFB*, and a *MQTTJSONPublisherFB* to publish JSON MQTT messages with the channels’ data. The properties of the *MQTTJSONPublisherFB* are set according to the selected mode, which can be specified via the *--mode* option. Posible values are: - - 0 - One MQTT message per signal / one message per sample / one topic per signal / one timestamp for each sample; - - 1 - One MQTT message per signal / one message containing several samples / one topic per signal / one timestamp per sample (array of samples); - - 2 - One MQTT message for several signals (from 1 to N) / one message per sample for each signal / one topic for all signals / separate timestamps for each signal; - - 3 - One MQTT message for all signals / one message per sample containing all signals / one topic for all signals / one shared timestamp for all signals. + - **ref-dev-mqtt-pub** - demonstrates how to work with the *MQTTJSONPublisherFB*. The application creates an *openDAQ ref-device* with four channels, an *MQTTClientFB*, and a *MQTTJSONPublisherFB* to publish JSON MQTT messages with the channels’ data. The properties of the *MQTTJSONPublisherFB* are set according to the selected mode, which can be specified via the *--mode* option, and array size, which can be specified via the *--array* option with size. Posible *--mode* option values are: + - 0 - One MQTT topic per signal; + - 1 - One MQTT message/topic for all signals. ```bash - ./ref-dev-mqtt-pub --address broker.emqx.io --mode 1 + ./ref-dev-mqtt-pub --address broker.emqx.io --mode 1 --array 5 ``` Published messages can be observed using third-party tools (see the **External MQTT tools** section). For all applications, by default, the IP address *127.0.0.1* is used for the broker connection. It can be set via the *--address* option, for example: From fb33e3169cdf58a110e2c469142bdde66be94ad7 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Mon, 2 Feb 2026 11:48:28 +0100 Subject: [PATCH 42/49] mqtt: version 0.1.0 --- CMakeLists.txt | 2 ++ mqtt_streaming_module/CMakeLists.txt | 1 - mqtt_streaming_protocol/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 63dad17..24f1825 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,8 @@ if(OPENDAQ_MQTT_MODULE_ENABLE_SSL) add_compile_definitions(OPENDAQ_MQTT_MODULE_ENABLE_SSL) endif() +set(MQTT_MODULE_VERSION "0.1.0" CACHE STRING "MQTT module version" FORCE) + add_subdirectory(external) add_subdirectory(helper_utils) add_subdirectory(mqtt_streaming_protocol) diff --git a/mqtt_streaming_module/CMakeLists.txt b/mqtt_streaming_module/CMakeLists.txt index 8fa8f18..5ec96cd 100644 --- a/mqtt_streaming_module/CMakeLists.txt +++ b/mqtt_streaming_module/CMakeLists.txt @@ -1,7 +1,6 @@ cmake_minimum_required(VERSION 3.10) set_cmake_folder_context(TARGET_FOLDER_NAME) -set(MQTT_MODULE_VERSION ${OPENDAQ_PACKAGE_VERSION}) set(MQTT_MODULE_PRJ_NAME "OpenDaqMqttModule") message(STATUS "${MQTT_MODULE_PRJ_NAME} version: ${MQTT_MODULE_VERSION}") diff --git a/mqtt_streaming_protocol/CMakeLists.txt b/mqtt_streaming_protocol/CMakeLists.txt index 870c4f3..b03ad05 100644 --- a/mqtt_streaming_protocol/CMakeLists.txt +++ b/mqtt_streaming_protocol/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10) set_cmake_folder_context(TARGET_FOLDER_NAME) -set(MQTT_STREAMING_PROTOCOL_VERSION ${OPENDAQ_PACKAGE_VERSION}) +set(MQTT_STREAMING_PROTOCOL_VERSION ${MQTT_MODULE_VERSION}) set(MQTT_STREAMING_PROTOCOL_PRJ_NAME "OpenDaqMqttStreamingProtocol") message(STATUS "${MQTT_STREAMING_PROTOCOL_PRJ_NAME} version: ${MQTT_STREAMING_PROTOCOL_VERSION}") From 9ce17e7e4d7824bed54d37cb77f9b93e8dca9732 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Mon, 2 Feb 2026 14:11:50 +0100 Subject: [PATCH 43/49] new layout --- CMakeLists.txt | 5 ++--- modules/CMakeLists.txt | 4 ++++ .../mqtt_streaming_module}/CMakeLists.txt | 0 .../atomic_signal_atomic_sample_handler.h | 0 .../mqtt_streaming_module/atomic_signal_sample_arr_handler.h | 0 .../include/mqtt_streaming_module/common.h | 0 .../include/mqtt_streaming_module/constants.h | 0 .../group_signal_shared_ts_arr_handler.h | 0 .../mqtt_streaming_module/group_signal_shared_ts_handler.h | 0 .../include/mqtt_streaming_module/handler_base.h | 0 .../include/mqtt_streaming_module/handler_factory.h | 0 .../include/mqtt_streaming_module/helper.h | 0 .../include/mqtt_streaming_module/module_dll.h | 0 .../include/mqtt_streaming_module/mqtt_client_fb_impl.h | 0 .../mqtt_streaming_module/mqtt_json_decoder_fb_impl.h | 0 .../include/mqtt_streaming_module/mqtt_publisher_fb_impl.h | 0 .../mqtt_streaming_module/mqtt_streaming_module_impl.h | 0 .../include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h | 0 .../mqtt_streaming_module/signal_arr_atomic_sample_handler.h | 0 .../include/mqtt_streaming_module/status_adaptor.h | 0 .../include/mqtt_streaming_module/status_helper.h | 0 .../include/mqtt_streaming_module/types.h | 0 .../mqtt_streaming_module}/src/CMakeLists.txt | 0 .../src/atomic_signal_atomic_sample_handler.cpp | 0 .../src/atomic_signal_sample_arr_handler.cpp | 0 .../src/group_signal_shared_ts_arr_handler.cpp | 0 .../src/group_signal_shared_ts_handler.cpp | 0 .../mqtt_streaming_module}/src/helper.cpp | 0 .../mqtt_streaming_module}/src/module_dll.cpp | 0 .../mqtt_streaming_module}/src/mqtt_client_fb_impl.cpp | 0 .../mqtt_streaming_module}/src/mqtt_json_decoder_fb_impl.cpp | 0 .../mqtt_streaming_module}/src/mqtt_publisher_fb_impl.cpp | 0 .../src/mqtt_streaming_module_impl.cpp | 0 .../mqtt_streaming_module}/src/mqtt_subscriber_fb_impl.cpp | 0 .../src/signal_arr_atomic_sample_handler.cpp | 0 .../mqtt_streaming_module}/tests/CMakeLists.txt | 0 .../mqtt_streaming_module}/tests/data/public-example0.json | 0 .../mqtt_streaming_module}/tests/data/public-example1.json | 0 .../mqtt_streaming_module}/tests/data/public-example2.json | 0 .../mqtt_streaming_module}/tests/data/public-example3.json | 0 .../mqtt_streaming_module}/tests/test_app.cpp | 0 .../mqtt_streaming_module}/tests/test_daq_test_helper.h | 0 .../mqtt_streaming_module}/tests/test_data.h | 0 .../mqtt_streaming_module}/tests/test_mqtt_client_fb.cpp | 0 .../tests/test_mqtt_json_decoder_fb.cpp | 0 .../mqtt_streaming_module}/tests/test_mqtt_publisher_fb.cpp | 0 .../tests/test_mqtt_streaming_module.cpp | 0 .../mqtt_streaming_module}/tests/test_mqtt_subscriber_fb.cpp | 0 shared/CMakeLists.txt | 5 +++++ {helper_utils => shared/helper_utils}/CMakeLists.txt | 0 .../helper_utils}/include/mqtt_streaming_helper/timer.h | 0 .../mqtt_streaming_protocol}/CMakeLists.txt | 0 .../mqtt_streaming_protocol}/include/MqttAsyncClient.h | 0 .../mqtt_streaming_protocol}/include/MqttDataWrapper.h | 0 .../mqtt_streaming_protocol}/include/MqttMessage.h | 0 .../mqtt_streaming_protocol}/include/MqttSettings.h | 0 .../mqtt_streaming_protocol}/include/timestampConverter.h | 0 .../mqtt_streaming_protocol}/src/CMakeLists.txt | 0 .../mqtt_streaming_protocol}/src/MqttAsyncClient.cpp | 0 .../mqtt_streaming_protocol}/src/MqttDataWrapper.cpp | 0 .../mqtt_streaming_protocol}/tests/CMakeLists.txt | 0 .../tests/MqttAsyncClientWrapper.cpp | 0 .../mqtt_streaming_protocol}/tests/MqttAsyncClientWrapper.h | 0 .../mqtt_streaming_protocol}/tests/test_app.cpp | 0 .../tests/test_mqtt_streaming_protocol.cpp | 0 65 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 modules/CMakeLists.txt rename {mqtt_streaming_module => modules/mqtt_streaming_module}/CMakeLists.txt (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/common.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/constants.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/group_signal_shared_ts_handler.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/handler_base.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/handler_factory.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/helper.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/module_dll.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/mqtt_client_fb_impl.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/mqtt_streaming_module_impl.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/status_adaptor.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/status_helper.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/include/mqtt_streaming_module/types.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/CMakeLists.txt (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/atomic_signal_atomic_sample_handler.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/atomic_signal_sample_arr_handler.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/group_signal_shared_ts_arr_handler.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/group_signal_shared_ts_handler.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/helper.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/module_dll.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/mqtt_client_fb_impl.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/mqtt_json_decoder_fb_impl.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/mqtt_publisher_fb_impl.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/mqtt_streaming_module_impl.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/mqtt_subscriber_fb_impl.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/src/signal_arr_atomic_sample_handler.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/CMakeLists.txt (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/data/public-example0.json (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/data/public-example1.json (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/data/public-example2.json (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/data/public-example3.json (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/test_app.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/test_daq_test_helper.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/test_data.h (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/test_mqtt_client_fb.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/test_mqtt_json_decoder_fb.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/test_mqtt_publisher_fb.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/test_mqtt_streaming_module.cpp (100%) rename {mqtt_streaming_module => modules/mqtt_streaming_module}/tests/test_mqtt_subscriber_fb.cpp (100%) create mode 100644 shared/CMakeLists.txt rename {helper_utils => shared/helper_utils}/CMakeLists.txt (100%) rename {helper_utils => shared/helper_utils}/include/mqtt_streaming_helper/timer.h (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/CMakeLists.txt (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/include/MqttAsyncClient.h (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/include/MqttDataWrapper.h (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/include/MqttMessage.h (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/include/MqttSettings.h (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/include/timestampConverter.h (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/src/CMakeLists.txt (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/src/MqttAsyncClient.cpp (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/src/MqttDataWrapper.cpp (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/tests/CMakeLists.txt (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/tests/MqttAsyncClientWrapper.cpp (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/tests/MqttAsyncClientWrapper.h (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/tests/test_app.cpp (100%) rename {mqtt_streaming_protocol => shared/mqtt_streaming_protocol}/tests/test_mqtt_streaming_protocol.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 24f1825..e10c47c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,9 +64,8 @@ endif() set(MQTT_MODULE_VERSION "0.1.0" CACHE STRING "MQTT module version" FORCE) add_subdirectory(external) -add_subdirectory(helper_utils) -add_subdirectory(mqtt_streaming_protocol) -add_subdirectory(mqtt_streaming_module) +add_subdirectory(shared) +add_subdirectory(modules) if(OPENDAQ_DEVICE_EXAMPLE_ENABLE_EXAMPLE_APPS) message(STATUS "Example applications have been enabled") diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt new file mode 100644 index 0000000..f68db72 --- /dev/null +++ b/modules/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.10) +list(APPEND CMAKE_MESSAGE_CONTEXT modules) + +add_subdirectory(mqtt_streaming_module) \ No newline at end of file diff --git a/mqtt_streaming_module/CMakeLists.txt b/modules/mqtt_streaming_module/CMakeLists.txt similarity index 100% rename from mqtt_streaming_module/CMakeLists.txt rename to modules/mqtt_streaming_module/CMakeLists.txt diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_atomic_sample_handler.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/atomic_signal_sample_arr_handler.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/common.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/common.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/common.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/common.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/constants.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/constants.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/constants.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/constants.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_arr_handler.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/group_signal_shared_ts_handler.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/handler_base.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/handler_factory.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/helper.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/helper.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/helper.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/helper.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/module_dll.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/module_dll.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/module_dll.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/module_dll.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_client_fb_impl.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_client_fb_impl.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/mqtt_client_fb_impl.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_client_fb_impl.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_json_decoder_fb_impl.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_publisher_fb_impl.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_streaming_module_impl.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_streaming_module_impl.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/mqtt_streaming_module_impl.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_streaming_module_impl.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/mqtt_subscriber_fb_impl.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/signal_arr_atomic_sample_handler.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/status_adaptor.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/status_adaptor.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/status_adaptor.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/status_adaptor.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/status_helper.h diff --git a/mqtt_streaming_module/include/mqtt_streaming_module/types.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/types.h similarity index 100% rename from mqtt_streaming_module/include/mqtt_streaming_module/types.h rename to modules/mqtt_streaming_module/include/mqtt_streaming_module/types.h diff --git a/mqtt_streaming_module/src/CMakeLists.txt b/modules/mqtt_streaming_module/src/CMakeLists.txt similarity index 100% rename from mqtt_streaming_module/src/CMakeLists.txt rename to modules/mqtt_streaming_module/src/CMakeLists.txt diff --git a/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp b/modules/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp similarity index 100% rename from mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp rename to modules/mqtt_streaming_module/src/atomic_signal_atomic_sample_handler.cpp diff --git a/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp b/modules/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp similarity index 100% rename from mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp rename to modules/mqtt_streaming_module/src/atomic_signal_sample_arr_handler.cpp diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp b/modules/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp similarity index 100% rename from mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp rename to modules/mqtt_streaming_module/src/group_signal_shared_ts_arr_handler.cpp diff --git a/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp b/modules/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp similarity index 100% rename from mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp rename to modules/mqtt_streaming_module/src/group_signal_shared_ts_handler.cpp diff --git a/mqtt_streaming_module/src/helper.cpp b/modules/mqtt_streaming_module/src/helper.cpp similarity index 100% rename from mqtt_streaming_module/src/helper.cpp rename to modules/mqtt_streaming_module/src/helper.cpp diff --git a/mqtt_streaming_module/src/module_dll.cpp b/modules/mqtt_streaming_module/src/module_dll.cpp similarity index 100% rename from mqtt_streaming_module/src/module_dll.cpp rename to modules/mqtt_streaming_module/src/module_dll.cpp diff --git a/mqtt_streaming_module/src/mqtt_client_fb_impl.cpp b/modules/mqtt_streaming_module/src/mqtt_client_fb_impl.cpp similarity index 100% rename from mqtt_streaming_module/src/mqtt_client_fb_impl.cpp rename to modules/mqtt_streaming_module/src/mqtt_client_fb_impl.cpp diff --git a/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp b/modules/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp similarity index 100% rename from mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp rename to modules/mqtt_streaming_module/src/mqtt_json_decoder_fb_impl.cpp diff --git a/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp b/modules/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp similarity index 100% rename from mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp rename to modules/mqtt_streaming_module/src/mqtt_publisher_fb_impl.cpp diff --git a/mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp b/modules/mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp similarity index 100% rename from mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp rename to modules/mqtt_streaming_module/src/mqtt_streaming_module_impl.cpp diff --git a/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp b/modules/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp similarity index 100% rename from mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp rename to modules/mqtt_streaming_module/src/mqtt_subscriber_fb_impl.cpp diff --git a/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp b/modules/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp similarity index 100% rename from mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp rename to modules/mqtt_streaming_module/src/signal_arr_atomic_sample_handler.cpp diff --git a/mqtt_streaming_module/tests/CMakeLists.txt b/modules/mqtt_streaming_module/tests/CMakeLists.txt similarity index 100% rename from mqtt_streaming_module/tests/CMakeLists.txt rename to modules/mqtt_streaming_module/tests/CMakeLists.txt diff --git a/mqtt_streaming_module/tests/data/public-example0.json b/modules/mqtt_streaming_module/tests/data/public-example0.json similarity index 100% rename from mqtt_streaming_module/tests/data/public-example0.json rename to modules/mqtt_streaming_module/tests/data/public-example0.json diff --git a/mqtt_streaming_module/tests/data/public-example1.json b/modules/mqtt_streaming_module/tests/data/public-example1.json similarity index 100% rename from mqtt_streaming_module/tests/data/public-example1.json rename to modules/mqtt_streaming_module/tests/data/public-example1.json diff --git a/mqtt_streaming_module/tests/data/public-example2.json b/modules/mqtt_streaming_module/tests/data/public-example2.json similarity index 100% rename from mqtt_streaming_module/tests/data/public-example2.json rename to modules/mqtt_streaming_module/tests/data/public-example2.json diff --git a/mqtt_streaming_module/tests/data/public-example3.json b/modules/mqtt_streaming_module/tests/data/public-example3.json similarity index 100% rename from mqtt_streaming_module/tests/data/public-example3.json rename to modules/mqtt_streaming_module/tests/data/public-example3.json diff --git a/mqtt_streaming_module/tests/test_app.cpp b/modules/mqtt_streaming_module/tests/test_app.cpp similarity index 100% rename from mqtt_streaming_module/tests/test_app.cpp rename to modules/mqtt_streaming_module/tests/test_app.cpp diff --git a/mqtt_streaming_module/tests/test_daq_test_helper.h b/modules/mqtt_streaming_module/tests/test_daq_test_helper.h similarity index 100% rename from mqtt_streaming_module/tests/test_daq_test_helper.h rename to modules/mqtt_streaming_module/tests/test_daq_test_helper.h diff --git a/mqtt_streaming_module/tests/test_data.h b/modules/mqtt_streaming_module/tests/test_data.h similarity index 100% rename from mqtt_streaming_module/tests/test_data.h rename to modules/mqtt_streaming_module/tests/test_data.h diff --git a/mqtt_streaming_module/tests/test_mqtt_client_fb.cpp b/modules/mqtt_streaming_module/tests/test_mqtt_client_fb.cpp similarity index 100% rename from mqtt_streaming_module/tests/test_mqtt_client_fb.cpp rename to modules/mqtt_streaming_module/tests/test_mqtt_client_fb.cpp diff --git a/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp b/modules/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp similarity index 100% rename from mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp rename to modules/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp diff --git a/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp b/modules/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp similarity index 100% rename from mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp rename to modules/mqtt_streaming_module/tests/test_mqtt_publisher_fb.cpp diff --git a/mqtt_streaming_module/tests/test_mqtt_streaming_module.cpp b/modules/mqtt_streaming_module/tests/test_mqtt_streaming_module.cpp similarity index 100% rename from mqtt_streaming_module/tests/test_mqtt_streaming_module.cpp rename to modules/mqtt_streaming_module/tests/test_mqtt_streaming_module.cpp diff --git a/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp b/modules/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp similarity index 100% rename from mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp rename to modules/mqtt_streaming_module/tests/test_mqtt_subscriber_fb.cpp diff --git a/shared/CMakeLists.txt b/shared/CMakeLists.txt new file mode 100644 index 0000000..6e825c2 --- /dev/null +++ b/shared/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.10) +list(APPEND CMAKE_MESSAGE_CONTEXT shared) + +add_subdirectory(mqtt_streaming_protocol) +add_subdirectory(helper_utils) \ No newline at end of file diff --git a/helper_utils/CMakeLists.txt b/shared/helper_utils/CMakeLists.txt similarity index 100% rename from helper_utils/CMakeLists.txt rename to shared/helper_utils/CMakeLists.txt diff --git a/helper_utils/include/mqtt_streaming_helper/timer.h b/shared/helper_utils/include/mqtt_streaming_helper/timer.h similarity index 100% rename from helper_utils/include/mqtt_streaming_helper/timer.h rename to shared/helper_utils/include/mqtt_streaming_helper/timer.h diff --git a/mqtt_streaming_protocol/CMakeLists.txt b/shared/mqtt_streaming_protocol/CMakeLists.txt similarity index 100% rename from mqtt_streaming_protocol/CMakeLists.txt rename to shared/mqtt_streaming_protocol/CMakeLists.txt diff --git a/mqtt_streaming_protocol/include/MqttAsyncClient.h b/shared/mqtt_streaming_protocol/include/MqttAsyncClient.h similarity index 100% rename from mqtt_streaming_protocol/include/MqttAsyncClient.h rename to shared/mqtt_streaming_protocol/include/MqttAsyncClient.h diff --git a/mqtt_streaming_protocol/include/MqttDataWrapper.h b/shared/mqtt_streaming_protocol/include/MqttDataWrapper.h similarity index 100% rename from mqtt_streaming_protocol/include/MqttDataWrapper.h rename to shared/mqtt_streaming_protocol/include/MqttDataWrapper.h diff --git a/mqtt_streaming_protocol/include/MqttMessage.h b/shared/mqtt_streaming_protocol/include/MqttMessage.h similarity index 100% rename from mqtt_streaming_protocol/include/MqttMessage.h rename to shared/mqtt_streaming_protocol/include/MqttMessage.h diff --git a/mqtt_streaming_protocol/include/MqttSettings.h b/shared/mqtt_streaming_protocol/include/MqttSettings.h similarity index 100% rename from mqtt_streaming_protocol/include/MqttSettings.h rename to shared/mqtt_streaming_protocol/include/MqttSettings.h diff --git a/mqtt_streaming_protocol/include/timestampConverter.h b/shared/mqtt_streaming_protocol/include/timestampConverter.h similarity index 100% rename from mqtt_streaming_protocol/include/timestampConverter.h rename to shared/mqtt_streaming_protocol/include/timestampConverter.h diff --git a/mqtt_streaming_protocol/src/CMakeLists.txt b/shared/mqtt_streaming_protocol/src/CMakeLists.txt similarity index 100% rename from mqtt_streaming_protocol/src/CMakeLists.txt rename to shared/mqtt_streaming_protocol/src/CMakeLists.txt diff --git a/mqtt_streaming_protocol/src/MqttAsyncClient.cpp b/shared/mqtt_streaming_protocol/src/MqttAsyncClient.cpp similarity index 100% rename from mqtt_streaming_protocol/src/MqttAsyncClient.cpp rename to shared/mqtt_streaming_protocol/src/MqttAsyncClient.cpp diff --git a/mqtt_streaming_protocol/src/MqttDataWrapper.cpp b/shared/mqtt_streaming_protocol/src/MqttDataWrapper.cpp similarity index 100% rename from mqtt_streaming_protocol/src/MqttDataWrapper.cpp rename to shared/mqtt_streaming_protocol/src/MqttDataWrapper.cpp diff --git a/mqtt_streaming_protocol/tests/CMakeLists.txt b/shared/mqtt_streaming_protocol/tests/CMakeLists.txt similarity index 100% rename from mqtt_streaming_protocol/tests/CMakeLists.txt rename to shared/mqtt_streaming_protocol/tests/CMakeLists.txt diff --git a/mqtt_streaming_protocol/tests/MqttAsyncClientWrapper.cpp b/shared/mqtt_streaming_protocol/tests/MqttAsyncClientWrapper.cpp similarity index 100% rename from mqtt_streaming_protocol/tests/MqttAsyncClientWrapper.cpp rename to shared/mqtt_streaming_protocol/tests/MqttAsyncClientWrapper.cpp diff --git a/mqtt_streaming_protocol/tests/MqttAsyncClientWrapper.h b/shared/mqtt_streaming_protocol/tests/MqttAsyncClientWrapper.h similarity index 100% rename from mqtt_streaming_protocol/tests/MqttAsyncClientWrapper.h rename to shared/mqtt_streaming_protocol/tests/MqttAsyncClientWrapper.h diff --git a/mqtt_streaming_protocol/tests/test_app.cpp b/shared/mqtt_streaming_protocol/tests/test_app.cpp similarity index 100% rename from mqtt_streaming_protocol/tests/test_app.cpp rename to shared/mqtt_streaming_protocol/tests/test_app.cpp diff --git a/mqtt_streaming_protocol/tests/test_mqtt_streaming_protocol.cpp b/shared/mqtt_streaming_protocol/tests/test_mqtt_streaming_protocol.cpp similarity index 100% rename from mqtt_streaming_protocol/tests/test_mqtt_streaming_protocol.cpp rename to shared/mqtt_streaming_protocol/tests/test_mqtt_streaming_protocol.cpp From dff2d5b9cbe3ebb44a90c286acee68688eab782f Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Mon, 2 Feb 2026 14:13:47 +0100 Subject: [PATCH 44/49] ci.yml --- .github/workflows/ci.yml | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6e30f1f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: Build and Test + +on: + pull_request: + types: [opened, reopened, synchronize, ready_for_review] + +jobs: + build-and-test: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + generator: Ninja + - os: windows-latest + generator: "Visual Studio 17 2022" + + runs-on: ${{ matrix.os }} + + steps: + - name: Install additional dependencies + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get install -y --no-install-recommends mono-runtime libmono-system-json-microsoft4.0-cil libmono-system-data4.0-cil # TODO add mosquitto package + + - name: Checkout project repo + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch || github.event.client_payload.branch || github.ref }} + + - name: Configure project with CMake + run: cmake -B build/output -S . -G "${{ matrix.generator }}" -DTODO_ENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug + + - name: Build project with CMake + run: cmake --build build/output --config Debug + + - name: Install and run mosquitto Linux + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt-get install -y --no-install-recommends # TODO add mosquitto package name + # TODO mosquitto run cmds + + - name: Install and run mosquitto Windows + if: matrix.os == 'windows-latest' + run: # TODO install and run mosquitto + + - name: Run project tests with CMake + run: ctest --test-dir build/output --output-on-failure -C Debug From 4330faebb59459cb863e2fc97211145afd4c2e3b Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Mon, 2 Feb 2026 15:00:42 +0100 Subject: [PATCH 45/49] mqtt: export only if tests are enabled --- .../include/mqtt_streaming_module/common.h | 16 ++++++++++------ modules/mqtt_streaming_module/src/CMakeLists.txt | 6 ++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/modules/mqtt_streaming_module/include/mqtt_streaming_module/common.h b/modules/mqtt_streaming_module/include/mqtt_streaming_module/common.h index 9be4ab5..ce81e9a 100644 --- a/modules/mqtt_streaming_module/include/mqtt_streaming_module/common.h +++ b/modules/mqtt_streaming_module/include/mqtt_streaming_module/common.h @@ -20,12 +20,16 @@ #define BEGIN_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE BEGIN_NAMESPACE_OPENDAQ_MODULE(mqtt_streaming_module) #define END_NAMESPACE_OPENDAQ_MQTT_STREAMING_MODULE END_NAMESPACE_OPENDAQ_MODULE -#if defined(_WIN32) - #ifdef OPENDAQ_MODULE_DLL_IMPORT - #define DAQ_MQTT_STREAM_MODULE_API __declspec(dllexport) +#if !defined(OPENDAQ_MQTT_ENABLE_TESTS) + #define DAQ_MQTT_STREAM_MODULE_API +#else + #if defined(_WIN32) + #if defined(OPENDAQ_MODULE_DLL_IMPORT) + #define DAQ_MQTT_STREAM_MODULE_API __declspec(dllimport) + #else + #define DAQ_MQTT_STREAM_MODULE_API __declspec(dllexport) + #endif #else - #define DAQ_MQTT_STREAM_MODULE_API __declspec(dllimport) + #define DAQ_MQTT_STREAM_MODULE_API __attribute__((visibility("default"))) #endif -#else - #define DAQ_MQTT_STREAM_MODULE_API #endif diff --git a/modules/mqtt_streaming_module/src/CMakeLists.txt b/modules/mqtt_streaming_module/src/CMakeLists.txt index b289ddd..25ac9bf 100644 --- a/modules/mqtt_streaming_module/src/CMakeLists.txt +++ b/modules/mqtt_streaming_module/src/CMakeLists.txt @@ -84,6 +84,12 @@ add_library(${LIB_NAME} SHARED ${SRC_Include} ) add_library(${SDK_TARGET_NAMESPACE}::${LIB_NAME} ALIAS ${LIB_NAME}) +if(OPENDAQ_MQTT_ENABLE_TESTS) + target_compile_definitions(${LIB_NAME} + PRIVATE OPENDAQ_MQTT_ENABLE_TESTS + ) +endif() + if (MSVC) target_compile_options(${LIB_NAME} PRIVATE /bigobj) endif() From 5c4c297e5454bfb29d2819677f81cbbf166c5ac8 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Tue, 3 Feb 2026 11:17:29 +0100 Subject: [PATCH 46/49] CI: tests and mosquitto --- .github/workflows/ci.yml | 4 ++-- CMakeLists.txt | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e30f1f..9cc87ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: ref: ${{ github.event.inputs.branch || github.event.client_payload.branch || github.ref }} - name: Configure project with CMake - run: cmake -B build/output -S . -G "${{ matrix.generator }}" -DTODO_ENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug + run: cmake -B build/output -S . -G "${{ matrix.generator }}" -DOPENDAQ_MQTT_ENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug - name: Build project with CMake run: cmake --build build/output --config Debug @@ -37,7 +37,7 @@ jobs: - name: Install and run mosquitto Linux if: matrix.os == 'ubuntu-latest' run: | - sudo apt-get install -y --no-install-recommends # TODO add mosquitto package name + sudo apt-get install -y --no-install-recommends mosquitto # TODO mosquitto run cmds - name: Install and run mosquitto Windows diff --git a/CMakeLists.txt b/CMakeLists.txt index e10c47c..bf86d18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,10 @@ endif() set(MQTT_MODULE_VERSION "0.1.0" CACHE STRING "MQTT module version" FORCE) +if(OPENDAQ_MQTT_ENABLE_TESTS) + enable_testing() +endif() + add_subdirectory(external) add_subdirectory(shared) add_subdirectory(modules) From 2927f6569d857e85823856cc37df72844f9e967b Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Tue, 3 Feb 2026 12:10:28 +0100 Subject: [PATCH 47/49] mqtt: disable tests that require 2 mqtt brokers --- modules/mqtt_streaming_module/tests/test_mqtt_client_fb.cpp | 2 +- .../mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/mqtt_streaming_module/tests/test_mqtt_client_fb.cpp b/modules/mqtt_streaming_module/tests/test_mqtt_client_fb.cpp index 08a6da4..96e642c 100644 --- a/modules/mqtt_streaming_module/tests/test_mqtt_client_fb.cpp +++ b/modules/mqtt_streaming_module/tests/test_mqtt_client_fb.cpp @@ -103,7 +103,7 @@ TEST_F(MqttFbTest, CreatingMqttFbWithPartialConfig) Enumeration("ComponentStatusType", "Ok", instance.getContext().getTypeManager())); } -TEST_F(MqttFbTest, CreatingSeveralMqttFbs) +TEST_F(MqttFbTest, DISABLED_CreatingSeveralMqttFbs) { const auto instance = Instance(); daq::FunctionBlockPtr fb; diff --git a/modules/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp b/modules/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp index ca8d3e2..3acbfd0 100644 --- a/modules/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp +++ b/modules/mqtt_streaming_module/tests/test_mqtt_json_decoder_fb.cpp @@ -950,7 +950,7 @@ TEST_F(MqttJsonFbCommunicationTest, FullDataTransfer) EXPECT_NE(decoderObj.getStatusContainer().getStatusMessage("ComponentStatus").toStdString().find("Parsing succeeded"), std::string::npos); } -TEST_F(MqttJsonFbCommunicationTest, FullDataTransferFor2MqttFbs) +TEST_F(MqttJsonFbCommunicationTest, DISABLED_FullDataTransferFor2MqttFbs) { const auto msgTemplate = VALID_JSON_DATA_0; const std::string valueF = extractFieldName(msgTemplate, ""); From bec120464708d260d9ed32c0f5db899c97ec7bb6 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Tue, 3 Feb 2026 13:28:31 +0100 Subject: [PATCH 48/49] CI: remove tests for Windows --- .github/workflows/ci.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9cc87ac..ad302b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - name: Install additional dependencies if: matrix.os == 'ubuntu-latest' run: | - sudo apt-get install -y --no-install-recommends mono-runtime libmono-system-json-microsoft4.0-cil libmono-system-data4.0-cil # TODO add mosquitto package + sudo apt-get install -y --no-install-recommends mono-runtime libmono-system-json-microsoft4.0-cil libmono-system-data4.0-cil - name: Checkout project repo uses: actions/checkout@v4 @@ -38,11 +38,7 @@ jobs: if: matrix.os == 'ubuntu-latest' run: | sudo apt-get install -y --no-install-recommends mosquitto - # TODO mosquitto run cmds - - - name: Install and run mosquitto Windows - if: matrix.os == 'windows-latest' - run: # TODO install and run mosquitto - name: Run project tests with CMake + if: matrix.os == 'ubuntu-latest' run: ctest --test-dir build/output --output-on-failure -C Debug From 42176a5072656776df7bd9294f2e5f853ad4acc6 Mon Sep 17 00:00:00 2001 From: Viacheslau Date: Wed, 4 Feb 2026 10:21:02 +0100 Subject: [PATCH 49/49] mqtt: openDAQ version update --- external/opendaq/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/opendaq/CMakeLists.txt b/external/opendaq/CMakeLists.txt index cddbe03..08f4999 100644 --- a/external/opendaq/CMakeLists.txt +++ b/external/opendaq/CMakeLists.txt @@ -3,7 +3,7 @@ set(OPENDAQ_ENABLE_TESTS false) FetchContent_Declare( opendaq GIT_REPOSITORY https://github.com/openDAQ/openDAQ.git - GIT_TAG origin/main + GIT_TAG 41396d19a1567ab6aecacfe7e381ea616dfdad6c GIT_PROGRESS ON EXCLUDE_FROM_ALL SYSTEM