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