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
17QT_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.
30static quint16 extractMaxBitNum(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*/
150QCanFrameProcessor::QCanFrameProcessor()
151 : d(std::make_unique<QCanFrameProcessorPrivate>())
152{
153}
154
155/*!
156 Destroys this frame processor.
157*/
158QCanFrameProcessor::~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*/
192QCanBusFrame 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*/
296QCanFrameProcessor::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*/
306QString 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*/
317QStringList 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*/
329QList<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*/
349void 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*/
362void 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*/
374void 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*/
388QCanUniqueIdDescription 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*/
402void 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*/
436QCanFrameProcessor::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
539void QCanFrameProcessorPrivate::resetErrors()
540{
541 error = QCanFrameProcessor::Error::None;
542 errorString.clear();
543 warnings.clear();
544}
545
546void QCanFrameProcessorPrivate::setError(QCanFrameProcessor::Error err, const QString &desc)
547{
548 error = err;
549 errorString = desc;
550}
551
552void QCanFrameProcessorPrivate::addWarning(const QString &warning)
553{
554 warnings.push_back(t: warning);
555}
556
557QVariant 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
585static bool needValueConversion(const QCanSignalDescription &signalDesc)
586{
587 return !qIsNaN(d: signalDesc.factor()) || !qIsNaN(d: signalDesc.offset())
588 || !qIsNaN(d: signalDesc.scaling());
589}
590
591template <typename T>
592static 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
607static 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
626template <typename T>
627static QVariant extractValue(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
739template <typename T>
740static 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
843static 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
864QVariant 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
887template <typename T>
888static 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
977static constexpr bool isNativeLittleEndian()
978{
979#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
980 return true;
981#else
982 return false;
983#endif
984}
985
986template <typename T>
987static 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
1059static 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
1084void 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
1109std::optional<QtCanBus::UniqueId>
1110QCanFrameProcessorPrivate::extractUniqueId(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
1152bool 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
1184QCanFrameProcessorPrivate *QCanFrameProcessorPrivate::get(const QCanFrameProcessor &processor)
1185{
1186 return processor.d.get();
1187}
1188
1189QT_END_NAMESPACE
1190
1191#include "moc_qcanframeprocessor.cpp"
1192

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