1 | // Copyright (C) 2022 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qcanbusframe.h" |
5 | #include "qcanframeprocessor.h" |
6 | #include "qcanframeprocessor_p.h" |
7 | #include "qcanmessagedescription.h" |
8 | #include "qcanmessagedescription_p.h" |
9 | #include "qcansignaldescription.h" |
10 | #include "qcansignaldescription_p.h" |
11 | |
12 | #include <QtCore/QHash> |
13 | #include <QtCore/QMap> |
14 | #include <QtCore/QVariant> |
15 | #include <QtCore/QtEndian> |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | // The initial revision of QCanFrameProcessor introduced the BE data processing |
20 | // logic which is different from what is normally done in CAN protocols. |
21 | // A later patch fixes the logic to be compliant with normal CAN approach |
22 | // (taking DBC as a reference), and introduces this define to disable the |
23 | // unused functions. |
24 | // We could completely remove the "dead code", but for now we want to get some |
25 | // feedback from the users to see if we need to have both approaches or not. |
26 | #define USE_DBC_COMPATIBLE_BE_HANDLING |
27 | |
28 | // Helper method to extract the max bit number of the signal. |
29 | // Note that for BE it's not the last bit of the signal. |
30 | static quint16 (quint16 startBit, quint16 bitLength, QSysInfo::Endian endian) |
31 | { |
32 | #ifdef USE_DBC_COMPATIBLE_BE_HANDLING |
33 | if (endian == QSysInfo::Endian::LittleEndian) { |
34 | return startBit + bitLength - 1; |
35 | } else { |
36 | const auto startByteNum = startBit / 8; |
37 | const auto bitsInStartByte = startBit % 8 + 1; |
38 | const auto leftBits = bitLength - bitsInStartByte; |
39 | if (leftBits <= 0) |
40 | return startBit; // so start bit is the largest |
41 | |
42 | const auto leftBytesRounded = (leftBits % 8 == 0) ? leftBits / 8 : leftBits / 8 + 1; |
43 | return (startByteNum + leftBytesRounded + 1) * 8 - 1; |
44 | } |
45 | #else |
46 | return startBit + bitLength - 1; |
47 | #endif // USE_DBC_COMPATIBLE_BE_HANDLING |
48 | } |
49 | |
50 | /*! |
51 | \class QCanFrameProcessor |
52 | \inmodule QtSerialBus |
53 | \since 6.5 |
54 | \preliminary |
55 | |
56 | \brief The QCanFrameProcessor class can be used to decode |
57 | a \l QCanBusFrame or to convert the input data into a \l QCanBusFrame that |
58 | is ready to be sent to the receiver. |
59 | |
60 | The QCanFrameProcessor class operates on the CAN message descriptions |
61 | (represented by the \l QCanMessageDescription and \l QCanSignalDescription |
62 | classes) and a unique identifier description (represented by |
63 | \l QCanUniqueIdDescription). It uses the descriptions to decode the |
64 | incoming \l QCanBusFrame or to encode the user-specified data into the |
65 | proper payload. |
66 | |
67 | Before doing any decoding or encoding, the QCanFrameProcessor instance |
68 | \e must be initialized properly. The following data needs to be provided: |
69 | |
70 | \list |
71 | \li A \l {QCanUniqueIdDescription::isValid}{valid} unique identifier |
72 | description. Use the \l setUniqueIdDescription() method to provide |
73 | a proper description. |
74 | \li At least one message description. Use the |
75 | \l addMessageDescriptions() or \l setMessageDescriptions() method |
76 | to provide message descriptions. |
77 | All message descriptions \e must have distinct unique identifiers. |
78 | Each message can contain multiple signal descriptions, but signal |
79 | names within one message \e must be unique as well. |
80 | \endlist |
81 | |
82 | The \l parseFrame() method can be used to process the incoming |
83 | \l QCanBusFrame. The method returns a \l {QCanFrameProcessor::}{ParseResult} |
84 | structure which contains the \l {QCanFrameProcessor::ParseResult::uniqueId} |
85 | {unique identifier} and the \l {QCanFrameProcessor::ParseResult::signalValues} |
86 | {signal values} map. The keys of the map are the |
87 | \l {QCanSignalDescription::name}{signal names}, and the values of the map |
88 | are signal values. |
89 | |
90 | The \l prepareFrame() method can be used to generate a \l QCanBusFrame |
91 | object for a specific unique identifier, using the provided signal names |
92 | and desired values. |
93 | |
94 | Errors can occur during the encoding or decoding process. In such cases |
95 | the \l error() and \l errorString() methods can be used to get the |
96 | information about the error. |
97 | |
98 | Some non-critical problems may occur as well. Such problems will be logged, |
99 | but the process will not be stopped. After the process is completed, the |
100 | \l warnings() method can be used to access the list of all the warnings. |
101 | |
102 | \note The last error and error description, as well as the warnings, are |
103 | reset once the decoding or encoding is started. |
104 | |
105 | \sa QCanMessageDescription, QCanSignalDescription |
106 | */ |
107 | |
108 | /*! |
109 | \enum QCanFrameProcessor::Error |
110 | |
111 | This enum represents the possible errors that can occur while |
112 | encoding or decoding the \l QCanBusFrame. |
113 | |
114 | \value None No error occurred. |
115 | \value InvalidFrame The received frame is invalid and cannot be parsed. |
116 | \value UnsupportedFrameFormat The format of the received frame is not |
117 | supported and cannot be parsed. |
118 | \value Decoding An error occurred during decoding. Use |
119 | \l errorString() to get a string representation |
120 | of the error. |
121 | \value Encoding An error occurred during encoding. Use |
122 | \l errorString() to get a string representation |
123 | of the error. |
124 | */ |
125 | |
126 | /*! |
127 | \struct QCanFrameProcessor::ParseResult |
128 | \inmodule QtSerialBus |
129 | \since 6.5 |
130 | |
131 | \brief The struct is used as a return value for the |
132 | \l QCanFrameProcessor::parseFrame() method. |
133 | */ |
134 | |
135 | /*! |
136 | \variable QCanFrameProcessor::ParseResult::uniqueId |
137 | \brief the value of the unique identifier of the parsed frame. |
138 | */ |
139 | |
140 | /*! |
141 | \variable QCanFrameProcessor::ParseResult::signalValues |
142 | \brief the map containing the extracted signals and their values. |
143 | The keys of the map are the \l {QCanSignalDescription::name}{signal names}, |
144 | and the values of the map are signal values. |
145 | */ |
146 | |
147 | /*! |
148 | Creates a CAN frame processor. |
149 | */ |
150 | QCanFrameProcessor::QCanFrameProcessor() |
151 | : d(std::make_unique<QCanFrameProcessorPrivate>()) |
152 | { |
153 | } |
154 | |
155 | /*! |
156 | Destroys this frame processor. |
157 | */ |
158 | QCanFrameProcessor::~QCanFrameProcessor() = default; |
159 | |
160 | /*! |
161 | Constructs a CAN data frame, using \a uniqueId and \a signalValues |
162 | and returns the constructed \l QCanBusFrame. |
163 | |
164 | The \a signalValues parameter \e must contain signal names as keys, and |
165 | expected signal values as values. |
166 | |
167 | The process of creating the frame is as follows: |
168 | |
169 | \list 1 |
170 | \li The \a uniqueId is used to find an appropriate message |
171 | description. |
172 | \li If the message description is found, a \l QCanBusFrame with |
173 | a payload of the specified size is created. All bytes of the |
174 | payload, as well as the frame id, are initialized to zeros. |
175 | \li The \l uniqueIdDescription() is used to encode the \a uniqueId into |
176 | the appropriate part of the frame (frame id or payload). |
177 | \li The selected message description is used to encode all the |
178 | \a signalValues into the frame. |
179 | \li The parts of the frame that are not covered by a unique id or |
180 | existing signal descriptions are untouched (and so still contain |
181 | zeros). |
182 | \endlist |
183 | |
184 | If an error occurred during the encoding, an invalid \l QCanBusFrame is |
185 | returned. In such cases, the \l error() and \l errorString() methods |
186 | can be used to get information about the errors. |
187 | |
188 | \note Calling this method clears all previous errors and warnings. |
189 | |
190 | \sa addMessageDescriptions(), error(), errorString(), warnings() |
191 | */ |
192 | QCanBusFrame QCanFrameProcessor::prepareFrame(QtCanBus::UniqueId uniqueId, |
193 | const QVariantMap &signalValues) |
194 | { |
195 | d->resetErrors(); |
196 | |
197 | if (!d->uidDescription.isValid()) { |
198 | d->setError(err: Error::Encoding, |
199 | desc: QObject::tr(s: "No valid unique identifier description is specified." )); |
200 | return QCanBusFrame(QCanBusFrame::InvalidFrame); |
201 | } |
202 | |
203 | if (!d->messages.contains(key: uniqueId)) { |
204 | d->setError(err: Error::Encoding, |
205 | desc: QObject::tr(s: "Failed to find message description for unique id %1." ). |
206 | arg(a: qToUnderlying(e: uniqueId))); |
207 | return QCanBusFrame(QCanBusFrame::InvalidFrame); |
208 | } |
209 | |
210 | const auto message = d->messages.value(key: uniqueId); |
211 | QCanBusFrame::FrameId canFrameId = 0; // may be modified by the signal values |
212 | QByteArray payload(message.size(), 0x00); |
213 | |
214 | // encode the uniqueId value into the frame on the proper position |
215 | { |
216 | const bool uidInPayload = d->uidDescription.source() == QtCanBus::DataSource::Payload; |
217 | const quint16 bitsSize = uidInPayload ? payload.size() * 8 : 29; |
218 | unsigned char *data = uidInPayload ? reinterpret_cast<unsigned char *>(payload.data()) |
219 | : reinterpret_cast<unsigned char *>(&canFrameId); |
220 | if (!d->fillUniqueId(data, sizeInBits: bitsSize, uniqueId)) { |
221 | d->setError(err: Error::Encoding, |
222 | desc: QObject::tr(s: "Failed to encode unique id %1 into the frame" ). |
223 | arg(a: qToUnderlying(e: uniqueId))); |
224 | return QCanBusFrame(QCanBusFrame::InvalidFrame); |
225 | } |
226 | } |
227 | |
228 | // helper function to check for multiplexor preconditions |
229 | auto checkMuxValues = [](const QCanSignalDescription &desc, |
230 | const QVariantMap &signalValues) -> bool |
231 | { |
232 | const auto muxValues = desc.multiplexSignals(); |
233 | if (muxValues.isEmpty()) |
234 | return true; |
235 | const auto *descPrivate = QCanSignalDescriptionPrivate::get(desc); |
236 | for (auto it = muxValues.cbegin(); it != muxValues.cend(); ++it) { |
237 | const auto &name = it.key(); |
238 | const auto &ranges = it.value(); |
239 | if (!signalValues.contains(key: name) |
240 | || !descPrivate->muxValueInRange(value: signalValues.value(key: name), ranges)) { |
241 | return false; |
242 | } |
243 | } |
244 | return true; |
245 | }; |
246 | |
247 | auto descriptionsHash = QCanMessageDescriptionPrivate::get(desc: message)->messageSignals; |
248 | for (auto it = signalValues.cbegin(); it != signalValues.cend(); ++it) { |
249 | const QString &signalName = it.key(); |
250 | if (!descriptionsHash.contains(key: signalName)) { |
251 | d->addWarning(warning: QObject::tr(s: "Skipping signal %1. It is not found in " |
252 | "message description for unique id %2." ). |
253 | arg(args: signalName, args: QString::number(qToUnderlying(e: uniqueId)))); |
254 | continue; |
255 | } |
256 | |
257 | const auto &signalDesc = descriptionsHash.value(key: signalName); |
258 | if (!signalDesc.isValid()) { |
259 | d->addWarning(warning: QObject::tr(s: "Skipping signal %1. Its description is invalid." ). |
260 | arg(a: signalName)); |
261 | continue; |
262 | } |
263 | |
264 | // check for multiplexor prerequisites |
265 | if (!checkMuxValues(signalDesc, signalValues)) { |
266 | d->addWarning(warning: QObject::tr(s: "Skipping signal %1. Proper multiplexor values not found." ). |
267 | arg(a: signalName)); |
268 | continue; |
269 | } |
270 | |
271 | const bool dataInPayload = signalDesc.dataSource() == QtCanBus::DataSource::Payload; |
272 | // For data in FrameId we consider max length == 29, because we do not |
273 | // know if the frame is extended or not. |
274 | const quint16 maxDataLength = dataInPayload ? payload.size() * 8 : 29; |
275 | const auto signalDataEnd = extractMaxBitNum(startBit: signalDesc.startBit(), bitLength: signalDesc.bitLength(), |
276 | endian: signalDesc.dataEndian()); |
277 | if (signalDataEnd >= maxDataLength) { |
278 | d->addWarning(warning: QObject::tr(s: "Skipping signal %1. Its length exceeds the expected " |
279 | "message length." ).arg(a: signalName)); |
280 | continue; |
281 | } |
282 | |
283 | unsigned char *data = dataInPayload ? reinterpret_cast<unsigned char *>(payload.data()) |
284 | : reinterpret_cast<unsigned char *>(&canFrameId); |
285 | d->encodeSignal(data, value: it.value(), signalDesc); |
286 | } |
287 | |
288 | return QCanBusFrame(canFrameId, payload); |
289 | } |
290 | |
291 | /*! |
292 | Returns the last error. |
293 | |
294 | \sa errorString(), prepareFrame(), parseFrame() |
295 | */ |
296 | QCanFrameProcessor::Error QCanFrameProcessor::error() const |
297 | { |
298 | return d->error; |
299 | } |
300 | |
301 | /*! |
302 | Returns the text description of the last error. |
303 | |
304 | \sa error(), prepareFrame(), parseFrame() |
305 | */ |
306 | QString QCanFrameProcessor::errorString() const |
307 | { |
308 | return d->errorString; |
309 | } |
310 | |
311 | /*! |
312 | Returns the list of warnings generated during the last encoding or decoding |
313 | call. |
314 | |
315 | \sa error(), errorString(), prepareFrame(), parseFrame() |
316 | */ |
317 | QStringList QCanFrameProcessor::warnings() const |
318 | { |
319 | return d->warnings; |
320 | } |
321 | |
322 | /*! |
323 | Returns all the message descriptions that are currently used by this frame |
324 | processor. |
325 | |
326 | \sa addMessageDescriptions(), setMessageDescriptions(), |
327 | clearMessageDescriptions() |
328 | */ |
329 | QList<QCanMessageDescription> QCanFrameProcessor::messageDescriptions() const |
330 | { |
331 | return QList<QCanMessageDescription>(d->messages.cbegin(), d->messages.cend()); |
332 | } |
333 | |
334 | /*! |
335 | Adds new message descriptions \a descriptions to the available message |
336 | descriptions. |
337 | |
338 | All message descriptions should have distinct unique ids. |
339 | |
340 | If some message descriptions have repeated unique ids, only the last |
341 | description will be used. |
342 | |
343 | If the parser already had a message description with the same unique id, it |
344 | will be overwritten. |
345 | |
346 | \sa messageDescriptions(), setMessageDescriptions(), |
347 | clearMessageDescriptions() |
348 | */ |
349 | void QCanFrameProcessor::addMessageDescriptions(const QList<QCanMessageDescription> &descriptions) |
350 | { |
351 | for (const auto &desc : descriptions) |
352 | d->messages.insert(key: desc.uniqueId(), value: desc); |
353 | } |
354 | |
355 | /*! |
356 | Replaces current message descriptions used by this frame processor with the |
357 | new message descriptions \a descriptions. |
358 | |
359 | \sa messageDescriptions(), addMessageDescriptions(), |
360 | clearMessageDescriptions() |
361 | */ |
362 | void QCanFrameProcessor::setMessageDescriptions(const QList<QCanMessageDescription> &descriptions) |
363 | { |
364 | d->messages.clear(); |
365 | addMessageDescriptions(descriptions); |
366 | } |
367 | |
368 | /*! |
369 | Removes all message descriptions for this frame processor. |
370 | |
371 | \sa messageDescriptions(), addMessageDescriptions(), |
372 | setMessageDescriptions() |
373 | */ |
374 | void QCanFrameProcessor::clearMessageDescriptions() |
375 | { |
376 | d->messages.clear(); |
377 | } |
378 | |
379 | /*! |
380 | Returns the unique identifier description. |
381 | |
382 | The unique identifier description must be valid in order to encode or decode |
383 | the CAN bus frames. See the \l QCanUniqueIdDescription class documentation |
384 | for more details. |
385 | |
386 | \sa setUniqueIdDescription(), QCanUniqueIdDescription |
387 | */ |
388 | QCanUniqueIdDescription QCanFrameProcessor::uniqueIdDescription() const |
389 | { |
390 | return d->uidDescription; |
391 | } |
392 | |
393 | /*! |
394 | Sets the unique identifier description to \a description. |
395 | |
396 | The unique identifier description must be valid in order to encode or decode |
397 | the CAN bus frames. See the \l QCanUniqueIdDescription class documentation |
398 | for more details. |
399 | |
400 | \sa uniqueIdDescription(), QCanUniqueIdDescription |
401 | */ |
402 | void QCanFrameProcessor::setUniqueIdDescription(const QCanUniqueIdDescription &description) |
403 | { |
404 | d->uidDescription = description; |
405 | } |
406 | |
407 | /*! |
408 | Parses the frame \a frame using the specified message descriptions. |
409 | |
410 | The process of parsing is as follows: |
411 | \list 1 |
412 | \li The \l uniqueIdDescription() is used to extract the unique |
413 | identifier of the message. |
414 | \li The extracted unique identifier is used to search for a suitable |
415 | \l QCanMessageDescription from the list of all available |
416 | \l messageDescriptions(). |
417 | \li The matching \l QCanMessageDescription is used to extract |
418 | the signal values from the frame. |
419 | \endlist |
420 | |
421 | This method returns a \l QCanFrameProcessor::ParseResult, which contains |
422 | both the extracted unique identifier and a \l QVariantMap with the signals |
423 | and their values. The keys of the map are the |
424 | \l {QCanSignalDescription::name}{signal names}, and the values of the map |
425 | are signal values. |
426 | |
427 | If an error occurred during the decoding, a result with empty |
428 | \l {QCanFrameProcessor::ParseResult::}{signalValues} is returned. |
429 | In such cases, the \l error() and \l errorString() methods can be used |
430 | to get information about the errors. |
431 | |
432 | \note Calling this method clears all previous errors and warnings. |
433 | |
434 | \sa addMessageDescriptions(), error(), errorString(), warnings() |
435 | */ |
436 | QCanFrameProcessor::ParseResult QCanFrameProcessor::parseFrame(const QCanBusFrame &frame) |
437 | { |
438 | d->resetErrors(); |
439 | |
440 | if (!frame.isValid()) { |
441 | d->setError(err: Error::InvalidFrame, desc: QObject::tr(s: "Invalid frame." )); |
442 | return {}; |
443 | } |
444 | if (frame.frameType() != QCanBusFrame::DataFrame) { |
445 | d->setError(err: Error::UnsupportedFrameFormat, desc: QObject::tr(s: "Unsupported frame format." )); |
446 | return {}; |
447 | } |
448 | if (!d->uidDescription.isValid()) { |
449 | d->setError(err: Error::Decoding, |
450 | desc: QObject::tr(s: "No valid unique identifier description is specified." )); |
451 | return {}; |
452 | } |
453 | |
454 | const auto uidOpt = d->extractUniqueId(frame); |
455 | if (!uidOpt.has_value()) { |
456 | d->setError(err: Error::Decoding, |
457 | desc: QObject::tr(s: "Failed to extract unique id from the frame." )); |
458 | return {}; |
459 | } |
460 | |
461 | const auto uniqueId = uidOpt.value(); |
462 | if (!d->messages.contains(key: uniqueId)) { |
463 | d->setError(err: Error::Decoding, |
464 | desc: QObject::tr(s: "Could not find a message description for unique id %1." ). |
465 | arg(a: qToUnderlying(e: uniqueId))); |
466 | return {}; |
467 | } |
468 | |
469 | const auto message = d->messages.value(key: uniqueId); |
470 | if (message.size() != frame.payload().size()) { |
471 | d->setError(err: Error::Decoding, |
472 | desc: QObject::tr(s: "Payload size does not match message description. " |
473 | "Actual size = %1, expected size = %2." ). |
474 | arg(a: frame.payload().size()).arg(a: message.size())); |
475 | return {}; |
476 | } |
477 | |
478 | QVariantMap parsedSignals; |
479 | // The multiplexor signals can form a complex dependency, so we can't |
480 | // simply iterate through the signal descriptions in a natural order. |
481 | // Instead, we first need to process all signals with no dependency on |
482 | // other multiplexors, then handle the signals that have dependency on |
483 | // already parsed signals, and so on, until we parse all signals. |
484 | // One potential problem here is that the dependencies can be specified |
485 | // incorrectly (for example, we can have circular dependencies, or |
486 | // dependencies on non-existent signal), so we need to come up with a |
487 | // reasonable condition to stop. |
488 | |
489 | auto seenNeededSignals = [](const QCanSignalDescription &desc, |
490 | const QVariantMap &parsedSignals) -> bool { |
491 | const auto muxSignals = desc.multiplexSignals(); |
492 | if (muxSignals.isEmpty()) |
493 | return true; |
494 | const auto *descPrivate = QCanSignalDescriptionPrivate::get(desc); |
495 | for (auto it = muxSignals.cbegin(); it != muxSignals.cend(); ++it) { |
496 | const auto &name = it.key(); |
497 | const auto &ranges = it.value(); |
498 | if (!parsedSignals.contains(key: name) |
499 | || !descPrivate->muxValueInRange(value: parsedSignals.value(key: name), ranges)) { |
500 | return false; |
501 | } |
502 | } |
503 | return true; |
504 | }; |
505 | |
506 | auto descriptionsHash = QCanMessageDescriptionPrivate::get(desc: message)->messageSignals; |
507 | while (true) { |
508 | QList<QString> newNames; |
509 | for (const auto &desc : std::as_const(t&: descriptionsHash)) { |
510 | if (seenNeededSignals(desc, parsedSignals)) { |
511 | newNames.push_back(t: desc.name()); |
512 | if (!desc.isValid()) { |
513 | d->addWarning(warning: QObject::tr(s: "Skipping signal %1 in message with unique id %2" |
514 | " because its description is invalid." ). |
515 | arg(args: desc.name(), args: QString::number(qToUnderlying(e: uniqueId)))); |
516 | continue; |
517 | } |
518 | const QVariant value = d->decodeSignal(frame, signalDesc: desc); |
519 | if (value.isValid()) |
520 | parsedSignals.insert(key: desc.name(), value); |
521 | } |
522 | } |
523 | for (const auto &name : std::as_const(t&: newNames)) |
524 | descriptionsHash.remove(key: name); |
525 | if (newNames.isEmpty() || descriptionsHash.isEmpty()) { |
526 | // We either processed all signals, or failed to process more during |
527 | // the last loop. The latter means that the multiplexor conditions |
528 | // do not match for the rest of the signals, which is fine and will |
529 | // always happen when multiplexing |
530 | break; |
531 | } |
532 | } |
533 | |
534 | return {.uniqueId: uniqueId, .signalValues: parsedSignals}; |
535 | } |
536 | |
537 | /* QCanFrameProcessorPrivate implementation */ |
538 | |
539 | void QCanFrameProcessorPrivate::resetErrors() |
540 | { |
541 | error = QCanFrameProcessor::Error::None; |
542 | errorString.clear(); |
543 | warnings.clear(); |
544 | } |
545 | |
546 | void QCanFrameProcessorPrivate::setError(QCanFrameProcessor::Error err, const QString &desc) |
547 | { |
548 | error = err; |
549 | errorString = desc; |
550 | } |
551 | |
552 | void QCanFrameProcessorPrivate::addWarning(const QString &warning) |
553 | { |
554 | warnings.push_back(t: warning); |
555 | } |
556 | |
557 | QVariant QCanFrameProcessorPrivate::decodeSignal(const QCanBusFrame &frame, |
558 | const QCanSignalDescription &signalDesc) |
559 | { |
560 | const auto signalDataEnd = extractMaxBitNum(startBit: signalDesc.startBit(), bitLength: signalDesc.bitLength(), |
561 | endian: signalDesc.dataEndian()); |
562 | const bool dataFromPayload = |
563 | signalDesc.dataSource() == QtCanBus::DataSource::Payload; |
564 | |
565 | const auto frameIdLength = frame.hasExtendedFrameFormat() ? 29 : 11; |
566 | const auto maxDataLength = dataFromPayload ? frame.payload().size() * 8 |
567 | : frameIdLength; |
568 | |
569 | if (signalDataEnd >= maxDataLength) { |
570 | addWarning(warning: QObject::tr(s: "Skipping signal %1 in message with unique id %2. " |
571 | "Its expected length exceeds the data length." ). |
572 | arg(args: signalDesc.name(), args: QString::number(frame.frameId()))); |
573 | return QVariant(); |
574 | } |
575 | |
576 | const QByteArray payload = frame.payload(); |
577 | const auto frameId = frame.frameId(); |
578 | const unsigned char *data = dataFromPayload |
579 | ? reinterpret_cast<const unsigned char *>(payload.data()) |
580 | : reinterpret_cast<const unsigned char *>(&frameId); |
581 | |
582 | return parseData(data, signalDesc); |
583 | } |
584 | |
585 | static bool needValueConversion(const QCanSignalDescription &signalDesc) |
586 | { |
587 | return !qIsNaN(d: signalDesc.factor()) || !qIsNaN(d: signalDesc.offset()) |
588 | || !qIsNaN(d: signalDesc.scaling()); |
589 | } |
590 | |
591 | template <typename T> |
592 | static double convertFromCanValue(T value, const QCanSignalDescription &signalDesc) |
593 | { |
594 | double result = static_cast<double>(value); |
595 | if (!qIsNaN(d: signalDesc.factor())) |
596 | result *= signalDesc.factor(); |
597 | |
598 | if (!qIsNaN(d: signalDesc.offset())) |
599 | result += signalDesc.offset(); |
600 | |
601 | if (!qIsNaN(d: signalDesc.scaling())) |
602 | result *= signalDesc.scaling(); |
603 | |
604 | return result; |
605 | } |
606 | |
607 | static double convertToCanValue(const QVariant &value, const QCanSignalDescription &signalDesc) |
608 | { |
609 | // Checks for 0 are done in the corresponding setters, so we can divide |
610 | // safely. |
611 | double result = value.toDouble(); |
612 | if (!qIsNaN(d: signalDesc.scaling())) |
613 | result /= signalDesc.scaling(); |
614 | |
615 | if (!qIsNaN(d: signalDesc.offset())) |
616 | result -= signalDesc.offset(); |
617 | |
618 | if (!qIsNaN(d: signalDesc.factor())) |
619 | result /= signalDesc.factor(); |
620 | |
621 | return result; |
622 | } |
623 | |
624 | #ifdef USE_DBC_COMPATIBLE_BE_HANDLING |
625 | |
626 | template <typename T> |
627 | static QVariant (const unsigned char *data, const QCanSignalDescription &signalDesc) |
628 | { |
629 | constexpr auto tBitLength = sizeof(T) * 8; |
630 | const auto length = signalDesc.bitLength(); |
631 | if constexpr (std::is_floating_point_v<T>) |
632 | Q_ASSERT(tBitLength == length); |
633 | else |
634 | Q_ASSERT(tBitLength >= length); |
635 | const auto maxBytesToRead = (length % 8 == 0) ? length / 8 : length / 8 + 1; |
636 | const auto start = signalDesc.startBit(); |
637 | T value = {}; |
638 | const bool isBigEndian = signalDesc.dataEndian() == QSysInfo::Endian::BigEndian; |
639 | if (isBigEndian) { |
640 | // Big Endian - start bit is MSB |
641 | if (start % 8 == 7 && length % 8 == 0) { |
642 | // The data is aligned at byte offset, we can simply memcpy |
643 | memcpy(&value, &data[(start - 7) / 8], maxBytesToRead); |
644 | } else { |
645 | // Data is not aligned at byte offset, we need to do some bit |
646 | // shifting. We cannot perform bit operations on float or double |
647 | // types, so we convert the value to uchar *. |
648 | // Because of how BE data is organized, the indices for reading |
649 | // would not be continuous. If we want to extract BE data from the |
650 | // middle 12 bits of a 2-byte payload, we will need to read bits 5-0 |
651 | // and 15-10: |
652 | // _________________________________________________________________ |
653 | // |7 |6 |5(MSB) |4 |3 |2 |1 |0 | |
654 | // ----------------------------------------------------------------- |
655 | // |15 |14 |13 |12 |11 |10(LSB)|9 |8 | |
656 | // ----------------------------------------------------------------- |
657 | unsigned char *valueData = reinterpret_cast<unsigned char *>(&value); |
658 | qsizetype bitIdx = start; |
659 | for (qsizetype processedBits = 0; processedBits < length; ++processedBits) { |
660 | const auto dataByteIdx = bitIdx / 8; |
661 | const auto dataBitIdx = bitIdx % 8; |
662 | if (data[dataByteIdx] & (0x01 << dataBitIdx)) { |
663 | const auto byteIdx = processedBits / 8; |
664 | // start filling each byte from MSB |
665 | const auto bitIdx = 7 - (processedBits % 8); |
666 | valueData[byteIdx] |= (0x01 << bitIdx); |
667 | } |
668 | // handle jump like 0 -> 15 from the example above |
669 | if (bitIdx % 8 == 0) |
670 | bitIdx += 15; |
671 | else |
672 | --bitIdx; |
673 | } |
674 | } |
675 | } else { |
676 | // Little Endian - start bit is LSB |
677 | if (start % 8 == 0 && length % 8 == 0) { |
678 | // The data is aligned at byte offset, we can simply memcpy |
679 | memcpy(&value, &data[start / 8], maxBytesToRead); |
680 | } else { |
681 | // Data is not aligned at byte offset, we need to do some bit |
682 | // shifting. We cannot perform bit operations on float or double |
683 | // types, so we convert the value to uchar *. |
684 | unsigned char *valueData = reinterpret_cast<unsigned char *>(&value); |
685 | quint16 valueIdx = 0; |
686 | for (auto i = start; i < start + length; ++i, ++valueIdx) { |
687 | const auto byteIdx = i / 8; |
688 | const auto bitIdx = i % 8; |
689 | if (data[byteIdx] & (0x01 << bitIdx)) |
690 | valueData[valueIdx / 8] |= 0x01 << (valueIdx % 8); |
691 | } |
692 | } |
693 | } |
694 | // check and convert endian |
695 | T convertedValue = {}; |
696 | if (isBigEndian) |
697 | convertedValue = qFromBigEndian(value); |
698 | else |
699 | convertedValue = qFromLittleEndian(value); |
700 | const bool endianChanged = convertedValue != value; |
701 | value = convertedValue; |
702 | // for signed & unsigned fill the most significant bits with proper values |
703 | if constexpr (std::is_integral_v<T>) { |
704 | if (tBitLength > length) { |
705 | if (endianChanged) { |
706 | // After endian conversion we have unneeded bits in the end, |
707 | // so we need to cut them |
708 | value = value >> (tBitLength - length); |
709 | } |
710 | // value has more bits than we could actually read, so we need to |
711 | // fill the most significant bits properly |
712 | const auto dataFormat = signalDesc.dataFormat(); |
713 | if (dataFormat == QtCanBus::DataFormat::SignedInteger) { |
714 | if (value & (0x01ULL << (length - 1))) { |
715 | // msb = 1 -> negative value, fill the rest with 1's |
716 | for (auto i = length; i < tBitLength; ++i) |
717 | value |= (0x01ULL << i); |
718 | } else { |
719 | // msb = 0 -> positive value, fill the rest with 0's |
720 | for (auto i = length; i < tBitLength; ++i) |
721 | value &= ~(0x01ULL << i); |
722 | } |
723 | } else if (dataFormat == QtCanBus::DataFormat::UnsignedInteger) { |
724 | // simply fill most significant bits with 0's |
725 | for (auto i = length; i < tBitLength; ++i) |
726 | value &= ~(0x01ULL << i); |
727 | } |
728 | } |
729 | } |
730 | // perform value conversions, if needed |
731 | if (needValueConversion(signalDesc)) |
732 | return QVariant::fromValue(convertFromCanValue(value, signalDesc)); |
733 | |
734 | return QVariant::fromValue(value); |
735 | } |
736 | |
737 | #else |
738 | |
739 | template <typename T> |
740 | static QVariant extractValue(const unsigned char *data, const QCanSignalDescription &signalDesc) |
741 | { |
742 | constexpr auto tBitLength = sizeof(T) * 8; |
743 | const auto length = signalDesc.bitLength(); |
744 | if constexpr (std::is_floating_point_v<T>) |
745 | Q_ASSERT(tBitLength == length); |
746 | else |
747 | Q_ASSERT(tBitLength >= length); |
748 | const auto maxBytesToRead = (length % 8 == 0) ? length / 8 : length / 8 + 1; |
749 | const auto start = signalDesc.startBit(); |
750 | T value = {}; |
751 | if (start % 8 == 0 && length % 8 == 0) { |
752 | // The data is aligned at byte offset, we can simply memcpy |
753 | memcpy(&value, &data[start / 8], maxBytesToRead); |
754 | } else { |
755 | // Data is not aligned at byte offset, we need to do some bit shifting |
756 | // We cannot perform bit operations on float or double types, so we |
757 | // convert the value to uchar *. |
758 | |
759 | // If the data is in big endian, and data length % 8 != 0, then the |
760 | // first byte is not full. So we need to read (8 - length % 8) bits |
761 | // from it, and then complete it properly |
762 | |
763 | unsigned char *valueData = reinterpret_cast<unsigned char *>(&value); |
764 | quint16 valueIdx = 0; |
765 | quint16 startIdx = start; |
766 | quint16 numToRead = length; |
767 | if (signalDesc.dataEndian() == QSysInfo::Endian::BigEndian) { |
768 | const auto readInFirstByte = length % 8; |
769 | // else we have round number of bytes and all these tricks are not needed |
770 | if (readInFirstByte) { |
771 | const auto missingBits = 8 - readInFirstByte; |
772 | bool lastBitIsOne = false; |
773 | for (auto i = startIdx; i < startIdx + readInFirstByte; ++i, ++valueIdx) { |
774 | const auto byteIdx = i / 8; |
775 | const auto bitIdx = i % 8; |
776 | lastBitIsOne = data[byteIdx] & (0x01 << bitIdx); |
777 | if (lastBitIsOne) |
778 | valueData[valueIdx / 8] |= 0x01 << (valueIdx % 8); |
779 | } |
780 | if (lastBitIsOne) { |
781 | for (auto i = 0; i < missingBits; ++i, ++valueIdx) |
782 | valueData[valueIdx / 8] |= 0x01 << (valueIdx % 8); |
783 | } else { |
784 | // We simply have zeros there, but still need to increase valueIdx |
785 | valueIdx += missingBits; |
786 | } |
787 | startIdx += readInFirstByte; |
788 | numToRead -= readInFirstByte; |
789 | } |
790 | } |
791 | for (auto i = startIdx; i < startIdx + numToRead; ++i, ++valueIdx) { |
792 | const auto byteIdx = i / 8; |
793 | const auto bitIdx = i % 8; |
794 | if (data[byteIdx] & (0x01 << bitIdx)) |
795 | valueData[valueIdx / 8] |= 0x01 << (valueIdx % 8); |
796 | } |
797 | } |
798 | // check and convert endian |
799 | T convertedValue = {}; |
800 | if (signalDesc.dataEndian() == QSysInfo::Endian::LittleEndian) |
801 | convertedValue = qFromLittleEndian(value); |
802 | else |
803 | convertedValue = qFromBigEndian(value); |
804 | const bool endianChanged = convertedValue != value; |
805 | value = convertedValue; |
806 | // for signed & unsigned fill the most significant bits with proper values |
807 | if constexpr (std::is_integral_v<T>) { |
808 | if (tBitLength > length) { |
809 | if (endianChanged) { |
810 | // After endian conversion we have unneeded bits in the end, |
811 | // so we need to cut them |
812 | value = value >> (tBitLength - maxBytesToRead * 8); |
813 | } |
814 | // value has more bits than we could actually read, so we need to |
815 | // fill the most significant bits properly |
816 | const auto dataFormat = signalDesc.dataFormat(); |
817 | if (dataFormat == QtCanBus::DataFormat::SignedInteger) { |
818 | if (value & (0x01ULL << (length - 1))) { |
819 | // msb = 1 -> negative value, fill the rest with 1's |
820 | for (auto i = length; i < tBitLength; ++i) |
821 | value |= (0x01ULL << i); |
822 | } else { |
823 | // msb = 0 -> positive value, fill the rest with 0's |
824 | for (auto i = length; i < tBitLength; ++i) |
825 | value &= ~(0x01ULL << i); |
826 | } |
827 | } else if (dataFormat == QtCanBus::DataFormat::UnsignedInteger) { |
828 | // simply fill most significant bits with 0's |
829 | for (auto i = length; i < tBitLength; ++i) |
830 | value &= ~(0x01ULL << i); |
831 | } |
832 | } |
833 | } |
834 | // perform value conversions, if needed |
835 | if (needValueConversion(signalDesc)) |
836 | return QVariant::fromValue(convertFromCanValue(value, signalDesc)); |
837 | |
838 | return QVariant::fromValue(value); |
839 | } |
840 | |
841 | #endif // USE_DBC_COMPATIBLE_BE_HANDLING |
842 | |
843 | static QVariant parseAscii(const unsigned char *data, const QCanSignalDescription &signalDesc) |
844 | { |
845 | Q_ASSERT(signalDesc.bitLength() % 8 == 0); |
846 | |
847 | const auto length = signalDesc.bitLength(); |
848 | const auto start = signalDesc.startBit(); |
849 | |
850 | QByteArray value(length / 8, 0x00); |
851 | |
852 | char *valueData = value.data(); |
853 | quint16 valueIdx = 0; |
854 | for (quint16 i = start; i < start + length; ++i, ++valueIdx) { |
855 | const auto byteIdx = i / 8; |
856 | const auto bitIdx = i % 8; |
857 | if (data[byteIdx] & (0x01 << bitIdx)) |
858 | valueData[valueIdx / 8] |= 0x01 << (valueIdx % 8); |
859 | } |
860 | |
861 | return QVariant(value); |
862 | } |
863 | |
864 | QVariant QCanFrameProcessorPrivate::parseData(const unsigned char *data, |
865 | const QCanSignalDescription &signalDesc) |
866 | { |
867 | // We assume that signal's length does not exceed data size. |
868 | // That is checked as a precondition to calling this method, so we do not |
869 | // pass size for the data. |
870 | switch (signalDesc.dataFormat()) { |
871 | case QtCanBus::DataFormat::SignedInteger: |
872 | return extractValue<qint64>(data, signalDesc); |
873 | case QtCanBus::DataFormat::UnsignedInteger: |
874 | return extractValue<quint64>(data, signalDesc); |
875 | case QtCanBus::DataFormat::Float: |
876 | return extractValue<float>(data, signalDesc); |
877 | case QtCanBus::DataFormat::Double: |
878 | return extractValue<double>(data, signalDesc); |
879 | case QtCanBus::DataFormat::AsciiString: |
880 | return parseAscii(data, signalDesc); |
881 | } |
882 | Q_UNREACHABLE(); |
883 | } |
884 | |
885 | #ifdef USE_DBC_COMPATIBLE_BE_HANDLING |
886 | |
887 | template <typename T> |
888 | static void encodeValue(unsigned char *data, const QVariant &valueVar, |
889 | const QCanSignalDescription &signalDesc) |
890 | { |
891 | constexpr auto tBitLength = sizeof(T) * 8; |
892 | const auto length = signalDesc.bitLength(); |
893 | if constexpr (std::is_floating_point_v<T>) |
894 | Q_ASSERT(tBitLength == length); |
895 | else |
896 | Q_ASSERT(tBitLength >= length); |
897 | |
898 | // Perform value conversion. |
899 | T value = {}; |
900 | if (needValueConversion(signalDesc)) |
901 | value = static_cast<T>(std::round(x: convertToCanValue(value: valueVar, signalDesc))); |
902 | else |
903 | value = valueVar.value<T>(); |
904 | |
905 | const bool dataLittleEndian = signalDesc.dataEndian() == QSysInfo::Endian::LittleEndian; |
906 | |
907 | const auto maxBytesToWrite = (length % 8 == 0) ? length / 8 : length / 8 + 1; |
908 | |
909 | // always treat the value-to-write as LE for simplicity |
910 | value = qToLittleEndian(value); |
911 | const quint16 start = signalDesc.startBit(); |
912 | if (dataLittleEndian) { |
913 | // Little Endian |
914 | if (start % 8 == 0 && length % 8 == 0) { |
915 | // The data is aligned at byte offset, and has a round number of |
916 | // bytes, so we can simply memcpy |
917 | memcpy(&data[start / 8], &value, maxBytesToWrite); |
918 | } else { |
919 | const uchar *valueData = reinterpret_cast<const uchar *>(&value); |
920 | for (quint16 i = 0; i < length; ++i) { |
921 | const auto valueByteIdx = i / 8; |
922 | const auto valueBitIdx = i % 8; |
923 | const auto dataByteIdx = (start + i) / 8; |
924 | const auto dataBitIdx = (start + i) % 8; |
925 | |
926 | if (valueData[valueByteIdx] & (0x01 << valueBitIdx)) |
927 | data[dataByteIdx] |= (0x01 << dataBitIdx); |
928 | else |
929 | data[dataByteIdx] &= ~(0x01 << dataBitIdx); |
930 | } |
931 | } |
932 | } else { |
933 | // Big Endian |
934 | if (start % 8 == 7 && length % 8 == 0) { |
935 | // The data is aligned at byte offset and has a round number of |
936 | // bytes, so we can simply memcpy. Just need to convert to BE and |
937 | // take the meaningful bytes (those will be the most significant |
938 | // bytes after switching to BE). |
939 | value = qToBigEndian(value); |
940 | const uchar *valueData = reinterpret_cast<const uchar *>(&value); |
941 | const auto byteIdx = sizeof(value) - maxBytesToWrite; |
942 | memcpy(dest: &data[(start - 7) / 8], src: &valueData[byteIdx], n: maxBytesToWrite); |
943 | } else { |
944 | // We need to start from the MSB of the valueToWrite |
945 | // Because of how BE data is organized, the indices for writing |
946 | // would not be continuous. If we want to write BE data to the |
947 | // middle 12 bits of a 2-byte payload, we will need to write bits |
948 | // 5-0 and 15-10: |
949 | // _________________________________________________________________ |
950 | // |7 |6 |5(MSB) |4 |3 |2 |1 |0 | |
951 | // ----------------------------------------------------------------- |
952 | // |15 |14 |13 |12 |11 |10(LSB)|9 |8 | |
953 | // ----------------------------------------------------------------- |
954 | const uchar *valueData = reinterpret_cast<const uchar *>(&value); |
955 | auto dataBit = signalDesc.startBit(); |
956 | for (auto valueBit = length - 1; valueBit >= 0; --valueBit) { |
957 | const auto valueByteIdx = valueBit / 8; |
958 | const auto valueBitIdx = valueBit % 8; |
959 | const auto dataByteIdx = dataBit / 8; |
960 | const auto dataBitIdx = dataBit % 8; |
961 | if (valueData[valueByteIdx] & (0x01 << valueBitIdx)) |
962 | data[dataByteIdx] |= (0x01 << dataBitIdx); |
963 | else |
964 | data[dataByteIdx] &= ~(0x01 << dataBitIdx); |
965 | // handle jumps like 0 -> 15 |
966 | if (dataBit % 8 == 0) |
967 | dataBit += 15; |
968 | else |
969 | --dataBit; |
970 | } |
971 | } |
972 | } |
973 | } |
974 | |
975 | #else |
976 | |
977 | static constexpr bool isNativeLittleEndian() |
978 | { |
979 | #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN |
980 | return true; |
981 | #else |
982 | return false; |
983 | #endif |
984 | } |
985 | |
986 | template <typename T> |
987 | static void encodeValue(unsigned char *data, const QVariant &valueVar, |
988 | const QCanSignalDescription &signalDesc) |
989 | { |
990 | constexpr auto tBitLength = sizeof(T) * 8; |
991 | const auto length = signalDesc.bitLength(); |
992 | if constexpr (std::is_floating_point_v<T>) |
993 | Q_ASSERT(tBitLength == length); |
994 | else |
995 | Q_ASSERT(tBitLength >= length); |
996 | const auto maxBytesToWrite = (length % 8 == 0) ? length / 8 : length / 8 + 1; |
997 | |
998 | // Perform value conversion. |
999 | T value = {}; |
1000 | if (needValueConversion(signalDesc)) |
1001 | value = static_cast<T>(std::round(convertToCanValue(valueVar, signalDesc))); |
1002 | else |
1003 | value = valueVar.value<T>(); |
1004 | |
1005 | // Check endian. |
1006 | // When doing endian-conversion for values with (bitSize % 8 != 0) we must |
1007 | // be very careful, because qTo{Little,Big}Endian swaps whole bytes. |
1008 | // After swapping the last byte, which could have less than 8 meaningful |
1009 | // bits, becomes the first byte. So we need to adjust it carefully, shifting |
1010 | // it in such a way that all meaningless bits are skipped. |
1011 | // We also need to consider that we operate on q{u}int64 values for |
1012 | // {un}signed integers, so we need to chop the unneeded bytes first. |
1013 | const bool dataLittleEndian = |
1014 | signalDesc.dataEndian() == QSysInfo::Endian::LittleEndian; |
1015 | |
1016 | T valueToWrite = value; |
1017 | quint16 writeOffset = 0; |
1018 | if (dataLittleEndian && !isNativeLittleEndian()) { |
1019 | valueToWrite = qToLittleEndian(valueToWrite); |
1020 | } else if (!dataLittleEndian && isNativeLittleEndian()) { |
1021 | valueToWrite = qToBigEndian(valueToWrite); |
1022 | // for floating point types we always pass the exact type, so no need |
1023 | // to shift extra/unneeded bits |
1024 | if constexpr (!std::is_floating_point_v<T>) { |
1025 | // get rid of the unneeded bytes |
1026 | valueToWrite = valueToWrite >> (tBitLength - maxBytesToWrite * 8); |
1027 | // skip meaningless bits in the first byte |
1028 | writeOffset = maxBytesToWrite * 8 - length; |
1029 | if (writeOffset > 0) { |
1030 | uchar *valueData = reinterpret_cast<uchar *>(&valueToWrite); |
1031 | valueData[0] = valueData[0] << writeOffset; |
1032 | } |
1033 | } |
1034 | } |
1035 | |
1036 | const quint16 start = signalDesc.startBit(); |
1037 | if (start % 8 == 0 && length % 8 == 0) { |
1038 | // The data is aligned at byte offset, and has a round number of bytes, |
1039 | // so we can simply memcpy |
1040 | memcpy(&data[start / 8], &valueToWrite, maxBytesToWrite); |
1041 | } else { |
1042 | const uchar *valueData = reinterpret_cast<const uchar *>(&valueToWrite); |
1043 | for (quint16 i = 0; i < length; ++i) { |
1044 | const auto valueByteIdx = (i + writeOffset) / 8; |
1045 | const auto valueBitIdx = (i + writeOffset) % 8; |
1046 | const auto dataByteIdx = (start + i) / 8; |
1047 | const auto dataBitIdx = (start + i) % 8; |
1048 | |
1049 | if (valueData[valueByteIdx] & (0x01 << valueBitIdx)) |
1050 | data[dataByteIdx] |= 0x01 << dataBitIdx; |
1051 | else |
1052 | data[dataByteIdx] &= ~(0x01 << dataBitIdx); |
1053 | } |
1054 | } |
1055 | } |
1056 | |
1057 | #endif // USE_DBC_COMPATIBLE_BE_HANDLING |
1058 | |
1059 | static void encodeAscii(unsigned char *data, const QVariant &value, |
1060 | const QCanSignalDescription &signalDesc) |
1061 | { |
1062 | Q_ASSERT(signalDesc.bitLength() % 8 == 0); |
1063 | |
1064 | const QByteArray ascii = value.toByteArray(); |
1065 | // The ascii array can have more or less bytes. Handle it. |
1066 | const auto length = std::min(a: ascii.size() * 8, b: static_cast<qsizetype>(signalDesc.bitLength())); |
1067 | |
1068 | const auto start = signalDesc.startBit(); |
1069 | for (auto i = 0; i < length; ++i) { |
1070 | const auto dataByteIdx = (start + i) / 8; |
1071 | const auto dataBitIdx = (start + i) % 8; |
1072 | if (ascii.data()[i / 8] & (0x01 << (i % 8))) |
1073 | data[dataByteIdx] |= 0x01 << dataBitIdx; |
1074 | else |
1075 | data[dataByteIdx] &= ~(0x01 << dataBitIdx); |
1076 | } |
1077 | if (length < signalDesc.bitLength()) { |
1078 | // fill the rest of the bits with 0's |
1079 | for (auto i = length; i < signalDesc.bitLength(); ++i) |
1080 | data[i / 8] &= ~(0x01 << (i % 8)); |
1081 | } |
1082 | } |
1083 | |
1084 | void QCanFrameProcessorPrivate::encodeSignal(unsigned char *data, const QVariant &value, |
1085 | const QCanSignalDescription &signalDesc) |
1086 | { |
1087 | // We assume that signal's length does not exceed data size. |
1088 | // That is checked as a precondition to calling this method, so we do not |
1089 | // pass size for the data. |
1090 | switch (signalDesc.dataFormat()) { |
1091 | case QtCanBus::DataFormat::SignedInteger: |
1092 | encodeValue<qint64>(data, valueVar: value, signalDesc); |
1093 | break; |
1094 | case QtCanBus::DataFormat::UnsignedInteger: |
1095 | encodeValue<quint64>(data, valueVar: value, signalDesc); |
1096 | break; |
1097 | case QtCanBus::DataFormat::Float: |
1098 | encodeValue<float>(data, valueVar: value, signalDesc); |
1099 | break; |
1100 | case QtCanBus::DataFormat::Double: |
1101 | encodeValue<double>(data, valueVar: value, signalDesc); |
1102 | break; |
1103 | case QtCanBus::DataFormat::AsciiString: |
1104 | encodeAscii(data, value, signalDesc); |
1105 | break; |
1106 | } |
1107 | } |
1108 | |
1109 | std::optional<QtCanBus::UniqueId> |
1110 | QCanFrameProcessorPrivate::(const QCanBusFrame &frame) const |
1111 | { |
1112 | const auto signalDataEnd = extractMaxBitNum(startBit: uidDescription.startBit(), |
1113 | bitLength: uidDescription.bitLength(), |
1114 | endian: uidDescription.endian()); |
1115 | const bool dataFromPayload = uidDescription.source() == QtCanBus::DataSource::Payload; |
1116 | |
1117 | // For the FrameId case we do not really care if the frame id is extended |
1118 | // or not, because QCanBusFrame::FrameId is anyway 32-bit unsigned. |
1119 | const auto maxDataLength = dataFromPayload ? frame.payload().size() * 8 : 29; |
1120 | |
1121 | if (signalDataEnd >= maxDataLength) |
1122 | return {}; // add a more specific error description? |
1123 | |
1124 | const QByteArray payload = frame.payload(); |
1125 | const auto frameId = frame.frameId(); |
1126 | const unsigned char *data = dataFromPayload |
1127 | ? reinterpret_cast<const unsigned char *>(payload.data()) |
1128 | : reinterpret_cast<const unsigned char *>(&frameId); |
1129 | |
1130 | // Now we need to do the same as when extracting a value for a signal, but |
1131 | // without additional value conversions. We have an extractValue() template |
1132 | // function, but it takes a QCanSignalDescription as an input parameter. |
1133 | // To reuse the code, we generate a dummy QCanSignalDescription based on the |
1134 | // values of uidDescription and call extractValue(). |
1135 | // This approach introduces some unneeded checks and also result conversions |
1136 | // to/from QVariant. If this becomes a problem, we can copy-paste the code |
1137 | // from extractValue() and remove the unneeded parts. |
1138 | |
1139 | QCanSignalDescription dummyDesc; |
1140 | dummyDesc.setDataSource(uidDescription.source()); |
1141 | dummyDesc.setDataEndian(uidDescription.endian()); |
1142 | dummyDesc.setStartBit(uidDescription.startBit()); |
1143 | dummyDesc.setBitLength(uidDescription.bitLength()); |
1144 | dummyDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger); |
1145 | // other fields are unused, so default-initialized |
1146 | |
1147 | using UnderlyingType = std::underlying_type_t<QtCanBus::UniqueId>; |
1148 | const QVariant val = extractValue<UnderlyingType>(data, signalDesc: dummyDesc); |
1149 | return QtCanBus::UniqueId{val.value<UnderlyingType>()}; |
1150 | } |
1151 | |
1152 | bool QCanFrameProcessorPrivate::fillUniqueId(unsigned char *data, quint16 sizeInBits, |
1153 | QtCanBus::UniqueId uniqueId) |
1154 | { |
1155 | const auto uidDataEnd = extractMaxBitNum(startBit: uidDescription.startBit(), |
1156 | bitLength: uidDescription.bitLength(), |
1157 | endian: uidDescription.endian()); |
1158 | if (uidDataEnd >= sizeInBits) { |
1159 | return false; // add a more specific error description? |
1160 | } |
1161 | |
1162 | // Now we need to do the same as when encoding signal value into the frame, |
1163 | // but without additional value conversions. We have encodeValue() template |
1164 | // function, but it takes QCanSignalDescription as an input parameter. |
1165 | // To reuse the code, we generate a dummy QCanSignalDescription based on the |
1166 | // values of uidDescription, and call encodeValue(). |
1167 | // This approach introduces some unneeded checks and QVariant conversions. |
1168 | // If this becomes a problem, we can copy-paste the code from encodeValue() |
1169 | // and remove all the unneeded parts. |
1170 | |
1171 | QCanSignalDescription dummyDesc; |
1172 | dummyDesc.setDataSource(uidDescription.source()); |
1173 | dummyDesc.setDataEndian(uidDescription.endian()); |
1174 | dummyDesc.setStartBit(uidDescription.startBit()); |
1175 | dummyDesc.setBitLength(uidDescription.bitLength()); |
1176 | dummyDesc.setDataFormat(QtCanBus::DataFormat::UnsignedInteger); |
1177 | // other fields are unused, so default-initialized |
1178 | |
1179 | using UnderlyingType = std::underlying_type_t<QtCanBus::UniqueId>; |
1180 | encodeValue<UnderlyingType>(data, valueVar: QVariant::fromValue(value: qToUnderlying(e: uniqueId)), signalDesc: dummyDesc); |
1181 | return true; |
1182 | } |
1183 | |
1184 | QCanFrameProcessorPrivate *QCanFrameProcessorPrivate::get(const QCanFrameProcessor &processor) |
1185 | { |
1186 | return processor.d.get(); |
1187 | } |
1188 | |
1189 | QT_END_NAMESPACE |
1190 | |
1191 | #include "moc_qcanframeprocessor.cpp" |
1192 | |