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
19#pragma once
20
21#include "cc_tools_qt/ToolsExtraInfoMessage.h"
22#include "cc_tools_qt/ToolsFrameCommon.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 ToolsFrameCommon<TMsgBase>
41{
42 using Base = ToolsFrameCommon<TMsgBase>;
43public:
44 using ProtInterface = typename Base::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
84 using ProtMsgPtr = typename ProtFrame::MsgPtr;
85 using ReadIter = typename ProtInterface::ReadIterator;
86 while (consumed < m_inData.size()) {
87 ProtMsgPtr msgPtr;
88
89 ReadIter readIterBeg = m_inData.data() + consumed;
90 ReadIter readIter = readIterBeg;
91 auto remLen = m_inData.size() - consumed;
92 assert(0U < remLen);
93
94 qlonglong msgId = 0;
95 std::size_t idx = 0;
96 auto es =
97 m_frame.read(
98 msgPtr,
99 readIter,
100 remLen,
101 comms::protocol::msgId(msgId),
102 comms::protocol::msgIndex(idx));
103
104 if (es == comms::ErrorStatus::NotEnoughData) {
105 break;
106 }
107
108 if (es == comms::ErrorStatus::MsgAllocFailure) {
109 [[maybe_unused]] static constexpr bool Must_not_be_happen = false;
110 assert(Must_not_be_happen);
111 break;
112 }
113
114 if (es != comms::ErrorStatus::Success) {
115 m_garbage.push_back(*readIterBeg);
116 static constexpr std::size_t GarbageLimit = 512;
117 if (GarbageLimit <= m_garbage.size()) {
118 checkGarbageFunc();
119 }
120
121 ++consumed;
122 continue;
123 }
124
125 checkGarbageFunc();
126 assert(msgPtr);
127 auto diff = static_cast<std::size_t>(std::distance(readIterBeg, readIter));
128 consumed += diff;
129
130 auto toolsMsg = m_factory.createMessage(msgId, static_cast<unsigned>(idx));
131 if (!toolsMsg) {
132 [[maybe_unused]] static constexpr bool Protocol_and_Tools_Frames_Out_of_Sync = false;
133 assert(Protocol_and_Tools_Frames_Out_of_Sync);
134
135 m_garbage.reserve(m_garbage.size() + diff);
136 m_garbage.insert(m_garbage.end(), readIterBeg, readIter);
137 checkGarbageFunc();
138 continue;
139 }
140
141 toolsMsg->assignProtMessage(msgPtr.get());
142
143 DataSeq data(readIterBeg, readIter);
144 updateTransportInternal(data, *toolsMsg);
145 updateRawDataInternal(data, *toolsMsg);
146 allMsgs.push_back(std::move(toolsMsg));
147 }
148
149 static_cast<void>(final);
150
151 assert(consumed <= m_inData.size());
152 m_inData.erase(m_inData.begin(), m_inData.begin() + 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 ProtInterface& msg) override
228 {
229 DataSeq data;
230 data.reserve(m_frame.length(msg));
231
232 auto writeIter = std::back_inserter(data);
233 auto es = m_frame.write(msg, writeIter, data.max_size());
234 if (es == comms::ErrorStatus::UpdateRequired) {
235 auto updateIter = data.data();
236 es = m_frame.update(msg, updateIter, data.size());
237 }
238
239 if (es != comms::ErrorStatus::Success) {
240 [[maybe_unused]] static constexpr bool Unexpected_write_update_failure = false;
241 assert(Unexpected_write_update_failure);
242 data.clear();
243 }
244
245 return data;
246 }
247
248private:
249 void updateTransportInternal(const DataSeq& data, ToolsMessage& msg)
250 {
251 ToolsMessagePtr transportMsg(new TransportMsg);
252 if (!transportMsg->decodeData(data)) {
253 std::cerr << "ERROR: Failed to decode transport message: " << std::hex;
254 std::copy(data.begin(), data.end(), std::ostream_iterator<unsigned>(std::cerr, " "));
255 std::cerr << std::dec << std::endl;
256
257 [[maybe_unused]] static constexpr bool Must_not_be_happen = false;
258 assert(Must_not_be_happen);
259 }
260
261 property::message::ToolsMsgTransportMsg().setTo(std::move(transportMsg), msg);
262 }
263
264 void updateRawDataInternal(const DataSeq& data, ToolsMessage& msg)
265 {
266 ToolsMessagePtr rawDataMsg(new RawDataMsg);
267 if (!rawDataMsg->decodeData(data)) {
268 std::cerr << "ERROR: Failed to decode raw data message: " << std::hex;
269 std::copy(data.begin(), data.end(), std::ostream_iterator<unsigned>(std::cerr, " "));
270 std::cerr << std::dec << std::endl;
271
272 [[maybe_unused]] static constexpr bool Must_not_be_happen = false;
273 assert(Must_not_be_happen);
274 }
275
276 property::message::ToolsMsgRawDataMsg().setTo(std::move(rawDataMsg), msg);
277 }
278
279 void updateExtraInfoInternal(const DataSeq& jsonRawBytes, ToolsMessage& msg)
280 {
281 ToolsMessagePtr extraInfoMsg(new ExtraInfoMsg);
282 if (!extraInfoMsg->decodeData(jsonRawBytes)) {
283 std::cerr << "ERROR: Failed to decode extra info:\n";
284 std::copy(jsonRawBytes.begin(), jsonRawBytes.end(), std::ostream_iterator<char>(std::cerr, " "));
285 std::cerr << std::endl;
286
287 [[maybe_unused]] static constexpr bool Must_not_be_happen = false;
288 assert(Must_not_be_happen);
289 }
290
291 property::message::ToolsMsgExtraInfoMsg().setTo(std::move(extraInfoMsg), msg);
292 }
293
294 ProtFrame m_frame;
295 TMsgFactory m_factory;
296 DataSeq m_inData;
297 DataSeq m_garbage;
298};
299
300} // namespace cc_tools_qt
301
302
Main namespace for all classes / functions of the shared library.
std::shared_ptr< ToolsMessage > ToolsMessagePtr
Smart pointer to ToolsMessage.
Definition ToolsMessage.h:163