From 9ee72984b9cd034203bba314a70b45d92b8b292e Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Wed, 29 May 2024 08:50:33 +1000 Subject: [PATCH 01/11] Updating next release version to be v5.3.1 --- lib/include/cc_tools_qt/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/include/cc_tools_qt/version.h b/lib/include/cc_tools_qt/version.h index 3db1f4d..0b4a976 100644 --- a/lib/include/cc_tools_qt/version.h +++ b/lib/include/cc_tools_qt/version.h @@ -28,7 +28,7 @@ #define CC_TOOLS_QT_MINOR_VERSION 3U /// @brief Patch level of the library -#define CC_TOOLS_QT_PATCH_VERSION 0U +#define CC_TOOLS_QT_PATCH_VERSION 1U /// @brief Macro to create numeric version as single unsigned number #define CC_TOOLS_QT_MAKE_VERSION(major_, minor_, patch_) \ From 954589a956cdacc541915822c1f6c98b49a231db Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Tue, 18 Jun 2024 07:53:50 +1000 Subject: [PATCH 02/11] Fixing UdpProxySocketPlugin and UdpGenericSocketPlugin having the same plugin id. --- plugin/udp_socket/proxy/UdpProxySocketPlugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/udp_socket/proxy/UdpProxySocketPlugin.h b/plugin/udp_socket/proxy/UdpProxySocketPlugin.h index caeead4..78cb7d5 100644 --- a/plugin/udp_socket/proxy/UdpProxySocketPlugin.h +++ b/plugin/udp_socket/proxy/UdpProxySocketPlugin.h @@ -33,7 +33,7 @@ namespace plugin class UdpProxySocketPlugin : public cc_tools_qt::Plugin { Q_OBJECT - Q_PLUGIN_METADATA(IID "cc.UdpSocketPlugin" FILE "udp_proxy_socket.json") + Q_PLUGIN_METADATA(IID "cc.UdpProxySocketPlugin" FILE "udp_proxy_socket.json") Q_INTERFACES(cc_tools_qt::Plugin) public: From 6cf3fcaef53d4f387476b76ee6a5e9792d0283e8 Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Thu, 20 Jun 2024 08:40:44 +1000 Subject: [PATCH 03/11] Extra bundles testing in the demo protocol. --- demo/cc_plugin/AllMessages.h | 4 +- demo/cc_plugin/CMakeLists.txt | 1 + demo/cc_plugin/DemoTransportMessage.cpp | 3 +- demo/cc_plugin/message/Bundles.cpp | 129 +++++++++++++++ demo/cc_plugin/message/Bundles.h | 57 +++++++ demo/include/demo/DefaultOptions.h | 25 +++ demo/include/demo/MsgId.h | 3 +- demo/include/demo/message/Bundles.h | 207 ++++++++++++++++++++++++ 8 files changed, 426 insertions(+), 3 deletions(-) create mode 100644 demo/cc_plugin/message/Bundles.cpp create mode 100644 demo/cc_plugin/message/Bundles.h create mode 100644 demo/include/demo/message/Bundles.h diff --git a/demo/cc_plugin/AllMessages.h b/demo/cc_plugin/AllMessages.h index 652ed12..4a39338 100644 --- a/demo/cc_plugin/AllMessages.h +++ b/demo/cc_plugin/AllMessages.h @@ -30,6 +30,7 @@ #include "cc_plugin/message/Optionals.h" #include "cc_plugin/message/FloatValues.h" #include "cc_plugin/message/Variants.h" +#include "cc_plugin/message/Bundles.h" namespace demo { @@ -46,7 +47,8 @@ using AllMessages = std::tuple< cc_plugin::message::Lists, cc_plugin::message::Optionals, cc_plugin::message::FloatValues, - cc_plugin::message::Variants + cc_plugin::message::Variants, + cc_plugin::message::Bundles >; static_assert(std::tuple_size::value == MsgId_NumOfValues, diff --git a/demo/cc_plugin/CMakeLists.txt b/demo/cc_plugin/CMakeLists.txt index 28dccdf..4902a0c 100644 --- a/demo/cc_plugin/CMakeLists.txt +++ b/demo/cc_plugin/CMakeLists.txt @@ -28,6 +28,7 @@ function (cc_plugin_demo) message/Optionals.cpp message/FloatValues.cpp message/Variants.cpp + message/Bundles.cpp ) add_library (${name} MODULE ${src}) diff --git a/demo/cc_plugin/DemoTransportMessage.cpp b/demo/cc_plugin/DemoTransportMessage.cpp index 3355c9d..5cc44b5 100644 --- a/demo/cc_plugin/DemoTransportMessage.cpp +++ b/demo/cc_plugin/DemoTransportMessage.cpp @@ -43,7 +43,8 @@ QVariantMap createMsgIdProperties() "Lists", "Optionals", "FloatValues", - "Variants" + "Variants", + "Bundles", }; static const auto NamesCount = std::extent::value; diff --git a/demo/cc_plugin/message/Bundles.cpp b/demo/cc_plugin/message/Bundles.cpp new file mode 100644 index 0000000..7c3586a --- /dev/null +++ b/demo/cc_plugin/message/Bundles.cpp @@ -0,0 +1,129 @@ +// +// Copyright 2015 - 2018 (C). Alex Robenko. All rights reserved. +// + +// This file is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "Bundles.h" + +namespace cc = cc_tools_qt; + +namespace demo +{ + +namespace cc_plugin +{ + +namespace message +{ + +namespace +{ + +using BundlesFields = demo::message::BundlesFields<>; + +QVariantMap createProps_field1_mem1() +{ + using Field = BundlesFields::field1_mem1; + auto props = + cc::property::field::ForField() + .name("length"); + return props.asMap(); +} + +QVariantMap createProps_field1_mem2() +{ + using Field = BundlesFields::field1_mem2; + auto props = + cc::property::field::ForField() + .name("str"); + return props.asMap(); +} + +QVariantMap createProps_field1() +{ + using Field = BundlesFields::field1; + auto props = + cc::property::field::ForField() + .name("field1") + .serialisedHidden() + .add(createProps_field1_mem1()) + .add(createProps_field1_mem2()); + assert(props.members().size() == Field::FieldIdx_numOfValues); + return props.asMap(); +} + +QVariantMap createProps_field2_mem1() +{ + using Field = BundlesFields::field2_mem1; + auto props = + cc::property::field::ForField() + .name("length"); + return props.asMap(); +} + +QVariantMap createProps_field2_mem2() +{ + using Field = BundlesFields::field2_mem2; + auto props = + cc::property::field::ForField() + .name("data"); + return props.asMap(); +} + +QVariantMap createProps_field2() +{ + using Field = BundlesFields::field2; + auto props = + cc::property::field::ForField() + .name("field2") + .serialisedHidden() + .add(createProps_field2_mem1()) + .add(createProps_field2_mem2()); + assert(props.members().size() == Field::FieldIdx_numOfValues); + return props.asMap(); +} + +QVariantList createFieldsProperties() +{ + QVariantList props; + props.append(createProps_field1()); + props.append(createProps_field2()); + + assert(props.size() == Bundles::FieldIdx_numOfValues); + return props; +} + +} // namespace + +Bundles::Bundles() = default; +Bundles::~Bundles() noexcept = default; + +Bundles& Bundles::operator=(const Bundles&) = default; +Bundles& Bundles::operator=(Bundles&&) = default; + +const QVariantList& Bundles::fieldsPropertiesImpl() const +{ + static const auto Props = createFieldsProperties(); + return Props; +} + +} // namespace message + +} // namespace cc_plugin + +} // namespace demo + diff --git a/demo/cc_plugin/message/Bundles.h b/demo/cc_plugin/message/Bundles.h new file mode 100644 index 0000000..03e282c --- /dev/null +++ b/demo/cc_plugin/message/Bundles.h @@ -0,0 +1,57 @@ +// +// Copyright 2015 - 2018 (C). Alex Robenko. All rights reserved. +// + +// This file is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +#pragma once + +#include "cc_tools_qt/cc_tools_qt.h" +#include "demo/message/Bundles.h" +#include "cc_plugin/DemoMessage.h" + +namespace demo +{ + +namespace cc_plugin +{ + +namespace message +{ + +class Bundles : public + cc_tools_qt::ProtocolMessageBase< + demo::message::Bundles, + Bundles> +{ +public: + Bundles(); + Bundles(const Bundles&) = delete; + Bundles(Bundles&&) = delete; + virtual ~Bundles() noexcept; + + Bundles& operator=(const Bundles&); + Bundles& operator=(Bundles&&); + +protected: + virtual const QVariantList& fieldsPropertiesImpl() const override; +}; + +} // namespace message + +} // namespace cc_plugin + +} // namespace demo + diff --git a/demo/include/demo/DefaultOptions.h b/demo/include/demo/DefaultOptions.h index c5c0c08..8bedbf0 100644 --- a/demo/include/demo/DefaultOptions.h +++ b/demo/include/demo/DefaultOptions.h @@ -206,6 +206,28 @@ struct DefaultOptions using field2 = comms::option::EmptyOption; }; + /// @brief Extra options for fields in @ref demo::message::BundlesFields struct + struct BundlesFields + { + /// @brief Extra options for @ref demo::message::BundlesFields::field1_mem1 + using field1_mem1 = comms::option::EmptyOption; + + /// @brief Extra options for @ref demo::message::BundlesFields::field1_mem2 + using field1_mem2 = comms::option::EmptyOption; + + /// @brief Extra options for @ref demo::message::BundlesFields::field1 + using field1 = comms::option::EmptyOption; + + /// @brief Extra options for @ref demo::message::BundlesFields::field2_mem1 + using field2_mem1 = comms::option::EmptyOption; + + /// @brief Extra options for @ref demo::message::BundlesFields::field2_mem2 + using field2_mem2 = comms::option::EmptyOption; + + /// @brief Extra options for @ref demo::message::BundlesFields::field2 + using field2 = comms::option::EmptyOption; + }; + /// @brief Extra options for @ref demo::message::Bitfields message using Bitfields = comms::option::EmptyOption; @@ -232,6 +254,9 @@ struct DefaultOptions /// @brief Extra options for @ref demo::message::Variants message using Variants = comms::option::EmptyOption; + + /// @brief Extra options for @ref demo::message::Bundles message + using Bundles = comms::option::EmptyOption; }; }; diff --git a/demo/include/demo/MsgId.h b/demo/include/demo/MsgId.h index 142c7d2..92febea 100644 --- a/demo/include/demo/MsgId.h +++ b/demo/include/demo/MsgId.h @@ -1,5 +1,5 @@ // -// Copyright 2016 (C). Alex Robenko. All rights reserved. +// Copyright 2016 - 2024 (C). Alex Robenko. All rights reserved. // // This file is free software: you can redistribute it and/or modify @@ -37,6 +37,7 @@ enum MsgId : std::uint8_t MsgId_Optionals, ///< Message containing optional fields MsgId_FloatValues, ///< Floating point values accumulating message MsgId_Variants, ///< Variant values accumulating message + MsgId_Bundles, ///< Bundles accumulating message MsgId_NumOfValues ///< Limit to valid message IDs, must be last }; diff --git a/demo/include/demo/message/Bundles.h b/demo/include/demo/message/Bundles.h new file mode 100644 index 0000000..02a5aae --- /dev/null +++ b/demo/include/demo/message/Bundles.h @@ -0,0 +1,207 @@ +// +// Copyright 2016 - 2024 (C). Alex Robenko. All rights reserved. +// + +// This file is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/// @file +/// @brief Contains definition of Bundles message and its fields. + +#pragma once + +#include "comms/fields.h" +#include "comms/MessageBase.h" +#include "demo/MsgId.h" +#include "demo/FieldBase.h" +#include "demo/DefaultOptions.h" + +namespace demo +{ + + +namespace message +{ + +/// @brief Accumulates details of all the Bundles message fields. +/// @tparam TOpt Extra options +/// @see Bundles +template +struct BundlesFields +{ + /// @brief First member that can be stored in @ref field1 bundle field. + using field1_mem1 = + comms::field::IntValue< + demo::FieldBase, + std::uint8_t, + typename TOpt::message::BundlesFields::field1_mem1 + >; + + /// @brief Second member that can be stored in @ref field1 bundle field. + using field1_mem2 = + comms::field::String< + demo::FieldBase, + typename TOpt::message::BundlesFields::field1_mem2, + comms::option::def::SequenceFixedSize<8> + >; + + /// @brief Bundle field containing length and fixed size string. + class field1 : public + comms::field::Bundle< + demo::FieldBase, + std::tuple< + field1_mem1, + field1_mem2 + >, + comms::option::def::RemLengthMemberField<0>, + typename TOpt::message::BundlesFields::field1 + > + { + using Base = + comms::field::Bundle< + demo::FieldBase, + std::tuple< + field1_mem1, + field1_mem2 + >, + comms::option::def::RemLengthMemberField<0>, + typename TOpt::message::BundlesFields::field1 + >; + + public: + /// @brief Allow access to internal fields. + COMMS_FIELD_MEMBERS_NAMES(length, str) + }; + + /// @brief First member that can be stored in @ref field2 bundle field. + using field2_mem1 = + comms::field::IntValue< + demo::FieldBase, + std::uint8_t, + typename TOpt::message::BundlesFields::field2_mem1 + >; + + /// @brief Second member that can be stored in @ref field2 bundle field. + using field2_mem2 = + comms::field::ArrayList< + demo::FieldBase, + std::uint8_t, + typename TOpt::message::BundlesFields::field2_mem2, + comms::option::def::SequenceFixedSize<8> + >; + + /// @brief Bundle field containing length and fixed size data. + class field2 : public + comms::field::Bundle< + demo::FieldBase, + std::tuple< + field2_mem1, + field2_mem2 + >, + comms::option::def::RemLengthMemberField<0>, + typename TOpt::message::BundlesFields::field2 + > + { + using Base = + comms::field::Bundle< + demo::FieldBase, + std::tuple< + field2_mem1, + field2_mem2 + >, + comms::option::def::RemLengthMemberField<0>, + typename TOpt::message::BundlesFields::field2 + >; + + public: + /// @brief Allow access to internal fields. + COMMS_FIELD_MEMBERS_NAMES(length, data) + }; + + /// @brief All the fields bundled in std::tuple. + using All = std::tuple< + field1, + field2 + >; +}; + +/// @brief Accumulates various string fields. +/// @details Inherits from +/// @b comms::MessageBase +/// while providing @b TMsgBase as common interface class as well as +/// various implementation options. @n +/// See @ref BundlesFields for definition of the fields this message contains. +/// @tparam TOpt Extra options +template +class Bundles : public + comms::MessageBase< + TMsgBase, + typename TOpt::message::Bundles, + comms::option::StaticNumIdImpl, + comms::option::FieldsImpl::All>, + comms::option::MsgType >, + comms::option::HasName + > +{ + // Required for compilation with gcc earlier than v5.0, + // later versions don't require this type definition. + using Base = + comms::MessageBase< + TMsgBase, + typename TOpt::message::Bundles, + comms::option::StaticNumIdImpl, + comms::option::FieldsImpl::All>, + comms::option::MsgType >, + comms::option::HasName + >; + + static const bool AreFieldsVersionDependent = Base::areFieldsVersionDependent(); + static_assert(!AreFieldsVersionDependent, "Fields mustn't be version dependent"); +public: + + /// @brief Allow access to internal fields. + /// @details See definition of @b COMMS_MSG_FIELDS_NAMES macro + /// related to @b comms::MessageBase class from COMMS library + /// for details. + /// + COMMS_MSG_FIELDS_NAMES(field1, field2); + + /// @brief Default constructor + Bundles() = default; + + /// @brief Copy constructor + Bundles(const Bundles&) = default; + + /// @brief Move constructor + Bundles(Bundles&& other) = default; + + /// @brief Destructor + ~Bundles() noexcept = default; + + /// @brief Copy assignment + Bundles& operator=(const Bundles&) = default; + + /// @brief Move assignment + Bundles& operator=(Bundles&&) = default; + + /// @brief Name of the message. + static const char* doName() + { + return "Bundles"; + } +}; + +} // namespace message + +} // namespace demo + From 5c7b1e008a3f0d12ef870c7402a52ee4c123b5ba Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Fri, 21 Jun 2024 08:16:06 +1000 Subject: [PATCH 04/11] Improving creation of all messages with sparse IDs. --- lib/include/cc_tools_qt/ProtocolBase.h | 67 ++++++++++++++++++++------ 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/lib/include/cc_tools_qt/ProtocolBase.h b/lib/include/cc_tools_qt/ProtocolBase.h index 0c28c72..d119c12 100644 --- a/lib/include/cc_tools_qt/ProtocolBase.h +++ b/lib/include/cc_tools_qt/ProtocolBase.h @@ -476,18 +476,27 @@ class ProtocolBase : public Protocol using Tag = typename std::conditional< std::is_void::value, - NoMsgFactoryTag, + CreateWithTupleIterationTag, HasMsgFactoryTag >::type; - return createAllMessagesInTupleInternal(Tag()); + auto allMsgs = createAllMessagesInTupleInternal(Tag()); + for (auto& msgPtr : allMsgs) { + setNameToMessageProperties(*msgPtr); + setForceExtraInfoExistenceToMessageProperties(*msgPtr); + updateMessage(*msgPtr); + } + + return allMsgs; } private: struct NumericIdTag {}; struct OtherIdTag {}; struct HasMsgFactoryTag{}; - struct NoMsgFactoryTag{}; + struct HasStaticIdsTag{}; + struct CreateWithLoopIterationTag{}; + struct CreateWithTupleIterationTag{}; typedef typename std::conditional< (std::is_enum::value || std::is_integral::value), @@ -582,20 +591,32 @@ class ProtocolBase : public Protocol } template - MessagesList createAllMessagesInTupleInternal(NoMsgFactoryTag) + MessagesList createAllMessagesInTupleInternal(CreateWithTupleIterationTag) { MessagesList allMsgs; comms::util::tupleForEachType(AllMsgsCreateHelper(allMsgs)); - for (auto& msgPtr : allMsgs) { - setNameToMessageProperties(*msgPtr); - setForceExtraInfoExistenceToMessageProperties(*msgPtr); - updateMessage(*msgPtr); - } return allMsgs; } template MessagesList createAllMessagesInTupleInternal(HasMsgFactoryTag) + { + static_assert(std::tuple_size::value > 0U, "At least one message is expected to be defined"); + using FirstType = typename std::tuple_element<0, TMsgsTuple>::type; + using LastType = typename std::tuple_element::value - 1U, TMsgsTuple>::type; + + using Tag = + std::conditional_t< + FirstType::hasStaticMsgId() && LastType::hasStaticMsgId(), + HasStaticIdsTag, + CreateWithLoopIterationTag + >; + + return createAllMessagesInTupleInternal(Tag()); + } + + template + MessagesList createAllMessagesInTupleInternal(CreateWithLoopIterationTag) { static_assert(std::tuple_size::value > 0U, "At least one message is expected to be defined"); using FirstType = typename std::tuple_element<0, TMsgsTuple>::type; @@ -615,14 +636,32 @@ class ProtocolBase : public Protocol } } - for (auto& msgPtr : allMsgs) { - setNameToMessageProperties(*msgPtr); - setForceExtraInfoExistenceToMessageProperties(*msgPtr); - updateMessage(*msgPtr); - } return allMsgs; } + template + MessagesList createAllMessagesInTupleInternal(HasStaticIdsTag) + { + static_assert(std::tuple_size::value > 0U, "At least one message is expected to be defined"); + using FirstType = typename std::tuple_element<0, TMsgsTuple>::type; + using LastType = typename std::tuple_element::value - 1U, TMsgsTuple>::type; + static_assert(FirstType::hasStaticMsgId(), "Invalid displatch"); + static_assert(LastType::hasStaticMsgId(), "Invalid displatch"); + + static const auto FirstId = FirstType::staticMsgId(); + static const auto LastId = LastType::staticMsgId(); + + // When to sparse, use tuple iteration + using Tag = + std::conditional_t< + static_cast(FirstId - LastId) <= (std::tuple_size::value * 5), + CreateWithLoopIterationTag, + CreateWithTupleIterationTag + >; + + return createAllMessagesInTupleInternal(Tag()); + } + ProtocolStack m_protStack; std::vector m_data; std::vector m_garbage; From 75341943649f697e719d69dbe9730189117f2fa4 Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Fri, 21 Jun 2024 09:07:01 +1000 Subject: [PATCH 05/11] Storing and reusing the message index information when saving and loading files. --- lib/include/cc_tools_qt/ProtocolBase.h | 9 +----- lib/include/cc_tools_qt/property/message.h | 12 ++++++++ lib/src/MsgFileMgr.cpp | 34 ++++++++++++++++++++++ lib/src/Protocol.cpp | 24 +++++++++++++-- lib/src/property/message.cpp | 3 ++ 5 files changed, 72 insertions(+), 10 deletions(-) diff --git a/lib/include/cc_tools_qt/ProtocolBase.h b/lib/include/cc_tools_qt/ProtocolBase.h index d119c12..7803772 100644 --- a/lib/include/cc_tools_qt/ProtocolBase.h +++ b/lib/include/cc_tools_qt/ProtocolBase.h @@ -480,14 +480,7 @@ class ProtocolBase : public Protocol HasMsgFactoryTag >::type; - auto allMsgs = createAllMessagesInTupleInternal(Tag()); - for (auto& msgPtr : allMsgs) { - setNameToMessageProperties(*msgPtr); - setForceExtraInfoExistenceToMessageProperties(*msgPtr); - updateMessage(*msgPtr); - } - - return allMsgs; + return createAllMessagesInTupleInternal(Tag()); } private: diff --git a/lib/include/cc_tools_qt/property/message.h b/lib/include/cc_tools_qt/property/message.h index 903a2fc..c93ad87 100644 --- a/lib/include/cc_tools_qt/property/message.h +++ b/lib/include/cc_tools_qt/property/message.h @@ -120,6 +120,18 @@ class CC_API Type : public PropBase static const QByteArray PropName; }; +class CC_API MsgIdx : public PropBase +{ + typedef PropBase Base; +public: + MsgIdx() : Base(Name, PropName) {} + +private: + static const QString Name; + static const QByteArray PropName; +}; + + class CC_API Timestamp : public PropBase { typedef PropBase Base; diff --git a/lib/src/MsgFileMgr.cpp b/lib/src/MsgFileMgr.cpp index fd9ef6a..de2ff90 100644 --- a/lib/src/MsgFileMgr.cpp +++ b/lib/src/MsgFileMgr.cpp @@ -48,6 +48,19 @@ class IdProp : public property::message::PropBase const QString IdProp::Name("id"); const QByteArray IdProp::PropName = IdProp::Name.toUtf8(); +class MsgIdxProp : public property::message::PropBase +{ + typedef property::message::PropBase Base; +public: + MsgIdxProp() : Base(Name, PropName) {} +private: + static const QString Name; + static const QByteArray PropName; +}; + +const QString MsgIdxProp::Name("msg_idx"); +const QByteArray MsgIdxProp::PropName = MsgIdxProp::Name.toUtf8(); + class DataProp : public property::message::PropBase { typedef property::message::PropBase Base; @@ -220,6 +233,7 @@ MessagePtr createMsgObjectFrom( auto msgMap = msgMapVar.value(); auto msgId = IdProp().getFrom(msgMap); + auto msgIdx = MsgIdxProp().getFrom(msgMap); auto dataStr = DataProp().getFrom(msgMap); if (msgId.isEmpty() && dataStr.isEmpty()) { @@ -275,6 +289,23 @@ MessagePtr createMsgObjectFrom( return msg; } + do { + if (msgIdx == 0U) { + break; + } + + msg = protocol.createMessage(msgId, msgIdx); + if (!msg) { + break; + } + + if (!msg->decodeData(data)) { + msg.reset(); + break; + } + + } while (false); + unsigned idx = 0; while (!msg) { msg = protocol.createMessage(msgId, idx); @@ -312,7 +343,9 @@ QVariantMap convertRecvMsg(const Message& msg) if (!idStr.isEmpty()) { IdProp().setTo(std::move(idStr), msgInfoMap); + MsgIdxProp().setTo(property::message::MsgIdx().getFrom(msg), msgInfoMap); } + DataProp().setTo(std::move(dataStr), msgInfoMap); TimestampProp().setTo(property::message::Timestamp().getFrom(msg), msgInfoMap); TypeProp().setTo(static_cast(property::message::Type().getFrom(msg)), msgInfoMap); @@ -397,6 +430,7 @@ QVariantList convertSendMsgList( QVariantMap msgInfoMap; IdProp().setTo(msg->idAsString(), msgInfoMap); + MsgIdxProp().setTo(property::message::MsgIdx().getFrom(*msg), msgInfoMap); DataProp().setTo(encodeMsgData(*msg), msgInfoMap); DelayProp().setTo(property::message::Delay().getFrom(*msg), msgInfoMap); DelayUnitsProp().setTo(property::message::DelayUnits().getFrom(*msg), msgInfoMap); diff --git a/lib/src/Protocol.cpp b/lib/src/Protocol.cpp index 8c61eda..f1f4c10 100644 --- a/lib/src/Protocol.cpp +++ b/lib/src/Protocol.cpp @@ -135,12 +135,32 @@ DataInfoPtr Protocol::write(Message& msg) Protocol::MessagesList Protocol::createAllMessages() { - return createAllMessagesImpl(); + auto allMsgs = createAllMessagesImpl(); + QString prevId; + unsigned prevIdx = 0U; + for (auto& msgPtr : allMsgs) { + unsigned idx = 0U; + if (prevId == msgPtr->idAsString()) { + idx = prevIdx + 1U; + } + + prevId = msgPtr->idAsString(); + prevIdx = idx; + property::message::MsgIdx().setTo(idx, *msgPtr); + + setNameToMessageProperties(*msgPtr); + setForceExtraInfoExistenceToMessageProperties(*msgPtr); + updateMessage(*msgPtr); + } + + return allMsgs; } MessagePtr Protocol::createMessage(const QString& idAsString, unsigned idx) { - return createMessageImpl(idAsString, idx); + auto msgPtr = createMessageImpl(idAsString, idx); + property::message::MsgIdx().setTo(idx, *msgPtr); + return msgPtr; } Protocol::UpdateStatus Protocol::updateMessage(Message& msg) diff --git a/lib/src/property/message.cpp b/lib/src/property/message.cpp index 8591b98..d5a827d 100644 --- a/lib/src/property/message.cpp +++ b/lib/src/property/message.cpp @@ -29,6 +29,9 @@ namespace message const QString Type::Name("cc.msg_type"); const QByteArray Type::PropName = Type::Name.toUtf8(); +const QString MsgIdx::Name("cc.msg_idx"); +const QByteArray MsgIdx::PropName = MsgIdx::Name.toUtf8(); + const QString Timestamp::Name("cc.msg_timestamp"); const QByteArray Timestamp::PropName = Timestamp::Name.toUtf8(); From fd249870bdf592dc74e0dcad89336f38669f71dc Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Fri, 21 Jun 2024 09:28:08 +1000 Subject: [PATCH 06/11] Fix to storing message idx. --- lib/src/Protocol.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/Protocol.cpp b/lib/src/Protocol.cpp index f1f4c10..8f51da7 100644 --- a/lib/src/Protocol.cpp +++ b/lib/src/Protocol.cpp @@ -159,7 +159,9 @@ Protocol::MessagesList Protocol::createAllMessages() MessagePtr Protocol::createMessage(const QString& idAsString, unsigned idx) { auto msgPtr = createMessageImpl(idAsString, idx); - property::message::MsgIdx().setTo(idx, *msgPtr); + if (msgPtr) { + property::message::MsgIdx().setTo(idx, *msgPtr); + } return msgPtr; } From 56de9f079e352e004e462e7142c40e15f56fbfad Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Fri, 21 Jun 2024 09:47:24 +1000 Subject: [PATCH 07/11] Using std::maxint_t for the numeric message ID type when creating the message. --- lib/include/cc_tools_qt/ProtocolBase.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/include/cc_tools_qt/ProtocolBase.h b/lib/include/cc_tools_qt/ProtocolBase.h index 7803772..94d5f5d 100644 --- a/lib/include/cc_tools_qt/ProtocolBase.h +++ b/lib/include/cc_tools_qt/ProtocolBase.h @@ -560,9 +560,9 @@ class ProtocolBase : public Protocol MessagePtr result; do { bool ok = false; - int numId = idAsString.toInt(&ok, 10); + std::intmax_t numId = static_cast(idAsString.toLongLong(&ok, 10)); if (!ok) { - numId = idAsString.toInt(&ok, 16); + numId = static_cast(idAsString.toLongLong(&ok, 16)); if (!ok) { break; } From baec7582922227233c6a9cc899a3c05970ca5f9e Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Fri, 21 Jun 2024 10:53:17 +1000 Subject: [PATCH 08/11] Simplifying generic UDP socket plugin. --- .../udp_socket/generic/UdpGenericSocket.cpp | 46 ++++--------------- plugin/udp_socket/generic/UdpGenericSocket.h | 3 -- 2 files changed, 10 insertions(+), 39 deletions(-) diff --git a/plugin/udp_socket/generic/UdpGenericSocket.cpp b/plugin/udp_socket/generic/UdpGenericSocket.cpp index a0c9f49..9afdae0 100644 --- a/plugin/udp_socket/generic/UdpGenericSocket.cpp +++ b/plugin/udp_socket/generic/UdpGenericSocket.cpp @@ -111,32 +111,22 @@ UdpGenericSocket::UdpGenericSocket() connect( &m_socket, &QUdpSocket::readyRead, this, &UdpGenericSocket::readFromSocket); - connect( - &m_broadcastSocket, &QUdpSocket::readyRead, - this, &UdpGenericSocket::readFromBroadcastSocket); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) connect( &m_socket, &QUdpSocket::errorOccurred, this, &UdpGenericSocket::socketErrorOccurred); - connect( - &m_broadcastSocket, &QUdpSocket::errorOccurred, - this, &UdpGenericSocket::socketErrorOccurred); #else // #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) connect( &m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketErrorOccurred(QAbstractSocket::SocketError))); - connect( - &m_broadcastSocket, SIGNAL(error(QAbstractSocket::SocketError)), - this, SLOT(socketErrorOccurred(QAbstractSocket::SocketError))); #endif // #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) } UdpGenericSocket::~UdpGenericSocket() noexcept { m_socket.blockSignals(true); - m_broadcastSocket.blockSignals(true); } bool UdpGenericSocket::socketConnectImpl() @@ -156,7 +146,6 @@ bool UdpGenericSocket::socketConnectImpl() } assert(!m_socket.isOpen()); - assert(!m_broadcastSocket.isOpen()); m_running = true; do { @@ -168,10 +157,6 @@ bool UdpGenericSocket::socketConnectImpl() reportError("Failed to bind UDP socket to port " + QString("%1").arg(m_localPort)); } - if (!bindSocket(m_broadcastSocket)) { - reportError("Failed to bind broadcast UDP socket to port " + QString("%1").arg(m_localPort)); - } - } while (false); do { @@ -191,7 +176,6 @@ void UdpGenericSocket::socketDisconnectImpl() { m_socket.blockSignals(true); m_socket.close(); - m_broadcastSocket.close(); m_running = false; m_socket.blockSignals(false); } @@ -212,7 +196,7 @@ void UdpGenericSocket::sendDataImpl(DataInfoPtr dataPtr) } if ((!broadcastRequested) || - (!m_broadcastSocket.isOpen()) || + (!m_socket.isOpen()) || (m_port == 0)) { break; } @@ -227,7 +211,7 @@ void UdpGenericSocket::sendDataImpl(DataInfoPtr dataPtr) while (writtenCount < dataPtr->m_data.size()) { auto remSize = static_cast(dataPtr->m_data.size() - writtenCount); auto count = - m_broadcastSocket.writeDatagram( + m_socket.writeDatagram( reinterpret_cast(&dataPtr->m_data[writtenCount]), remSize, QHostAddress(broadcastMask), @@ -340,29 +324,14 @@ void UdpGenericSocket::socketDisconnected() void UdpGenericSocket::readFromSocket() { - readData(m_socket); -} - -void UdpGenericSocket::readFromBroadcastSocket() -{ - readData(m_broadcastSocket); -} - -void UdpGenericSocket::socketErrorOccurred([[maybe_unused]] QAbstractSocket::SocketError err) -{ - std::cout << "ERROR: UDP Socket: " << m_socket.errorString().toStdString() << std::endl; -} - -void UdpGenericSocket::readData(QUdpSocket& socket) -{ - while (socket.hasPendingDatagrams()) { + while (m_socket.hasPendingDatagrams()) { QHostAddress senderAddress; quint16 senderPort; auto dataPtr = makeDataInfo(); dataPtr->m_timestamp = DataInfo::TimestampClock::now(); - dataPtr->m_data.resize(static_cast(socket.pendingDatagramSize())); - socket.readDatagram( + dataPtr->m_data.resize(static_cast(m_socket.pendingDatagramSize())); + m_socket.readDatagram( reinterpret_cast(&dataPtr->m_data[0]), static_cast(dataPtr->m_data.size()), &senderAddress, @@ -388,6 +357,11 @@ void UdpGenericSocket::readData(QUdpSocket& socket) } } +void UdpGenericSocket::socketErrorOccurred([[maybe_unused]] QAbstractSocket::SocketError err) +{ + std::cout << "ERROR: UDP Socket: " << m_socket.errorString().toStdString() << std::endl; +} + bool UdpGenericSocket::bindSocket(QUdpSocket& socket) { if (!socket.bind(QHostAddress::AnyIPv4, m_localPort, QUdpSocket::ShareAddress)) { diff --git a/plugin/udp_socket/generic/UdpGenericSocket.h b/plugin/udp_socket/generic/UdpGenericSocket.h index 288eba9..cc54a6e 100644 --- a/plugin/udp_socket/generic/UdpGenericSocket.h +++ b/plugin/udp_socket/generic/UdpGenericSocket.h @@ -99,11 +99,9 @@ class UdpGenericSocket : public QObject, public cc_tools_qt::Socket private slots: void socketDisconnected(); void readFromSocket(); - void readFromBroadcastSocket(); void socketErrorOccurred(QAbstractSocket::SocketError err); private: - void readData(QUdpSocket& socket); bool bindSocket(QUdpSocket& socket); static const PortType DefaultPort = UDP_GENERIC_DEFAULT_PORT; @@ -113,7 +111,6 @@ private slots: PortType m_localPort = 0; QString m_broadcastMask = "255.255.255.255"; QUdpSocket m_socket; - QUdpSocket m_broadcastSocket; bool m_running = false; }; From ffe088a3d3788e5999626b03b1f5de4283009fd4 Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Sat, 22 Jun 2024 10:43:36 +1000 Subject: [PATCH 09/11] Removed appveyor configuration. --- .appveyor.yml | 64 ---------------------------------- script/appveyor_install.bat | 68 ------------------------------------- 2 files changed, 132 deletions(-) delete mode 100644 .appveyor.yml delete mode 100644 script/appveyor_install.bat diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 7979b7f..0000000 --- a/.appveyor.yml +++ /dev/null @@ -1,64 +0,0 @@ -image: - - Visual Studio 2022 - - Visual Studio 2019 - - Visual Studio 2017 - -init: - - git config --global core.autocrlf input - -clone_folder: c:\projects\cc_tools_qt -shallow_clone: true - -platform: - - x64 - - x86 - -configuration: - - Debug - - Release - -environment: - COMMS_TAG: v5.2.3 - matrix: - - CPP_STD: 17 - QT_MAJOR: 5 - - CPP_STD: 17 - QT_MAJOR: 6 - - CPP_STD: 20 - QT_MAJOR: 5 - - CPP_STD: 20 - QT_MAJOR: 6 - -matrix: - fast_finish: false - exclude: - - image: Visual Studio 2017 - CPP_STD: 20 - - image: Visual Studio 2017 - QT_MAJOR: 6 - - platform: x86 - QT_MAJOR: 6 - -install: - - call script\appveyor_install.bat - - set PATH=%PATH%;%QTDIR%\bin - - set BUILD_DIR=%APPVEYOR_BUILD_FOLDER%\build.%PLATFORM%.%CONFIGURATION%.%TOOLCHAIN% - - if exist %BUILD_DIR% rmdir /S /Q %BUILD_DIR% - - set COMMS_TAG=%COMMS_TAG% - - set COMMON_INSTALL_DIR=%BUILD_DIR%\install - - set COMMON_BUILD_TYPE=%CONFIGURATION% - - set COMMON_CXX_STANDARD=%CPP_STD% - - set GENERATOR="%CMAKE_GENERATOR%" - - set PLATFORM="%CMAKE_PLATFORM%" - - call script\prepare_externals.bat - -build_script: - - echo ------------------------- Building Project ------------------------- - - cd %BUILD_DIR% - - cmake .. -DCMAKE_BUILD_TYPE=%CONFIGURATION% -G "%CMAKE_GENERATOR%" %PLATFORM_PARAM% -DCMAKE_INSTALL_PREFIX="%COMMON_INSTALL_DIR%" ^ - -DCMAKE_PREFIX_PATH="%COMMON_INSTALL_DIR%;%QTDIR%" -DCMAKE_CXX_STANDARD=%CPP_STD% ^ - -DCC_TOOLS_QT_BUILD_PLUGIN_DEMO_PROTOCOL=ON -DCC_TOOLS_QT_MAJOR_QT_VERSION=%QT_MAJOR% - - cmake --build . --config %CONFIGURATION% --target install - - echo -------------------------- Deploying Qt --------------------------- - - cmake --build . --config %CONFIGURATION% --target deploy_qt - diff --git a/script/appveyor_install.bat b/script/appveyor_install.bat deleted file mode 100644 index b0ee3d6..0000000 --- a/script/appveyor_install.bat +++ /dev/null @@ -1,68 +0,0 @@ -IF "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" ( - set TOOLCHAIN=msvc15 - set QT_SUBDIR=msvc2017 - set QT_VER=5.13 - set CMAKE_GENERATOR=NMake Makefiles - IF "%PLATFORM%"=="x86" ( - echo Performing x86 build in VS2017 - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars32.bat" - ) ELSE ( - echo Performing amd64 build in VS2017 - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" - ) -) ELSE IF "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" ( - set TOOLCHAIN=msvc16 - set QT_SUBDIR=msvc2019 - set CMAKE_GENERATOR=Visual Studio 16 2019 - IF "%QT_MAJOR%"=="6" ( - set QT_VER=6.4 - ) ELSE ( - set QT_VER=5.15 - ) - IF "%PLATFORM%"=="x86" ( - echo Performing x86 build in VS2019 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" - set CMAKE_PLATFORM=Win32 - ) ELSE ( - echo Performing amd64 build in VS2019 - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" - set CMAKE_PLATFORM=x64 - ) -) ELSE IF "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2022" ( - set TOOLCHAIN=msvc17 - set QT_SUBDIR=msvc2019 - set CMAKE_GENERATOR=Visual Studio 17 2022 - IF "%QT_MAJOR%"=="6" ( - set QT_VER=6.4 - ) ELSE ( - set QT_VER=5.15 - ) - IF "%PLATFORM%"=="x86" ( - echo Performing x86 build in VS2022 - call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars32.bat" - set CMAKE_PLATFORM=Win32 - ) ELSE ( - echo Performing amd64 build in VS2022 - call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" - set CMAKE_PLATFORM=x64 - ) -) ELSE ( - echo Toolchain %TOOLCHAIN% is not supported - exit -1 -) - -set QTDIR_PREFIX=C:\Qt\%QT_VER% -IF "%PLATFORM%"=="x86" ( - set QTDIR_SUFFIX= -) ELSE ( - set QTDIR_SUFFIX=_64 -) - -set QTDIR=%QTDIR_PREFIX%\%QT_SUBDIR%%QTDIR_SUFFIX% -IF NOT EXIST %QTDIR% ( - echo WARNING: %QTDIR% does not exist!!! - dir C:\Qt -) - -echo Using Qt%QT_MAJOR% from %QTDIR% - From 6490da6255ecb5a78141841c65958d4dc40d2f01 Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Sat, 22 Jun 2024 10:46:24 +1000 Subject: [PATCH 10/11] Requiring v5.2.4 of the comms library. --- .github/workflows/actions_build.yml | 2 +- lib/include/cc_tools_qt/version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/actions_build.yml b/.github/workflows/actions_build.yml index b629c67..34e5fff 100644 --- a/.github/workflows/actions_build.yml +++ b/.github/workflows/actions_build.yml @@ -3,7 +3,7 @@ name: Github Actions Build on: [push] env: - COMMS_TAG: v5.2.3 + COMMS_TAG: v5.2.4 jobs: diff --git a/lib/include/cc_tools_qt/version.h b/lib/include/cc_tools_qt/version.h index 0b4a976..daddd82 100644 --- a/lib/include/cc_tools_qt/version.h +++ b/lib/include/cc_tools_qt/version.h @@ -77,7 +77,7 @@ constexpr unsigned version() return CC_TOOLS_QT_VERSION; } -static_assert(COMMS_MAKE_VERSION(5, 2, 3) <= comms::version(), +static_assert(COMMS_MAKE_VERSION(5, 2, 4) <= comms::version(), "The version of COMMS library is too old"); } // namespace cc_tools_qt From ae05432806532f3dd572a643064f85c109492610 Mon Sep 17 00:00:00 2001 From: Alex Robenko Date: Sat, 22 Jun 2024 10:51:14 +1000 Subject: [PATCH 11/11] Allow selection of the ccache executable. --- CMakeLists.txt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 15f9b68..8fd84ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ option (CC_TOOLS_QT_BUILD_PLUGIN_DEMO_PROTOCOL "Build demo protocol plugin." OFF # Extra configuration variables # CC_TOOLS_QT_MAJOR_QT_VERSION - Major Qt version, defaults to 5. # CC_TOOLS_QT_DEFAULT_NETWORK_PORT - Set default network port for the network plugins. +# CC_TOOLS_QT_CCACHE_EXECUTABLE - Path to ccache executable # Used standard CMake variables # CMAKE_CXX_STANDARD - Defaults to 17. @@ -85,23 +86,24 @@ find_package(LibComms REQUIRED NO_MODULE) ####################################################################### # Compiler options -set (warn_opt) +set (extra_opts) if (CC_TOOLS_QT_WARN_AS_ERR) - set (warn_opt WARN_AS_ERR) + list (APPEND extra_opts WARN_AS_ERR) endif () -set (static_runtime_opt) if (CC_TOOLS_QT_STATIC_RUNTIME) - set (static_runtime_opt STATIC_RUNTIME) + list (APPEND extra_opts STATIC_RUNTIME) endif () -set (ccache_opt) -if ((UNIX) AND (CC_TOOLS_QT_USE_CCACHE)) - set (ccache_opt USE_CCACHE) +if (CC_TOOLS_QT_USE_CCACHE) + list (APPEND extra_opts USE_CCACHE) + if (NOT "${CC_TOOLS_QT_CCACHE_EXECUTABLE}" STREQUAL "") + list (APPEND extra_opts CCACHE_EXECUTABLE "${CC_TOOLS_QT_CCACHE_EXECUTABLE}") + endif () endif () include (${LibComms_DIR}/CC_Compile.cmake) -cc_compile(${warn_opt} ${static_runtime_opt} ${ccache_opt}) +cc_compile(${extra_opts}) cc_msvc_force_warn_opt("/W4") #######################################################################