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

source code of qtserialbus/src/serialbus/qcanframeprocessor.cpp