cc_tools_qt
Common Environment for Protocol Analysis.
Loading...
Searching...
No Matches
ToolsFrameBase.h
1//
2// Copyright 2024 - 2026 (C). Alex Robenko. All rights reserved.
3//
4// SPDX-License-Identifier: GPL-3.0-or-later
5//
6
7// This file is free software: you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation, either version 3 of the License, or
10// (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20#pragma once
21
22#include "cc_tools_qt/ToolsExtraInfoMessage.h"
23#include "cc_tools_qt/ToolsInvalidMessage.h"
24#include "cc_tools_qt/ToolsRawDataMessage.h"
25#include "cc_tools_qt/property/message.h"
26
27#include <QtCore/QJsonObject>
28#include <QtCore/QJsonDocument>
29#include <QtCore/QByteArray>
30
31#include <algorithm>
32#include <cassert>
33#include <iterator>
34#include <iostream>
35
36namespace cc_tools_qt
37{
38
39template <typename TMsgBase, class TProtFrame, typename TMsgFactory, typename TTransportMsg>
40class ToolsFrameBase : public ToolsFrame
41{
42 using Base = ToolsFrame;
43public:
44 using ProtInterface = typename TMsgBase::ProtInterface;
45 using DataSeq = typename Base::DataSeq;
46
47 using TransportMsg = TTransportMsg;
48
50 using InvalidMsg = ToolsInvalidMessage<TMsgBase>;
51
52 using RawDataMsg = ToolsRawDataMessage<TMsgBase>;
53
55 using ExtraInfoMsg = ToolsExtraInfoMessage<TMsgBase>;
56
57 using ProtFrame = TProtFrame;
58
59 ToolsFrameBase() = default;
60
61protected:
62 virtual ToolsMessagesList readDataImpl(const ToolsDataInfo& dataInfo, bool final) override
63 {
64 m_inData.reserve(m_inData.size() + dataInfo.m_data.size());
65 m_inData.insert(m_inData.end(), dataInfo.m_data.begin(), dataInfo.m_data.end());
66
67 ToolsMessagesList allMsgs;
68 std::size_t consumed = 0U;
69
70 auto checkGarbageFunc =
71 [this, &allMsgs]()
72 {
73 if (m_garbage.empty()) {
74 return;
75 }
76
77 ToolsMessagePtr invalidMsgPtr(new InvalidMsg);
78 updateRawDataInternal(m_garbage, *invalidMsgPtr);
79 allMsgs.push_back(std::move(invalidMsgPtr));
80 m_garbage.clear();
81 };
82
83 using ProtMsgPtr = typename ProtFrame::MsgPtr;
84 using ReadIter = typename ProtInterface::ReadIterator;
85 while (consumed < m_inData.size()) {
86 ProtMsgPtr msgPtr;
87
88 ReadIter readIterBeg = m_inData.data() + consumed;
89 ReadIter readIter = readIterBeg;
90 auto remLen = m_inData.size() - consumed;
91 assert(0U < remLen);
92
93 qlonglong msgId = 0;
94 std::size_t idx = 0;
95 auto es =
96 m_frame.read(
97 msgPtr,
98 readIter,
99 remLen,
100 comms::frame::msgId(msgId),
101 comms::frame::msgIndex(idx));
102
103 if (es == comms::ErrorStatus::NotEnoughData) {
104 break;
105 }
106
107 if (es == comms::ErrorStatus::MsgAllocFailure) {
108 [[maybe_unused]] static constexpr bool Must_not_be_happen = false;
109 assert(Must_not_be_happen);
110 break;
111 }
112
113 if (es != comms::ErrorStatus::Success) {
114 m_garbage.push_back(*readIterBeg);
115 static constexpr std::size_t GarbageLimit = 512;
116 if (GarbageLimit <= m_garbage.size()) {
117 checkGarbageFunc();
118 }
119
120 ++consumed;
121 continue;
122 }
123
124 checkGarbageFunc();
125 assert(msgPtr);
126 auto diff = static_cast<std::size_t>(std::distance(readIterBeg, readIter));
127 consumed += diff;
128
129 auto toolsMsg = m_factory.createMessage(msgId, static_cast<unsigned>(idx));
130 if (!toolsMsg) {
131 [[maybe_unused]] static constexpr bool Protocol_and_Tools_Frames_Out_of_Sync = false;
132 assert(Protocol_and_Tools_Frames_Out_of_Sync);
133
134 m_garbage.reserve(m_garbage.size() + diff);
135 m_garbage.insert(m_garbage.end(), readIterBeg, readIter);
136 checkGarbageFunc();
137 continue;
138 }
139
140 toolsMsg->assignProtMessage(msgPtr.get());
141
142 DataSeq data(readIterBeg, readIter);
143 updateTransportInternal(data, *toolsMsg);
144 updateRawDataInternal(data, *toolsMsg);
145 allMsgs.push_back(std::move(toolsMsg));
146 }
147
148 static_cast<void>(final);
149
150 assert(consumed <= m_inData.size());
151 using DiffType = typename std::iterator_traits<DataSeq::iterator>::difference_type;
152 m_inData.erase(m_inData.begin(), m_inData.begin() + static_cast<DiffType>(consumed));
153
154 if (final && (!m_inData.empty())) {
155 m_garbage.reserve(m_garbage.size() + m_inData.size());
156 m_garbage.insert(m_garbage.end(), m_inData.begin(), m_inData.end());
157 m_inData.clear();
158 checkGarbageFunc();
159 }
160
161 if (!dataInfo.m_extraProperties.isEmpty()) {
162 auto jsonObj = QJsonObject::fromVariantMap(dataInfo.m_extraProperties);
163 QJsonDocument doc(jsonObj);
164 auto jsonData = doc.toJson();
165 DataSeq jsonRawBytes(jsonData.begin(), jsonData.end());
166
167 for (auto& m : allMsgs) {
168 property::message::ToolsMsgExtraInfo().setTo(dataInfo.m_extraProperties, *m);
169 updateExtraInfoInternal(jsonRawBytes, *m);
170 }
171 }
172
173 return allMsgs;
174 }
175
176 virtual void updateMessageImpl(ToolsMessage& msg) override
177 {
178 auto data = msg.encodeFramed(*this);
179 updateTransportInternal(data, msg);
180 updateRawDataInternal(data, msg);
181
182 auto extraProps = property::message::ToolsMsgExtraInfo().getFrom(msg);
183 bool extraInfoMsgIsForced = property::message::ToolsMsgForceExtraInfoExistence().getFrom(msg);
184 if (extraProps.isEmpty() && (!extraInfoMsgIsForced)) {
185 property::message::ToolsMsgExtraInfoMsg().setTo(ToolsMessagePtr(), msg);
186 return;
187 }
188
189 auto extraInfoMsgPtr = std::make_unique<ExtraInfoMsg>();
190 if (extraProps.isEmpty()) {
191 property::message::ToolsMsgExtraInfoMsg().setTo(ToolsMessagePtr(extraInfoMsgPtr.release()), msg);
192 return;
193 }
194
195 auto jsonObj = QJsonObject::fromVariantMap(extraProps);
196 QJsonDocument doc(jsonObj);
197 auto jsonData = doc.toJson();
198 DataSeq jsonRawBytes(jsonData.begin(), jsonData.end());
199 updateExtraInfoInternal(jsonRawBytes, msg);
200 }
201
202 virtual ToolsMessagePtr createInvalidMessageImpl() override
203 {
204 return ToolsMessagePtr(new InvalidMsg());
205 }
206
207 virtual ToolsMessagePtr createRawDataMessageImpl() override
208 {
209 return ToolsMessagePtr(new RawDataMsg());
210 }
211
212 virtual ToolsMessagePtr createExtraInfoMessageImpl() override
213 {
214 return ToolsMessagePtr(new ExtraInfoMsg());
215 }
216
217 virtual ToolsMessagesList createAllMessagesImpl() override
218 {
219 return m_factory.createAllMessages();
220 }
221
222 virtual ToolsMessagePtr createMessageImpl(const QString& idAsString, unsigned idx) override
223 {
224 return m_factory.createMessage(idAsString, idx);
225 }
226
227 virtual DataSeq writeProtMsgImpl(const void* protInterface) override
228 {
229 assert(protInterface != nullptr);
230 auto& msg = *(reinterpret_cast<const ProtInterface*>(protInterface));
231 DataSeq data;
232 data.reserve(m_frame.length(msg));
233
234 auto writeIter = std::back_inserter(data);
235 auto es = m_frame.write(msg, writeIter, data.max_size());
236 if (es == comms::ErrorStatus::UpdateRequired) {
237 auto updateIter = data.data();
238 es = m_frame.update(msg, updateIter, data.size());
239 }
240
241 if (es != comms::ErrorStatus::Success) {
242 [[maybe_unused]] static constexpr bool Unexpected_write_update_failure = false;
243 assert(Unexpected_write_update_failure);
244 data.clear();
245 }
246
247 return data;
248 }
249
250private:
251 void updateTransportInternal(const DataSeq& data, ToolsMessage& msg)
252 {
253 ToolsMessagePtr transportMsg(new TransportMsg);
254 if (!transportMsg->decodeData(data)) {
255 std::cerr << "ERROR: Failed to decode transport message: " << std::hex;
256 std::copy(data.begin(), data.end(), std::ostream_iterator<unsigned>(std::cerr, " "));
257 std::cerr << std::dec << std::endl;
258
259 [[maybe_unused]] static constexpr bool Must_not_be_happen = false;
260 assert(Must_not_be_happen);
261 }
262
263 property::message::ToolsMsgTransportMsg().setTo(std::move(transportMsg), msg);
264 }
265
266 void updateRawDataInternal(const DataSeq& data, ToolsMessage& msg)
267 {
268 ToolsMessagePtr rawDataMsg(new RawDataMsg);
269 if (!rawDataMsg->decodeData(data)) {
270 std::cerr << "ERROR: Failed to decode raw data message: " << std::hex;
271 std::copy(data.begin(), data.end(), std::ostream_iterator<unsigned>(std::cerr, " "));
272 std::cerr << std::dec << std::endl;
273
274 [[maybe_unused]] static constexpr bool Must_not_be_happen = false;
275 assert(Must_not_be_happen);
276 }
277
278 property::message::ToolsMsgRawDataMsg().setTo(std::move(rawDataMsg), msg);
279 }
280
281 void updateExtraInfoInternal(const DataSeq& jsonRawBytes, ToolsMessage& msg)
282 {
283 ToolsMessagePtr extraInfoMsg(new ExtraInfoMsg);
284 if (!extraInfoMsg->decodeData(jsonRawBytes)) {
285 std::cerr << "ERROR: Failed to decode extra info:\n";
286 std::copy(jsonRawBytes.begin(), jsonRawBytes.end(), std::ostream_iterator<char>(std::cerr, " "));
287 std::cerr << std::endl;
288
289 [[maybe_unused]] static constexpr bool Must_not_be_happen = false;
290 assert(Must_not_be_happen);
291 }
292
293 property::message::ToolsMsgExtraInfoMsg().setTo(std::move(extraInfoMsg), msg);
294 }
295
296 ProtFrame m_frame;
297 TMsgFactory m_factory;
298 DataSeq m_inData;
299 DataSeq m_garbage;
300};
301
302} // namespace cc_tools_qt
303
Main namespace for all classes / functions of the shared library.
std::shared_ptr< ToolsMessage > ToolsMessagePtr
Smart pointer to ToolsMessage.
Definition ToolsMessage.h:165