| 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 |  |