From 18bf971b2757b06fa9620f3339326c617b12de64 Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Thu, 23 May 2024 15:48:46 +1000 Subject: [PATCH] Saving work on the new client application. --- client/lib/CMakeLists.txt | 1 + client/lib/include/cc_mqttsn_client/common.h | 13 + .../lib/script/DefineDefaultConfigVars.cmake | 2 +- client/lib/script/WriteConfigHeader.cmake | 2 +- client/lib/script/WriteProtocolOptions.cmake | 8 +- client/lib/src/ClientImpl.cpp | 106 ++++---- client/lib/src/ClientImpl.h | 25 +- client/lib/src/ConfigState.h | 4 + client/lib/src/op/Op.cpp | 245 ++++++++++++++++++ client/lib/src/op/Op.h | 136 ++++++++++ client/lib/templ/Config.h.templ | 4 +- client/lib/templ/client.cpp.templ | 109 +++++--- client/lib/templ/client.h.templ | 26 +- 13 files changed, 578 insertions(+), 103 deletions(-) create mode 100644 client/lib/src/op/Op.cpp create mode 100644 client/lib/src/op/Op.h diff --git a/client/lib/CMakeLists.txt b/client/lib/CMakeLists.txt index 1df4505..7890520 100644 --- a/client/lib/CMakeLists.txt +++ b/client/lib/CMakeLists.txt @@ -140,6 +140,7 @@ function (gen_lib_mqttsn_client config_file) message (STATUS "Defining library ${lib_name}") set (src + src/op/Op.cpp src/ClientImpl.cpp src/TimerMgr.cpp ) diff --git a/client/lib/include/cc_mqttsn_client/common.h b/client/lib/include/cc_mqttsn_client/common.h index 28db596..a4a8b32 100644 --- a/client/lib/include/cc_mqttsn_client/common.h +++ b/client/lib/include/cc_mqttsn_client/common.h @@ -129,6 +129,8 @@ typedef struct { unsigned char gwId; ///< Gateway ID CC_MqttsnGwStatus status; ///< Gateway status + const unsigned char* m_addr; ///< Gateway address, NULL if not known + unsigned m_addrLen; ///< Length of the gateway address, 0 if not known. } CC_MqttsnGatewayInfo; /// @brief Callback used to request time measurement. @@ -199,8 +201,19 @@ typedef void (*CC_MqttsnSubscribeCompleteReportCb)(void* data, CC_MqttsnAsyncOpS /// @param[in] msgInfo Information about incoming message. typedef void (*CC_MqttsnMessageReportCb)(void* data, const CC_MqttsnMessageInfo* msgInfo); +/// @brief Callback used to report discovered errors. +/// @param[in] data Pointer to user data object, passed as the last parameter to +/// the request call. +/// @param[in] msg Error log message. +/// @ingroup client typedef void (*CC_MqttsnErrorLogCb)(void* data, const char* msg); +/// @brief Callback used to request delay (in ms) to wait before +/// responding with @b GWINFO message on behalf of a gateway. +/// @details In case function return 0U, the response on behalf of the gateway is disabled. +/// @return Number of milliseconds to wait for another @b GWINFO to cancel the intended send of @b GWINFO on behalf of the gateway. +typedef unsigned (*CC_MqttsnGwinfoDelayRequestCb)(void* data); + #ifdef __cplusplus } #endif diff --git a/client/lib/script/DefineDefaultConfigVars.cmake b/client/lib/script/DefineDefaultConfigVars.cmake index 5a727d8..f1da0ec 100644 --- a/client/lib/script/DefineDefaultConfigVars.cmake +++ b/client/lib/script/DefineDefaultConfigVars.cmake @@ -19,7 +19,7 @@ set_default_var_value(CC_MQTTSN_CLIENT_ALLOC_LIMIT 0) #set_default_var_value(CC_MQTTSN_CLIENT_PASSWORD_FIELD_FIXED_LEN 0) #set_default_var_value(CC_MQTTSN_CLIENT_TOPIC_FIELD_FIXED_LEN 0) #set_default_var_value(CC_MQTTSN_CLIENT_BIN_DATA_FIELD_FIXED_LEN 0) -#set_default_var_value(CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE 0) +set_default_var_value(CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE 0) set_default_var_value(CC_MQTTSN_CLIENT_RECEIVE_MAX_LIMIT 0) set_default_var_value(CC_MQTTSN_CLIENT_SEND_MAX_LIMIT 0) set_default_var_value(CC_MQTTSN_CLIENT_ASYNC_SUBS_LIMIT 0) diff --git a/client/lib/script/WriteConfigHeader.cmake b/client/lib/script/WriteConfigHeader.cmake index 2b6726d..4df6c3e 100644 --- a/client/lib/script/WriteConfigHeader.cmake +++ b/client/lib/script/WriteConfigHeader.cmake @@ -44,7 +44,7 @@ replace_in_text (CC_MQTTSN_CLIENT_HAS_DYN_MEM_ALLOC_CPP) replace_in_text (CC_MQTTSN_CLIENT_ALLOC_LIMIT) #replace_in_text (CC_MQTTSN_CLIENT_STRING_FIELD_FIXED_LEN) #replace_in_text (CC_MQTTSN_CLIENT_BIN_DATA_FIELD_FIXED_LEN) -#replace_in_text (CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE) +replace_in_text (CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE) replace_in_text (CC_MQTTSN_CLIENT_RECEIVE_MAX_LIMIT) replace_in_text (CC_MQTTSN_CLIENT_SEND_MAX_LIMIT) replace_in_text (CC_MQTTSN_CLIENT_ASYNC_SUBS_LIMIT) diff --git a/client/lib/script/WriteProtocolOptions.cmake b/client/lib/script/WriteProtocolOptions.cmake index cd8b2ac..eb21f4e 100644 --- a/client/lib/script/WriteProtocolOptions.cmake +++ b/client/lib/script/WriteProtocolOptions.cmake @@ -44,7 +44,7 @@ endmacro() #set_default_opt (MESSAGE_SUBSCRIBE_FIELDS_LIST) #set_default_opt (MESSAGE_UNSUBSCRIBE_FIELDS_LIST) -#set_default_opt (MAX_PACKET_SIZE) +set_default_opt (MAX_PACKET_SIZE) #set_default_opt (MSG_ALLOC_OPT) ######################################### @@ -64,9 +64,9 @@ endif () # set (FIELD_STRING "comms::option::app::FixedSizeStorage<${CC_MQTTSN_CLIENT_STRING_FIELD_FIXED_LEN}>") #endif () -#if (NOT ${CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE} EQUAL 0) -# set (MAX_PACKET_SIZE "comms::option::app::FixedSizeStorage<${CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE}>") -#endif () +if (NOT ${CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE} EQUAL 0) + set (MAX_PACKET_SIZE "comms::option::app::FixedSizeStorage<${CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE}>") +endif () #if (NOT ${CC_MQTTSN_CLIENT_CLIENT_ID_FIELD_FIXED_LEN} EQUAL 0) # set (MESSAGE_CONNECT_FIELDS_CLIENT_ID "comms::option::app::FixedSizeStorage<${CC_MQTTSN_CLIENT_CLIENT_ID_FIELD_FIXED_LEN}>") diff --git a/client/lib/src/ClientImpl.cpp b/client/lib/src/ClientImpl.cpp index c41e823..fab9f6b 100644 --- a/client/lib/src/ClientImpl.cpp +++ b/client/lib/src/ClientImpl.cpp @@ -523,67 +523,67 @@ void ClientImpl::processData(const std::uint8_t* iter, unsigned len) // } // } -// CC_MqttsnErrorCode ClientImpl::sendMessage(const ProtMessage& msg) -// { -// auto len = m_frame.length(msg); +CC_MqttsnErrorCode ClientImpl::sendMessage(const ProtMessage& msg, unsigned broadcastRadius) +{ + auto len = m_frame.length(msg); -// if (m_buf.max_size() < len) { -// errorLog("Output buffer overflow."); -// return CC_MqttsnErrorCode_BufferOverflow; -// } + if (m_buf.max_size() < len) { + errorLog("Output buffer overflow."); + return CC_MqttsnErrorCode_BufferOverflow; + } -// m_buf.resize(len); -// auto writeIter = comms::writeIteratorFor(&m_buf[0]); -// auto es = m_frame.write(msg, writeIter, len); -// COMMS_ASSERT(es == comms::ErrorStatus::Success); -// if (es != comms::ErrorStatus::Success) { -// errorLog("Failed to serialize output message."); -// return CC_MqttsnErrorCode_InternalError; -// } + m_buf.resize(len); + auto writeIter = comms::writeIteratorFor(&m_buf[0]); + auto es = m_frame.write(msg, writeIter, len); + COMMS_ASSERT(es == comms::ErrorStatus::Success); + if (es != comms::ErrorStatus::Success) { + errorLog("Failed to serialize output message."); + return CC_MqttsnErrorCode_InternalError; + } -// COMMS_ASSERT(m_sendOutputDataCb != nullptr); -// m_sendOutputDataCb(m_sendOutputDataData, &m_buf[0], static_cast(len)); + COMMS_ASSERT(m_sendOutputDataCb != nullptr); + m_sendOutputDataCb(m_sendOutputDataData, &m_buf[0], static_cast(len), broadcastRadius); -// for (auto& opPtr : m_keepAliveOps) { -// opPtr->messageSent(); -// } + // for (auto& opPtr : m_keepAliveOps) { + // opPtr->messageSent(); + // } -// return CC_MqttsnErrorCode_Success; -// } + return CC_MqttsnErrorCode_Success; +} -// void ClientImpl::opComplete(const op::Op* op) -// { -// auto iter = std::find(m_ops.begin(), m_ops.end(), op); -// COMMS_ASSERT(iter != m_ops.end()); -// if (iter == m_ops.end()) { -// return; -// } +void ClientImpl::opComplete(const op::Op* op) +{ + auto iter = std::find(m_ops.begin(), m_ops.end(), op); + COMMS_ASSERT(iter != m_ops.end()); + if (iter == m_ops.end()) { + return; + } -// *iter = nullptr; -// m_opsDeleted = true; - -// using ExtraCompleteFunc = void (ClientImpl::*)(const op::Op*); -// static const ExtraCompleteFunc Map[] = { -// /* Type_Connect */ &ClientImpl::opComplete_Connect, -// /* Type_KeepAlive */ &ClientImpl::opComplete_KeepAlive, -// /* Type_Disconnect */ &ClientImpl::opComplete_Disconnect, -// /* Type_Subscribe */ &ClientImpl::opComplete_Subscribe, -// /* Type_Unsubscribe */ &ClientImpl::opComplete_Unsubscribe, -// /* Type_Recv */ &ClientImpl::opComplete_Recv, -// /* Type_Send */ &ClientImpl::opComplete_Send, -// }; -// static const std::size_t MapSize = std::extent::value; -// static_assert(MapSize == op::Op::Type_NumOfValues); - -// auto idx = static_cast(op->type()); -// COMMS_ASSERT(idx < MapSize); -// if (MapSize <= idx) { -// return; -// } + *iter = nullptr; + m_opsDeleted = true; + + using ExtraCompleteFunc = void (ClientImpl::*)(const op::Op*); + static const ExtraCompleteFunc Map[] = { + // /* Type_Connect */ &ClientImpl::opComplete_Connect, + // /* Type_KeepAlive */ &ClientImpl::opComplete_KeepAlive, + // /* Type_Disconnect */ &ClientImpl::opComplete_Disconnect, + // /* Type_Subscribe */ &ClientImpl::opComplete_Subscribe, + // /* Type_Unsubscribe */ &ClientImpl::opComplete_Unsubscribe, + // /* Type_Recv */ &ClientImpl::opComplete_Recv, + // /* Type_Send */ &ClientImpl::opComplete_Send, + }; + static const std::size_t MapSize = std::extent::value; + static_assert(MapSize == op::Op::Type_NumOfValues); + + auto idx = static_cast(op->type()); + COMMS_ASSERT(idx < MapSize); + if (MapSize <= idx) { + return; + } -// auto func = Map[idx]; -// (this->*func)(op); -// } + auto func = Map[idx]; + (this->*func)(op); +} // void ClientImpl::brokerConnected(bool sessionPresent) // { diff --git a/client/lib/src/ClientImpl.h b/client/lib/src/ClientImpl.h index 703ed65..ab84fd4 100644 --- a/client/lib/src/ClientImpl.h +++ b/client/lib/src/ClientImpl.h @@ -20,7 +20,7 @@ // #include "op/ConnectOp.h" // #include "op/DisconnectOp.h" // #include "op/KeepAliveOp.h" -// #include "op/Op.h" +#include "op/Op.h" // #include "op/RecvOp.h" // #include "op/SendOp.h" // #include "op/SubscribeOp.h" @@ -132,6 +132,12 @@ class ClientImpl final : public ProtMsgHandler m_errorLogData = data; } + void setGwinfoDelayReqCb(CC_MqttsnGwinfoDelayRequestCb cb, void* data) + { + m_gwinfoDelayReqCb = cb; + m_gwinfoDelayReqData = data; + } + // -------------------- Message Handling ----------------------------- // using Base::handle; @@ -151,8 +157,8 @@ class ClientImpl final : public ProtMsgHandler // -------------------- Ops Access API ----------------------------- - // CC_MqttsnErrorCode sendMessage(const ProtMessage& msg); - // void opComplete(const op::Op* op); + CC_MqttsnErrorCode sendMessage(const ProtMessage& msg, unsigned broadcastRadius = 0); + void opComplete(const op::Op* op); // void brokerConnected(bool sessionPresent); // void brokerDisconnected( // CC_MqttsnBrokerDisconnectReason reason = CC_MqttsnBrokerDisconnectReason_ValuesLimit, @@ -236,9 +242,9 @@ class ClientImpl final : public ProtMsgHandler // using SendOpAlloc = ObjAllocator; // using SendOpsList = ObjListType; - // using OpPtrsList = ObjListType; + using OpPtrsList = ObjListType; // using OpToDeletePtrsList = ObjListType; - // using OutputBuf = ObjListType; + using OutputBuf = ObjListType; // enum TerminateMode // { @@ -291,6 +297,9 @@ class ClientImpl final : public ProtMsgHandler CC_MqttsnErrorLogCb m_errorLogCb = nullptr; void* m_errorLogData = nullptr; + CC_MqttsnGwinfoDelayRequestCb m_gwinfoDelayReqCb = nullptr; + void* m_gwinfoDelayReqData = nullptr; + ConfigState m_configState; // ClientState m_clientState; // SessionState m_sessionState; @@ -299,7 +308,7 @@ class ClientImpl final : public ProtMsgHandler TimerMgr m_timerMgr; unsigned m_apiEnterCount = 0U; - // OutputBuf m_buf; + OutputBuf m_buf; ProtFrame m_frame; @@ -324,8 +333,8 @@ class ClientImpl final : public ProtMsgHandler // SendOpAlloc m_sendOpsAlloc; // SendOpsList m_sendOps; - // OpPtrsList m_ops; - // bool m_opsDeleted = false; + OpPtrsList m_ops; + bool m_opsDeleted = false; // bool m_preparationLocked = false; }; diff --git a/client/lib/src/ConfigState.h b/client/lib/src/ConfigState.h index 729c859..bc26662 100644 --- a/client/lib/src/ConfigState.h +++ b/client/lib/src/ConfigState.h @@ -16,8 +16,12 @@ struct ConfigState { static constexpr unsigned DefaultResponseTimeoutMs = 10000; static constexpr unsigned DefaultRetryCount = 3U; + static constexpr unsigned DefaultBroadcastRadius = 3U; + static constexpr unsigned MaxBroadcastRadius = 255U; + unsigned m_responseTimeoutMs = DefaultResponseTimeoutMs; unsigned m_retryCount = DefaultRetryCount; + unsigned m_broadcastRadius = DefaultBroadcastRadius; // CC_MqttsnPublishOrdering m_publishOrdering = CC_MqttsnPublishOrdering_SameQos; // bool m_verifyOutgoingTopic = Config::HasTopicFormatVerification; // bool m_verifyIncomingTopic = Config::HasTopicFormatVerification; diff --git a/client/lib/src/op/Op.cpp b/client/lib/src/op/Op.cpp new file mode 100644 index 0000000..2521e9f --- /dev/null +++ b/client/lib/src/op/Op.cpp @@ -0,0 +1,245 @@ +// +// Copyright 2024 - 2024 (C). Alex Robenko. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "op/Op.h" + +#include "ClientImpl.h" + +#include "comms/util/ScopeGuard.h" +#include "comms/cast.h" + +#include +#include + +namespace cc_mqttsn_client +{ + +namespace op +{ + +namespace +{ + +// static constexpr char TopicSep = '/'; +// static constexpr char MultLevelWildcard = '#'; +// static constexpr char SingleLevelWildcard = '+'; + +} // namespace + + + +Op::Op(ClientImpl& client) : + m_client(client), + m_responseTimeoutMs(client.configState().m_responseTimeoutMs), + m_retryCount(client.configState().m_retryCount) +{ +} + +void Op::sendMessage(const ProtMessage& msg, unsigned broadcastRadius) +{ + m_client.sendMessage(msg, broadcastRadius); +} + +void Op::terminateOpImpl([[maybe_unused]] CC_MqttsnAsyncOpStatus status) +{ + opComplete(); +} + +void Op::opComplete() +{ + m_client.opComplete(this); +} + +// std::uint16_t Op::allocPacketId() +// { +// static constexpr auto MaxPacketId = std::numeric_limits::max(); +// auto& allocatedPacketIds = m_client.clientState().m_allocatedPacketIds; + +// if ((allocatedPacketIds.max_size() <= allocatedPacketIds.size()) || +// (MaxPacketId <= allocatedPacketIds.size())) { +// errorLog("No more available packet IDs for allocation"); +// return 0U; +// } + +// auto& lastPacketId = m_client.clientState().m_lastPacketId; +// auto nextPacketId = static_cast(lastPacketId + 1U); + +// if (nextPacketId == 0U) { +// nextPacketId = 1U; +// } + +// while (true) { +// if (allocatedPacketIds.empty() || (allocatedPacketIds.back() < nextPacketId)) { +// allocatedPacketIds.push_back(nextPacketId); +// break; +// } + +// auto iter = std::lower_bound(allocatedPacketIds.begin(), allocatedPacketIds.end(), nextPacketId); +// if ((iter == allocatedPacketIds.end()) || (*iter != nextPacketId)) { +// allocatedPacketIds.insert(iter, nextPacketId); +// break; +// } + +// ++nextPacketId; +// } + +// lastPacketId = static_cast(nextPacketId); +// return lastPacketId; +// } + +// void Op::releasePacketId(std::uint16_t id) +// { +// if (id == 0U) { +// return; +// } + +// auto& allocatedPacketIds = m_client.clientState().m_allocatedPacketIds; +// auto iter = std::lower_bound(allocatedPacketIds.begin(), allocatedPacketIds.end(), id); +// if ((iter == allocatedPacketIds.end()) || (*iter != id)) { +// [[maybe_unused]] static constexpr bool ShouldNotHappen = false; +// COMMS_ASSERT(ShouldNotHappen); +// return; +// } + +// allocatedPacketIds.erase(iter); +// } + +void Op::errorLogInternal(const char* msg) +{ + if constexpr (Config::HasErrorLog) { + m_client.errorLog(msg); + } +} + +// bool Op::verifySubFilterInternal(const char* filter) +// { +// if (Config::HasTopicFormatVerification) { +// if (!m_client.configState().m_verifyOutgoingTopic) { +// return true; +// } + +// COMMS_ASSERT(filter != nullptr); +// if (filter[0] == '\0') { +// return false; +// } + +// auto pos = 0U; +// int lastSep = -1; +// while (filter[pos] != '\0') { +// auto incPosGuard = +// comms::util::makeScopeGuard( +// [&pos]() +// { +// ++pos; +// }); + +// auto ch = filter[pos]; + +// if (ch == TopicSep) { +// comms::cast_assign(lastSep) = pos; +// continue; +// } + +// if (ch == MultLevelWildcard) { + +// if (filter[pos + 1] != '\0') { +// errorLog("Multi-level wildcard \'#\' must be last."); +// return false; +// } + +// if (pos == 0U) { +// return true; +// } + +// if ((lastSep < 0) || (static_cast(pos - 1U) != lastSep)) { +// errorLog("Multi-level wildcard \'#\' must follow separator."); +// return false; +// } + +// return true; +// } + +// if (ch != SingleLevelWildcard) { +// continue; +// } + +// auto nextCh = filter[pos + 1]; +// if ((nextCh != '\0') && (nextCh != TopicSep)) { +// errorLog("Single-level wildcard \'+\' must be last of followed by /."); +// return false; +// } + +// if (pos == 0U) { +// continue; +// } + +// if ((lastSep < 0) || (static_cast(pos - 1U) != lastSep)) { +// errorLog("Single-level wildcard \'+\' must follow separator."); +// return false; +// } +// } + +// return true; +// } +// else { +// [[maybe_unused]] static constexpr bool ShouldNotBeCalled = false; +// COMMS_ASSERT(ShouldNotBeCalled); +// return false; +// } +// } + +// bool Op::verifyPubTopicInternal(const char* topic, bool outgoing) +// { +// if (Config::HasTopicFormatVerification) { +// if (outgoing && (!m_client.configState().m_verifyOutgoingTopic)) { +// return true; +// } + +// if ((!outgoing) && (!m_client.configState().m_verifyIncomingTopic)) { +// return true; +// } + +// COMMS_ASSERT(topic != nullptr); +// if (topic[0] == '\0') { +// return false; +// } + +// if (outgoing && (topic[0] == '$')) { +// errorLog("Cannot start topic with \'$\'."); +// return false; +// } + +// auto pos = 0U; +// while (topic[pos] != '\0') { +// auto incPosGuard = +// comms::util::makeScopeGuard( +// [&pos]() +// { +// ++pos; +// }); + +// auto ch = topic[pos]; + +// if ((ch == MultLevelWildcard) || +// (ch == SingleLevelWildcard)) { +// errorLog("Wildcards cannot be used in publish topic"); +// return false; +// } +// } + +// return true; +// } +// else { +// [[maybe_unused]] static constexpr bool ShouldNotBeCalled = false; +// COMMS_ASSERT(ShouldNotBeCalled); +// return false; +// } +// } + +} // namespace op + +} // namespace cc_mqttsn_client diff --git a/client/lib/src/op/Op.h b/client/lib/src/op/Op.h new file mode 100644 index 0000000..26e0b86 --- /dev/null +++ b/client/lib/src/op/Op.h @@ -0,0 +1,136 @@ +// +// Copyright 2024 - 2024 (C). Alex Robenko. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "ExtConfig.h" +#include "ObjListType.h" +#include "ProtocolDefs.h" + +#include "cc_mqttsn_client/common.h" + +#include + +namespace cc_mqttsn_client +{ + +class ClientImpl; + +namespace op +{ + +class Op : public ProtMsgHandler +{ +public: + enum Type + { + // Type_Connect, + // Type_KeepAlive, + // Type_Disconnect, + // Type_Subscribe, + // Type_Unsubscribe, + // Type_Recv, + // Type_Send, + Type_NumOfValues // Must be last + }; + + using Qos = cc_mqttsn::field::QosCommon::ValueType; + + virtual ~Op() noexcept = default; + + Type type() const + { + return typeImpl(); + } + + void terminateOp(CC_MqttsnAsyncOpStatus status) + { + terminateOpImpl(status); + } + + unsigned getResponseTimeout() const + { + return m_responseTimeoutMs; + } + + void setResponseTimeout(unsigned ms) + { + m_responseTimeoutMs = ms; + } + + inline + static bool verifyQosValid(Qos qos) + { + return (qos <= static_cast(Config::MaxQos)); + } + +protected: + explicit Op(ClientImpl& client); + + virtual Type typeImpl() const = 0; + virtual void terminateOpImpl(CC_MqttsnAsyncOpStatus status); + + void sendMessage(const ProtMessage& msg, unsigned broadcastRadius = 0U); + void opComplete(); + // std::uint16_t allocPacketId(); + // void releasePacketId(std::uint16_t id); + + ClientImpl& client() + { + return m_client; + } + + const ClientImpl& client() const + { + return m_client; + } + + inline void errorLog(const char* msg) + { + if constexpr (Config::HasErrorLog) { + errorLogInternal(msg); + } + } + + // inline bool verifySubFilter(const char* filter) + // { + // if (Config::HasTopicFormatVerification) { + // return verifySubFilterInternal(filter); + // } + // else { + // return true; + // } + // } + + // inline bool verifyPubTopic(const char* topic, bool outgoing) + // { + // if (Config::HasTopicFormatVerification) { + // return verifyPubTopicInternal(topic, outgoing); + // } + // else { + // return true; + // } + // } + + static constexpr std::size_t maxStringLen() + { + return std::numeric_limits::max(); + } + +private: + void errorLogInternal(const char* msg); + // bool verifySubFilterInternal(const char* filter); + // bool verifyPubTopicInternal(const char* topic, bool outgoing); + + ClientImpl& m_client; + unsigned m_responseTimeoutMs = 0U; + unsigned m_retryCount = 0U; +}; + +} // namespace op + +} // namespace cc_mqttsn_client diff --git a/client/lib/templ/Config.h.templ b/client/lib/templ/Config.h.templ index cd585cd..9eda962 100644 --- a/client/lib/templ/Config.h.templ +++ b/client/lib/templ/Config.h.templ @@ -8,7 +8,7 @@ struct Config static constexpr bool HasDynMemAlloc = ##CC_MQTTSN_CLIENT_HAS_DYN_MEM_ALLOC_CPP##; static constexpr unsigned ClientAllocLimit = ##CC_MQTTSN_CLIENT_ALLOC_LIMIT##; // static constexpr unsigned StringFieldFixedLen = ##CC_MQTTSN_CLIENT_STRING_FIELD_FIXED_LEN##; -// static constexpr unsigned MaxOutputPacketSize = ##CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE##; + static constexpr unsigned MaxOutputPacketSize = ##CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE##; static constexpr unsigned ReceiveMaxLimit = ##CC_MQTTSN_CLIENT_RECEIVE_MAX_LIMIT##; static constexpr unsigned SendMaxLimit = ##CC_MQTTSN_CLIENT_SEND_MAX_LIMIT##; static constexpr unsigned SubscribeOpsLimit = ##CC_MQTTSN_CLIENT_ASYNC_SUBS_LIMIT##; @@ -21,7 +21,7 @@ struct Config static_assert(HasDynMemAlloc || (ClientAllocLimit > 0U), "Must use CC_MQTTSN_CLIENT_ALLOC_LIMIT in configuration to limit number of clients"); // static_assert(HasDynMemAlloc || (StringFieldFixedLen > 0U), "Must use CC_MQTTSN_CLIENT_STRING_FIELD_FIXED_LEN in configuration to limit string field length"); -// static_assert(HasDynMemAlloc || (MaxOutputPacketSize > 0U), "Must use CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE in configuration to limit packet size"); + static_assert(HasDynMemAlloc || (MaxOutputPacketSize > 0U), "Must use CC_MQTTSN_CLIENT_MAX_OUTPUT_PACKET_SIZE in configuration to limit packet size"); // static_assert(HasDynMemAlloc || (ReceiveMaxLimit > 0U) || (MaxQos < 2), "Must use CC_MQTTSN_CLIENT_RECEIVE_MAX_LIMIT in configuration to limit amount of messages to receive"); static_assert(HasDynMemAlloc || (SendMaxLimit > 0U), "Must use CC_MQTTSN_CLIENT_SEND_MAX_LIMIT in configuration to limit amount of messages to send"); static_assert(HasDynMemAlloc || (SubscribeOpsLimit > 0U), "Must use CC_MQTTSN_CLIENT_ASYNC_SUBS_LIMIT in configuration to limit amount of unfinished subscribes."); diff --git a/client/lib/templ/client.cpp.templ b/client/lib/templ/client.cpp.templ index 6a72a05..ddec5b8 100644 --- a/client/lib/templ/client.cpp.templ +++ b/client/lib/templ/client.cpp.templ @@ -12,6 +12,8 @@ #include "comms/Assert.h" //#include "comms/util/ScopeGuard.h" +#include + struct CC_MqttsnClient {}; namespace @@ -46,6 +48,76 @@ void cc_mqttsn_##NAME##client_free(CC_MqttsnClientHandle handle) getClientAllocator().free(clientFromHandle(handle)); } +void cc_mqttsn_##NAME##client_tick(CC_MqttsnClientHandle client, unsigned ms) +{ + COMMS_ASSERT(client != nullptr); + clientFromHandle(client)->tick(ms); +} + +void cc_mqttsn_##NAME##client_process_data(CC_MqttsnClientHandle client, const unsigned char* buf, unsigned bufLen) +{ + COMMS_ASSERT(client != nullptr); + clientFromHandle(client)->processData(buf, bufLen); +} + +CC_MqttsnErrorCode cc_mqttsn_##NAME##client_set_retry_period(CC_MqttsnClientHandle client, unsigned value) +{ + COMMS_ASSERT(client != nullptr); + static const unsigned MaxValue = std::numeric_limits::max() / 1000U; + if (MaxValue < value) { + clientFromHandle(client)->errorLog("The retry period value is too high"); + return CC_MqttsnErrorCode_BadParam; + } + + clientFromHandle(client)->configState().m_responseTimeoutMs = value * 1000U; + return CC_MqttsnErrorCode_Success; +} + +unsigned cc_mqttsn_##NAME##client_get_retry_period(CC_MqttsnClientHandle client) +{ + COMMS_ASSERT(client != nullptr); + return clientFromHandle(client)->configState().m_responseTimeoutMs; +} + +CC_MqttsnErrorCode cc_mqttsn_##NAME##client_set_retry_count(CC_MqttsnClientHandle client, unsigned value) +{ + COMMS_ASSERT(client != nullptr); + if (value < 1U) { + clientFromHandle(client)->errorLog("The retry count setting must be greater than zero."); + return CC_MqttsnErrorCode_BadParam; + } + + COMMS_ASSERT(client != nullptr); + clientFromHandle(client)->configState().m_retryCount = value; + return CC_MqttsnErrorCode_Success; +} + +unsigned cc_mqttsn_##NAME##client_get_retry_count(CC_MqttsnClientHandle client) +{ + COMMS_ASSERT(client != nullptr); + return clientFromHandle(client)->configState().m_retryCount; +} + +CC_MqttsnErrorCode cc_mqttsn_##NAME##client_set_broadcast_radius(CC_MqttsnClientHandle client, unsigned value) +{ + COMMS_ASSERT(client != nullptr); + if (cc_mqttsn_client::ConfigState::MaxBroadcastRadius < value) { + clientFromHandle(client)->errorLog("The broadcast radius is too high"); + return CC_MqttsnErrorCode_BadParam; + } + + clientFromHandle(client)->configState().m_broadcastRadius = value; + return CC_MqttsnErrorCode_Success; +} + +unsigned cc_mqttsn_##NAME##client_get_broadcast_radius(CC_MqttsnClientHandle client) +{ + COMMS_ASSERT(client != nullptr); + return clientFromHandle(client)->configState().m_broadcastRadius; +} + +// --------------------- Callbacks --------------------- + void cc_mqttsn_##NAME##client_set_next_tick_program_callback( CC_MqttsnClientHandle client, CC_MqttsnNextTickProgramCb cb, @@ -100,38 +172,11 @@ void cc_mqttsn_##NAME##client_set_message_report_callback( clientFromHandle(client)->setMessageReceivedCallback(cb, data); } -void cc_mqttsn_##NAME##client_tick(CC_MqttsnClientHandle client, unsigned ms) -{ - COMMS_ASSERT(client != nullptr); - clientFromHandle(client)->tick(ms); -} - -void cc_mqttsn_##NAME##client_process_data(CC_MqttsnClientHandle client, const unsigned char* buf, unsigned bufLen) -{ - COMMS_ASSERT(client != nullptr); - clientFromHandle(client)->processData(buf, bufLen); -} - -void cc_mqttsn_##NAME##client_set_retry_period(CC_MqttsnClientHandle client, unsigned value) -{ - COMMS_ASSERT(client != nullptr); - clientFromHandle(client)->configState().m_responseTimeoutMs = value; -} - -unsigned cc_mqttsn_##NAME##client_get_retry_period(CC_MqttsnClientHandle client) -{ - COMMS_ASSERT(client != nullptr); - return clientFromHandle(client)->configState().m_responseTimeoutMs; -} - -void cc_mqttsn_##NAME##client_set_retry_count(CC_MqttsnClientHandle client, unsigned value) +void cc_mqttsn_##NAME##client_set_gwinfo_delay_request_callback( + CC_MqttsnClientHandle client, + CC_MqttsnGwinfoDelayRequestCb cb, + void* data) { COMMS_ASSERT(client != nullptr); - clientFromHandle(client)->configState().m_retryCount = value; + clientFromHandle(client)->setGwinfoDelayReqCb(cb, data); } - -unsigned cc_mqttsn_##NAME##client_get_retry_count(CC_MqttsnClientHandle client) -{ - COMMS_ASSERT(client != nullptr); - return clientFromHandle(client)->configState().m_retryCount; -} \ No newline at end of file diff --git a/client/lib/templ/client.h.templ b/client/lib/templ/client.h.templ index d1bb4c8..62cb98c 100644 --- a/client/lib/templ/client.h.templ +++ b/client/lib/templ/client.h.templ @@ -64,7 +64,7 @@ void cc_mqttsn_##NAME##client_process_data(CC_MqttsnClientHandle client, const u /// @b 10 seconds. /// @param[in] client Handle returned by @ref cc_mqttsn_##NAME##client_new() function. /// @param[in] value Number of @b seconds to wait before making an attempt to resend. -void cc_mqttsn_##NAME##client_set_retry_period(CC_MqttsnClientHandle client, unsigned value); +CC_MqttsnErrorCode cc_mqttsn_##NAME##client_set_retry_period(CC_MqttsnClientHandle client, unsigned value); /// @brief Set configured retry period to wait between resending unacknowledged message to the gateway (@b Tretry from spec). /// @param[in] client Handle returned by @ref cc_mqttsn_##NAME##client_new() function. @@ -78,13 +78,27 @@ unsigned cc_mqttsn_##NAME##client_get_retry_period(CC_MqttsnClientHandle client) /// is @b 3. /// @param[in] client Handle returned by @ref cc_mqttsn_##NAME##client_new() function. /// @param[in] value Number of retry attempts. -void cc_mqttsn_##NAME##client_set_retry_count(CC_MqttsnClientHandle client, unsigned value); +CC_MqttsnErrorCode cc_mqttsn_##NAME##client_set_retry_count(CC_MqttsnClientHandle client, unsigned value); /// @brief Get configured number of retry attempts to perform before reporting unsuccessful result of the operation (@b Nretry from spec). /// @param[in] client Handle returned by @ref cc_mqttsn_##NAME##client_new() function. /// @see @ref cc_mqttsn_##NAME##client_set_retry_count() unsigned cc_mqttsn_##NAME##client_get_retry_count(CC_MqttsnClientHandle client); +/// @brief Set broadcast radius. +/// @details When searching for gateways, the client library broadcasts @b SEARCHGW +/// messages. It contains the broadcast radius value. This value can be +/// set using this function. Default radius value is @b 3. +/// @param[in] client Handle returned by @ref cc_mqttsn_##NAME##client_new() function. +/// @param[in] value Broadcast radius. +/// @pre The broadcast value cannot exceed 255. +CC_MqttsnErrorCode cc_mqttsn_##NAME##client_set_broadcast_radius(CC_MqttsnClientHandle client, unsigned value); + +/// @brief Get current broadcast radius configuration. +/// @param[in] client Handle returned by @ref cc_mqttsn_##NAME##client_new() function. +/// @see @ref cc_mqttsn_##NAME##client_set_broadcast_radius() +unsigned cc_mqttsn_##NAME##client_get_broadcast_radius(CC_MqttsnClientHandle client); + // --------------------- Callbacks --------------------- /// @brief Set callback to call when time measurement is required. @@ -162,6 +176,14 @@ void cc_mqttsn_##NAME##client_set_message_report_callback( CC_MqttsnMessageReportCb cb, void* data); +/// @brief Set callback to request a random timeout to send @b GWINFO as a response to the @b SEARCHGW from other client. +/// @details According to the MQTT-SN specification, the client can send @b GWINFO message on behalf of a gateway +/// after some randrom amount of time. Use this function to allow the library to +/// request such timeout. If not set, sending @b GWINFO on behalf of the gateway is @b disabled. +void cc_mqttsn_##NAME##client_set_gwinfo_delay_request_callback( + CC_MqttsnClientHandle client, + CC_MqttsnGwinfoDelayRequestCb cb, + void* data); #ifdef __cplusplus }