| 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 "qcansignaldescription.h" | 
| 5 | #include "qcansignaldescription_p.h" | 
| 6 |  | 
| 7 | QT_BEGIN_NAMESPACE | 
| 8 |  | 
| 9 | /*! | 
| 10 |     \class QCanSignalDescription | 
| 11 |     \inmodule QtSerialBus | 
| 12 |     \since 6.5 | 
| 13 |     \preliminary | 
| 14 |  | 
| 15 |     \brief The QCanSignalDescription class describes the rules to extract one | 
| 16 |     value out of the CAN frame and represent it in an application-defined | 
| 17 |     format. | 
| 18 |  | 
| 19 |     The QCanSignalDescription class can be used to provide a signal description | 
| 20 |     and later use it to decode a received \l QCanBusFrame or encode the input | 
| 21 |     data into a \l QCanBusFrame that can be sent to the receiver. | 
| 22 |  | 
| 23 |     \section2 General Description | 
| 24 |  | 
| 25 |     Each CAN frame can contain multiple values. The rules to extract the values | 
| 26 |     from a CAN frame include the following: | 
| 27 |     \list | 
| 28 |         \li Data source (frame ID or payload). | 
| 29 |         \li Data endianness. See \l {Data Endianness Processing} section for | 
| 30 |             more details. | 
| 31 |         \li Data format. | 
| 32 |         \li Start bit position. | 
| 33 |         \li Data length in bits. | 
| 34 |         \li Multiplexing options. | 
| 35 |     \endlist | 
| 36 |  | 
| 37 |     Start bit position is specified relative to the selected data source. The | 
| 38 |     bits are counted starting from the LSB. | 
| 39 |  | 
| 40 |     Once the data is extracted, it might require conversion to an | 
| 41 |     application-defined format. The following parameters can be used for that: | 
| 42 |     \list | 
| 43 |         \li Various parameters for converting the extracted value to a physical | 
| 44 |             value (factor, offset, scale). | 
| 45 |         \li Expected data range. | 
| 46 |         \li Data units. | 
| 47 |     \endlist | 
| 48 |  | 
| 49 |     The QCanSignalDescription class provides methods to control all those | 
| 50 |     parameters. | 
| 51 |  | 
| 52 |     \section2 Data Endianness Processing | 
| 53 |  | 
| 54 |     Little endian and big endian data is encoded differently. | 
| 55 |     For big endian values, start bit positions are given for the most | 
| 56 |     significant bit. For little endian values, the start position is that of | 
| 57 |     the least significant bit. | 
| 58 |  | 
| 59 |     Let's consider two examples. In both examples we will encode two 12-bit | 
| 60 |     values in the 3-byte payload. | 
| 61 |  | 
| 62 |     \section3 Little Endian | 
| 63 |  | 
| 64 |     For the little endian case the data layout can be represented by the | 
| 65 |     following image: | 
| 66 |  | 
| 67 |     \image canbus_signals_le.png | 
| 68 |  | 
| 69 |     Here the columns represent bit numbers, and the rows represent byte numbers. | 
| 70 |     \c {LSB} marks the first (least significant) bit of the value, and \c {MSB} | 
| 71 |     marks the last (most significant) bit of the value. The blue color marks the | 
| 72 |     first value, and the orange color marks the second value. | 
| 73 |  | 
| 74 |     The information about these values will be encoded in QCanSignalDescription | 
| 75 |     in the following way: | 
| 76 |  | 
| 77 |     \code | 
| 78 |     QCanSignalDescription signal1; | 
| 79 |     signal1.setDataEndian(QSysInfo::Endian::LittleEndian); | 
| 80 |     signal1.setStartBit(0); | 
| 81 |     signal1.setBitLength(12); | 
| 82 |     // other parameters for signal1 | 
| 83 |  | 
| 84 |     QCanSignalDescription signal2; | 
| 85 |     signal2.setDataEndian(QSysInfo::Endian::LittleEndian); | 
| 86 |     signal2.setStartBit(12); | 
| 87 |     signal2.setBitLength(12); | 
| 88 |     // other parameters for signal2 | 
| 89 |     \endcode | 
| 90 |  | 
| 91 |     \section3 Big Endian | 
| 92 |  | 
| 93 |     The following image represents the value layout for the big endian case: | 
| 94 |  | 
| 95 |     \image canbus_signals_be.png | 
| 96 |  | 
| 97 |     The values can be represented in QCanSignalDescription in the following | 
| 98 |     way: | 
| 99 |  | 
| 100 |     \code | 
| 101 |     QCanSignalDescription signal1; | 
| 102 |     signal1.setDataEndian(QSysInfo::Endian::BigEndian); | 
| 103 |     signal1.setStartBit(7); | 
| 104 |     signal1.setBitLength(12); | 
| 105 |     // other parameters for signal1 | 
| 106 |  | 
| 107 |     QCanSignalDescription signal2; | 
| 108 |     signal2.setDataEndian(QSysInfo::Endian::BigEndian); | 
| 109 |     signal2.setStartBit(11); | 
| 110 |     signal2.setBitLength(12); | 
| 111 |     // other parameters for signal2 | 
| 112 |     \endcode | 
| 113 |  | 
| 114 |     Note how the start bits are different from the little endian case. Also the | 
| 115 |     values are aligned differently. | 
| 116 |  | 
| 117 |     \section2 Multiplexed Signals Explained | 
| 118 |  | 
| 119 |     There are two common ways to encode the data in the CAN payload: | 
| 120 |     \list | 
| 121 |         \li Each range of bits always represents the same signal. For example, | 
| 122 |             \c {Bytes 0-1} in a payload can represent an engine speed (in rpm), | 
| 123 |             and \c {Bytes 2-3} can represent the vehicle speed (in km/h). | 
| 124 |         \li The same range of bits can represent different data, depending on | 
| 125 |             the values of some other bits in the payload. For example, if | 
| 126 |             \c {Byte 0} has the value \c {0}, the \c {Bytes 1-2} represent an | 
| 127 |             engine speed (in rpm), and if \c {Byte 0} has the value \c {1}, the | 
| 128 |             same \c {Bytes 1-2} represent a vehicle speed (in km/h). | 
| 129 |     \endlist | 
| 130 |  | 
| 131 |     The second case uses signal multiplexing. In the provided example we will | 
| 132 |     have three signals. The first signal represents the value of \c {Byte 0} and | 
| 133 |     acts like a multiplexor signal. The other two signals represent an engine | 
| 134 |     speed and a vehicle speed respectively, but only one of them can be | 
| 135 |     extracted from the CAN payload at a time. Which signal should be extracted | 
| 136 |     is defined by the value of the multiplexor signal. | 
| 137 |  | 
| 138 |     In more complicated cases the payload can have multiple multiplexor signals. | 
| 139 |     In such cases the signal can be extracted from the payload only when all | 
| 140 |     multiplexors contain the expected values. | 
| 141 |  | 
| 142 |     \section2 Value Conversions | 
| 143 |  | 
| 144 |     In many cases the signals transferred over CAN bus cannot hold the full | 
| 145 |     range of the physical values that they represent. To overcome these | 
| 146 |     limitations, the physical values are converted to a smaller range before | 
| 147 |     transmission, and can be restored on the receiving end. | 
| 148 |  | 
| 149 |     The following formulas are used to convert between the physical value and | 
| 150 |     the signal's value: | 
| 151 |  | 
| 152 |     \badcode | 
| 153 |     physicalValue = scaling * (signalValue * factor + offset); | 
| 154 |     signalValue = (physicalValue / scaling - offset) / factor; | 
| 155 |     \endcode | 
| 156 |  | 
| 157 |     The factor and scaling parameters cannot be equal to \c {0}. | 
| 158 |  | 
| 159 |     If any of the parameters equals to \l qQNaN(), it is not used during the | 
| 160 |     conversion. If all of the parameters are equal to \l qQNaN() (which is the | 
| 161 |     default), the conversion is not performed. | 
| 162 | */ | 
| 163 |  | 
| 164 | /*! | 
| 165 |     \struct QCanSignalDescription::MultiplexValueRange | 
| 166 |     \inmodule QtSerialBus | 
| 167 |     \since 6.5 | 
| 168 |  | 
| 169 |     \brief Defines a range of values for a multiplexor signal. | 
| 170 |  | 
| 171 |     Each multiplexor signal can have several ranges of values assigned to it. | 
| 172 |     This type represents one range. Both minimum and maximum values are | 
| 173 |     included in the range. Minimum and maximum values can be equal, so a | 
| 174 |     MultiplexValueRange is never empty. If maximum is less than minimum, the | 
| 175 |     values will be swapped while doing the range check. | 
| 176 |  | 
| 177 |     \sa {Multiplexed Signals Explained} | 
| 178 | */ | 
| 179 |  | 
| 180 | /*! | 
| 181 |     \variable QCanSignalDescription::MultiplexValueRange::minimum | 
| 182 |     \brief the minimum value of the range. | 
| 183 | */ | 
| 184 |  | 
| 185 | /*! | 
| 186 |     \variable QCanSignalDescription::MultiplexValueRange::maximum | 
| 187 |     \brief the maximum value of the range. | 
| 188 | */ | 
| 189 |  | 
| 190 | /*! | 
| 191 |     \typealias QCanSignalDescription::MultiplexValues | 
| 192 | */ | 
| 193 |  | 
| 194 | /*! | 
| 195 |     \typealias QCanSignalDescription::MultiplexSignalValues | 
| 196 | */ | 
| 197 |  | 
| 198 | /*! | 
| 199 |     Creates an empty signal description. | 
| 200 | */ | 
| 201 | QCanSignalDescription::QCanSignalDescription() : d(new QCanSignalDescriptionPrivate) | 
| 202 | { | 
| 203 | } | 
| 204 |  | 
| 205 | /*! | 
| 206 |     Creates a signal description with the values copied from \a other. | 
| 207 | */ | 
| 208 | QCanSignalDescription::QCanSignalDescription(const QCanSignalDescription &other) : d(other.d) | 
| 209 | { | 
| 210 | } | 
| 211 |  | 
| 212 | /*! | 
| 213 |     \fn QCanSignalDescription::QCanSignalDescription(QCanSignalDescription &&other) noexcept | 
| 214 |  | 
| 215 |     Creates a signal description by moving from \a other. | 
| 216 |  | 
| 217 |     \note The moved-from QCanSignalDescription object can only be destroyed or | 
| 218 |     assigned to. The effect of calling other functions than the destructor or | 
| 219 |     one of the assignment operators is undefined. | 
| 220 | */ | 
| 221 |  | 
| 222 | /*! | 
| 223 |     \fn QCanSignalDescription::~QCanSignalDescription() | 
| 224 |  | 
| 225 |     Destroys this signal description. | 
| 226 | */ | 
| 227 |  | 
| 228 | QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QCanSignalDescriptionPrivate) | 
| 229 |  | 
| 230 | /*! | 
| 231 |     Assigns the values from \a other to this signal description. | 
| 232 | */ | 
| 233 | QCanSignalDescription &QCanSignalDescription::operator=(const QCanSignalDescription &other) | 
| 234 | { | 
| 235 |     d = other.d; | 
| 236 |     return *this; | 
| 237 | } | 
| 238 |  | 
| 239 | /*! | 
| 240 |     \fn QCanSignalDescription &QCanSignalDescription::operator=(QCanSignalDescription &&other) noexcept | 
| 241 |  | 
| 242 |     Move-assigns the values from \a other to this signal description. | 
| 243 |  | 
| 244 |     \note The moved-from QCanSignalDescription object can only be destroyed or | 
| 245 |     assigned to. The effect of calling other functions than the destructor or | 
| 246 |     one of the assignment operators is undefined. | 
| 247 | */ | 
| 248 |  | 
| 249 | /*! | 
| 250 |     Returns \c true when the signal description is valid and \c false otherwise. | 
| 251 |  | 
| 252 |     A valid signal description \e must fulfill the following conditions: | 
| 253 |     \list | 
| 254 |         \li have a non-empty \l name() | 
| 255 |         \li have \l bitLength() \c {== 32} if the \l dataFormat() is | 
| 256 |             \l {QtCanBus::DataFormat::}{Float} | 
| 257 |         \li have \l bitLength() \c {== 64} if the \l dataFormat() is | 
| 258 |             \l {QtCanBus::DataFormat::}{Double} | 
| 259 |         \li the \l bitLength() \e must be a multiple of \c 8 if the | 
| 260 |             \l dataFormat() is \l {QtCanBus::DataFormat::}{AsciiString} | 
| 261 |         \li the \l bitLength() \e must be greater than \c 0 and less than or | 
| 262 |             equal to \c {64}. | 
| 263 |     \endlist | 
| 264 |  | 
| 265 |     \sa bitLength(), dataFormat(), name() | 
| 266 | */ | 
| 267 | bool QCanSignalDescription::isValid() const | 
| 268 | { | 
| 269 |     const bool formatMatch = [this]() { | 
| 270 |         if (d->format == QtCanBus::DataFormat::Float) | 
| 271 |             return d->dataLength == 32; | 
| 272 |         if (d->format == QtCanBus::DataFormat::Double) | 
| 273 |             return d->dataLength == 64; | 
| 274 |         if (d->format == QtCanBus::DataFormat::AsciiString) | 
| 275 |             return d->dataLength % 8 == 0; | 
| 276 |         return d->dataLength > 0 && d->dataLength <= 64; | 
| 277 |     }(); | 
| 278 |     return !d->name.isEmpty() && formatMatch; | 
| 279 | } | 
| 280 |  | 
| 281 | /*! | 
| 282 |     Returns the name of the signal. | 
| 283 |  | 
| 284 |     \sa setName(), isValid() | 
| 285 | */ | 
| 286 | QString QCanSignalDescription::name() const | 
| 287 | { | 
| 288 |     return d->name; | 
| 289 | } | 
| 290 |  | 
| 291 | /*! | 
| 292 |     Sets the name of the signal to \a name. | 
| 293 |  | 
| 294 |     The signal's name must be unique within a CAN message. | 
| 295 |  | 
| 296 |     \sa name() | 
| 297 | */ | 
| 298 | void QCanSignalDescription::setName(const QString &name) | 
| 299 | { | 
| 300 |     d.detach(); | 
| 301 |     d->name = name; | 
| 302 | } | 
| 303 |  | 
| 304 | /*! | 
| 305 |     Returns the physical unit (e.g. km/h) of the signal's value or an empty | 
| 306 |     string if the unit is not set. | 
| 307 |  | 
| 308 | //! [qcansignaldesc-aux-parameter] | 
| 309 |     This parameter is introduced only for extra description. It's not used | 
| 310 |     during signal processing. | 
| 311 | //! [qcansignaldesc-aux-parameter] | 
| 312 |  | 
| 313 |     \sa setPhysicalUnit() | 
| 314 | */ | 
| 315 | QString QCanSignalDescription::physicalUnit() const | 
| 316 | { | 
| 317 |     return d->unit; | 
| 318 | } | 
| 319 |  | 
| 320 | /*! | 
| 321 |     Sets the physical \a unit (e.g. km/h) of the signal's value. | 
| 322 |  | 
| 323 |     \include qcansignaldescription.cpp qcansignaldesc-aux-parameter | 
| 324 |  | 
| 325 |     \sa physicalUnit() | 
| 326 | */ | 
| 327 | void QCanSignalDescription::setPhysicalUnit(const QString &unit) | 
| 328 | { | 
| 329 |     d.detach(); | 
| 330 |     d->unit = unit; | 
| 331 | } | 
| 332 |  | 
| 333 | /*! | 
| 334 |     Returns the receiver node for this signal. | 
| 335 |  | 
| 336 |     \include qcansignaldescription.cpp qcansignaldesc-aux-parameter | 
| 337 |  | 
| 338 |     \sa setReceiver() | 
| 339 | */ | 
| 340 | QString QCanSignalDescription::receiver() const | 
| 341 | { | 
| 342 |     return d->receiver; | 
| 343 | } | 
| 344 |  | 
| 345 | /*! | 
| 346 |     Sets the \a receiver node for this signal. | 
| 347 |  | 
| 348 |     \include qcansignaldescription.cpp qcansignaldesc-aux-parameter | 
| 349 |  | 
| 350 |     \sa receiver() | 
| 351 | */ | 
| 352 | void QCanSignalDescription::setReceiver(const QString &receiver) | 
| 353 | { | 
| 354 |     d.detach(); | 
| 355 |     d->receiver = receiver; | 
| 356 | } | 
| 357 |  | 
| 358 | /*! | 
| 359 |     Returns the comment for the signal. | 
| 360 |  | 
| 361 |     \include qcansignaldescription.cpp qcansignaldesc-aux-parameter | 
| 362 |  | 
| 363 |     \sa setComment() | 
| 364 | */ | 
| 365 | QString QCanSignalDescription::() const | 
| 366 | { | 
| 367 |     return d->comment; | 
| 368 | } | 
| 369 |  | 
| 370 | /*! | 
| 371 |     Sets the comment for the signal to \a text. | 
| 372 |  | 
| 373 |     \include qcansignaldescription.cpp qcansignaldesc-aux-parameter | 
| 374 |  | 
| 375 |     \sa comment() | 
| 376 | */ | 
| 377 | void QCanSignalDescription::(const QString &text) | 
| 378 | { | 
| 379 |     d.detach(); | 
| 380 |     d->comment = text; | 
| 381 | } | 
| 382 |  | 
| 383 | /*! | 
| 384 |     Returns the data source of the signal's value. | 
| 385 |  | 
| 386 |     By default, \l {QtCanBus::DataSource::}{Payload} is used. | 
| 387 |  | 
| 388 |     \sa setDataSource(), QtCanBus::DataSource | 
| 389 | */ | 
| 390 | QtCanBus::DataSource QCanSignalDescription::dataSource() const | 
| 391 | { | 
| 392 |     return d->source; | 
| 393 | } | 
| 394 |  | 
| 395 | /*! | 
| 396 |     Sets the data source of the signal's value to \a source. | 
| 397 |  | 
| 398 |     \sa dataSource(), QtCanBus::DataSource | 
| 399 | */ | 
| 400 | void QCanSignalDescription::setDataSource(QtCanBus::DataSource source) | 
| 401 | { | 
| 402 |     d.detach(); | 
| 403 |     d->source = source; | 
| 404 | } | 
| 405 |  | 
| 406 | /*! | 
| 407 |     Returns the data endian of the signal's value. | 
| 408 |  | 
| 409 |     By default, \l {QSysInfo::}{BigEndian} is used. | 
| 410 |  | 
| 411 |     \note The data endian is ignored if the \l dataFormat() is set to | 
| 412 |     \l {QtCanBus::DataFormat::}{AsciiString}. | 
| 413 |  | 
| 414 |     \sa setDataEndian(), QSysInfo::Endian | 
| 415 | */ | 
| 416 | QSysInfo::Endian QCanSignalDescription::dataEndian() const | 
| 417 | { | 
| 418 |     return d->endian; | 
| 419 | } | 
| 420 |  | 
| 421 | /*! | 
| 422 |     Sets the data endian of the signal's value to \a endian. | 
| 423 |  | 
| 424 |     \sa dataEndian(), QSysInfo::Endian | 
| 425 | */ | 
| 426 | void QCanSignalDescription::setDataEndian(QSysInfo::Endian endian) | 
| 427 | { | 
| 428 |     d.detach(); | 
| 429 |     d->endian = endian; | 
| 430 | } | 
| 431 |  | 
| 432 | /*! | 
| 433 |     Returns the data format of the signal's value. | 
| 434 |  | 
| 435 |     By default, \l {QtCanBus::DataFormat::}{SignedInteger} is used. | 
| 436 |  | 
| 437 |     \sa setDataFormat(), QtCanBus::DataFormat | 
| 438 | */ | 
| 439 | QtCanBus::DataFormat QCanSignalDescription::dataFormat() const | 
| 440 | { | 
| 441 |     return d->format; | 
| 442 | } | 
| 443 |  | 
| 444 | /*! | 
| 445 |     Sets the data format of the signal's value to \a format. | 
| 446 |  | 
| 447 |     \sa dataFormat(), QtCanBus::DataFormat | 
| 448 | */ | 
| 449 | void QCanSignalDescription::setDataFormat(QtCanBus::DataFormat format) | 
| 450 | { | 
| 451 |     d.detach(); | 
| 452 |     d->format = format; | 
| 453 | } | 
| 454 |  | 
| 455 | /*! | 
| 456 |     Returns the start bit of the signal's value in the \l dataSource(). | 
| 457 |  | 
| 458 |     \sa setStartBit(), bitLength(), setBitLength() | 
| 459 | */ | 
| 460 | quint16 QCanSignalDescription::startBit() const | 
| 461 | { | 
| 462 |     return d->startBit; | 
| 463 | } | 
| 464 |  | 
| 465 | /*! | 
| 466 |     Sets the start bit of the signal's value in the \l dataSource() to \a bit. | 
| 467 |  | 
| 468 |     \sa startBit(), bitLength(), setBitLength() | 
| 469 | */ | 
| 470 | void QCanSignalDescription::setStartBit(quint16 bit) | 
| 471 | { | 
| 472 |     d.detach(); | 
| 473 |     d->startBit = bit; | 
| 474 | } | 
| 475 |  | 
| 476 | /*! | 
| 477 |     Returns the bit length of the signal's value. | 
| 478 |  | 
| 479 |     \sa setBitLength(), startBit(), setStartBit() | 
| 480 | */ | 
| 481 | quint16 QCanSignalDescription::bitLength() const | 
| 482 | { | 
| 483 |     return d->dataLength; | 
| 484 | } | 
| 485 |  | 
| 486 | /*! | 
| 487 |     Sets the bit length of the signal's value to \a length. | 
| 488 |  | 
| 489 |     \sa bitLength(), startBit(), setStartBit() | 
| 490 | */ | 
| 491 | void QCanSignalDescription::setBitLength(quint16 length) | 
| 492 | { | 
| 493 |     d.detach(); | 
| 494 |     d->dataLength = length; | 
| 495 | } | 
| 496 |  | 
| 497 | /*! | 
| 498 |     Returns the factor that is used to convert the signal's value to a physical | 
| 499 |     value and back. | 
| 500 |  | 
| 501 |     By default the function returns \l qQNaN(), which means that a factor is not | 
| 502 |     used. | 
| 503 |  | 
| 504 |     The \l {Value Conversions} section explains how this parameter is used. | 
| 505 |  | 
| 506 |     \sa setFactor(), offset(), scaling() | 
| 507 | */ | 
| 508 | double QCanSignalDescription::factor() const | 
| 509 | { | 
| 510 |     return d->factor; | 
| 511 | } | 
| 512 |  | 
| 513 | /*! | 
| 514 |     Sets the factor that is used to convert the signal's value to a physical | 
| 515 |     value and back to \a factor. | 
| 516 |  | 
| 517 |     Pass \l qQNaN() to this method to skip this parameter during the conversion. | 
| 518 |  | 
| 519 |     The factor cannot be 0. An attempt to set a zero factor is equivalent to | 
| 520 |     setting it to \l qQNaN(). | 
| 521 |  | 
| 522 |     The \l {Value Conversions} section explains how this parameter is used. | 
| 523 |  | 
| 524 |     \sa factor(), setOffset(), setScaling() | 
| 525 | */ | 
| 526 | void QCanSignalDescription::setFactor(double factor) | 
| 527 | { | 
| 528 |     d.detach(); | 
| 529 |     if (qFuzzyIsNull(d: factor)) | 
| 530 |         d->factor = qQNaN(); | 
| 531 |     else | 
| 532 |         d->factor = factor; | 
| 533 | } | 
| 534 |  | 
| 535 | /*! | 
| 536 |     Returns the offset that is used to convert the signal's value to a physical | 
| 537 |     value and back. | 
| 538 |  | 
| 539 |     By default the function returns \l qQNaN(), which means that an offset is | 
| 540 |     not used. | 
| 541 |  | 
| 542 |     The \l {Value Conversions} section explains how this parameter is used. | 
| 543 |  | 
| 544 |     \sa setOffset(), factor(), scaling() | 
| 545 | */ | 
| 546 | double QCanSignalDescription::offset() const | 
| 547 | { | 
| 548 |     return d->offset; | 
| 549 | } | 
| 550 |  | 
| 551 | /*! | 
| 552 |     Sets the offset that is used to convert the signal's value to a physical | 
| 553 |     value and back to \a offset. | 
| 554 |  | 
| 555 |     Pass \l qQNaN() to this method to skip this parameter during the conversion. | 
| 556 |  | 
| 557 |     The \l {Value Conversions} section explains how this parameter is used. | 
| 558 |  | 
| 559 |     \sa offset(), setFactor(), setScaling() | 
| 560 | */ | 
| 561 | void QCanSignalDescription::setOffset(double offset) | 
| 562 | { | 
| 563 |     d.detach(); | 
| 564 |     d->offset = offset; | 
| 565 | } | 
| 566 |  | 
| 567 | /*! | 
| 568 |     Returns the scaling that is used to convert the signal's value to a physical | 
| 569 |     value and back. | 
| 570 |  | 
| 571 |     By default the function returns \l qQNaN(), which means that scaling is not | 
| 572 |     used. | 
| 573 |  | 
| 574 |     The \l {Value Conversions} section explains how this parameter is used. | 
| 575 |  | 
| 576 |     \sa setScaling(), offset(), factor() | 
| 577 | */ | 
| 578 | double QCanSignalDescription::scaling() const | 
| 579 | { | 
| 580 |     return d->scaling; | 
| 581 | } | 
| 582 |  | 
| 583 | /*! | 
| 584 |     Sets the scaling that is used to convert the signal's value to a physical | 
| 585 |     value and back to \a scaling. | 
| 586 |  | 
| 587 |     Pass \l qQNaN() to this method to skip this parameter during the conversion. | 
| 588 |  | 
| 589 |     The scaling cannot be 0. An attempt to set zero scaling is equivalent to | 
| 590 |     setting it to \l qQNaN(). | 
| 591 |  | 
| 592 |     The \l {Value Conversions} section explains how this parameter is used. | 
| 593 |  | 
| 594 |     \sa scaling(), setOffset(), setFactor() | 
| 595 | */ | 
| 596 | void QCanSignalDescription::setScaling(double scaling) | 
| 597 | { | 
| 598 |     d.detach(); | 
| 599 |     if (qFuzzyIsNull(d: scaling)) | 
| 600 |         d->scaling = qQNaN(); | 
| 601 |     else | 
| 602 |         d->scaling = scaling; | 
| 603 | } | 
| 604 |  | 
| 605 | /*! | 
| 606 |     Returns the minimum supported value for the signal. | 
| 607 |  | 
| 608 |     By default the function returns \l qQNaN(), which means that there is no | 
| 609 |     minimum value. | 
| 610 |  | 
| 611 |     \sa setRange(), maximum() | 
| 612 | */ | 
| 613 | double QCanSignalDescription::minimum() const | 
| 614 | { | 
| 615 |     return d->minimum; | 
| 616 | } | 
| 617 |  | 
| 618 | /*! | 
| 619 |     Returns the maximum supported value for the signal. | 
| 620 |  | 
| 621 |     By default the function returns \l qQNaN(), which means that there is no | 
| 622 |     maximum value. | 
| 623 |  | 
| 624 |     \sa setRange(), minimum() | 
| 625 | */ | 
| 626 | double QCanSignalDescription::maximum() const | 
| 627 | { | 
| 628 |     return d->maximum; | 
| 629 | } | 
| 630 |  | 
| 631 | /*! | 
| 632 |     Sets the \a minimum and \a maximum for the signal's value. | 
| 633 |  | 
| 634 |     Setting one or both of the parameters to \l qQNaN() means that the | 
| 635 |     corresponding limit will not be used. | 
| 636 |  | 
| 637 |     \sa minimum(), maximum() | 
| 638 | */ | 
| 639 | void QCanSignalDescription::setRange(double minimum, double maximum) | 
| 640 | { | 
| 641 |     d.detach(); | 
| 642 |     if (qIsNaN(d: minimum) || qIsNaN(d: maximum) || minimum <= maximum) { | 
| 643 |         d->minimum = minimum; | 
| 644 |         d->maximum = maximum; | 
| 645 |     } else { | 
| 646 |         qWarning(msg: "Minimum value is greater than maximum. The values will be swapped." ); | 
| 647 |         d->minimum = maximum; | 
| 648 |         d->maximum = minimum; | 
| 649 |     } | 
| 650 | } | 
| 651 |  | 
| 652 | /*! | 
| 653 |     Returns the multiplex state of the signal. | 
| 654 |  | 
| 655 |     See the \l {Multiplexed Signals Explained} section for more details on | 
| 656 |     multiplexed signals. | 
| 657 |  | 
| 658 |     By default this method returns \l {QtCanBus::MultiplexState::}{None}. | 
| 659 |  | 
| 660 |     \sa setMultiplexState(), QtCanBus::MultiplexState | 
| 661 | */ | 
| 662 | QtCanBus::MultiplexState QCanSignalDescription::multiplexState() const | 
| 663 | { | 
| 664 |     return d->muxState; | 
| 665 | } | 
| 666 |  | 
| 667 | /*! | 
| 668 |     Sets the multiplex state of the signal to \a state. | 
| 669 |  | 
| 670 |     See the \l {Multiplexed Signals Explained} section for more details on | 
| 671 |     multiplexed signals. | 
| 672 |  | 
| 673 |     \sa multiplexState(), QtCanBus::MultiplexState | 
| 674 | */ | 
| 675 | void QCanSignalDescription::setMultiplexState(QtCanBus::MultiplexState state) | 
| 676 | { | 
| 677 |     d.detach(); | 
| 678 |     d->muxState = state; | 
| 679 | } | 
| 680 |  | 
| 681 | /*! | 
| 682 |     Returns the \l {Multiplexed Signals Explained}{multiplexor signals} and | 
| 683 |     their desired values that are used to properly identify this signal. | 
| 684 |  | 
| 685 |     The returned hash contains signal names as keys and respective desired | 
| 686 |     ranges of values as values. | 
| 687 |  | 
| 688 |     This signal's value can be extracted from the payload only when all the | 
| 689 |     signals from the hash have the expected values. | 
| 690 |  | 
| 691 |     \sa multiplexState(), clearMultiplexSignals(), setMultiplexSignals(), | 
| 692 |     addMultiplexSignal() | 
| 693 | */ | 
| 694 | QCanSignalDescription::MultiplexSignalValues QCanSignalDescription::multiplexSignals() const | 
| 695 | { | 
| 696 |     return d->muxSignals; | 
| 697 | } | 
| 698 |  | 
| 699 | /*! | 
| 700 |     Removes all \l {Multiplexed Signals Explained}{multiplexor signals} for | 
| 701 |     this signal. | 
| 702 |  | 
| 703 |     \sa multiplexSignals(), setMultiplexSignals(), addMultiplexSignal() | 
| 704 | */ | 
| 705 | void QCanSignalDescription::clearMultiplexSignals() | 
| 706 | { | 
| 707 |     d.detach(); | 
| 708 |     d->muxSignals.clear(); | 
| 709 | } | 
| 710 |  | 
| 711 | /*! | 
| 712 |     Sets the \l {Multiplexed Signals Explained}{multiplexor signals} for this | 
| 713 |     signal to \a multiplexorSignals. | 
| 714 |  | 
| 715 |     The \a multiplexorSignals hash \e must contain signal names as keys and | 
| 716 |     respective desired value ranges as values. | 
| 717 |  | 
| 718 |     \sa multiplexState(), multiplexSignals(), clearMultiplexSignals(), | 
| 719 |     addMultiplexSignal() | 
| 720 | */ | 
| 721 | void QCanSignalDescription::setMultiplexSignals(const MultiplexSignalValues &multiplexorSignals) | 
| 722 | { | 
| 723 |     d.detach(); | 
| 724 |     d->muxSignals = multiplexorSignals; | 
| 725 | } | 
| 726 |  | 
| 727 | /*! | 
| 728 |     Adds a new \l {Multiplexed Signals Explained}{multiplexor signal} for this | 
| 729 |     signal. The \a name parameter contains the name of the multiplexor signal, | 
| 730 |     and the \a ranges parameter contains the desired value ranges. | 
| 731 |  | 
| 732 |     If this signal already has desired value ranges for the multiplexor signal | 
| 733 |     \a name, the ranges are overwritten. | 
| 734 |  | 
| 735 |     \sa multiplexState(), multiplexSignals(), clearMultiplexSignals(), | 
| 736 |     setMultiplexSignals() | 
| 737 | */ | 
| 738 | void QCanSignalDescription::addMultiplexSignal(const QString &name, const MultiplexValues &ranges) | 
| 739 | { | 
| 740 |     d.detach(); | 
| 741 |     d->muxSignals.insert(key: name, value: ranges); | 
| 742 | } | 
| 743 |  | 
| 744 | /*! | 
| 745 |     \overload | 
| 746 |  | 
| 747 |     This is a convenience overload for the case when the multiplexor signal is | 
| 748 |     expected to have only one specific value, not a range of values. | 
| 749 |  | 
| 750 |     The \a name parameter contains the name of the multiplexor signal, | 
| 751 |     and the \a value parameter contains the desired value. | 
| 752 |  | 
| 753 |     If this signal already has desired value ranges for the multiplexor signal | 
| 754 |     \a name, the ranges are overwritten. | 
| 755 |  | 
| 756 |     \sa multiplexState(), multiplexSignals(), clearMultiplexSignals(), | 
| 757 |     setMultiplexSignals() | 
| 758 | */ | 
| 759 | void QCanSignalDescription::addMultiplexSignal(const QString &name, const QVariant &value) | 
| 760 | { | 
| 761 |     d.detach(); | 
| 762 |     d->muxSignals.insert(key: name, value: { {.minimum: value, .maximum: value} }); | 
| 763 | } | 
| 764 |  | 
| 765 | #ifndef QT_NO_DEBUG_STREAM | 
| 766 | QDebug QCanSignalDescription::debugStreaming(QDebug dbg, const QCanSignalDescription &sig) | 
| 767 | { | 
| 768 |     QDebugStateSaver saver(dbg); | 
| 769 |     dbg.nospace() << "QCanSignalDescription("  << sig.name() << ", Source = "  << sig.dataSource() | 
| 770 |                   << ", Format = "  << sig.dataFormat() << ", Endian = "  << sig.dataEndian() | 
| 771 |                   << ", StartBit = "  << sig.startBit() << ", BitLength = "  << sig.bitLength(); | 
| 772 |     if (!sig.physicalUnit().isEmpty()) | 
| 773 |         dbg << ", Units = "  << sig.physicalUnit(); | 
| 774 |     if (!sig.receiver().isEmpty()) | 
| 775 |         dbg << ", Receiver = "  << sig.receiver(); | 
| 776 |     if (!sig.comment().isEmpty()) | 
| 777 |         dbg << ", Comment = "  << sig.comment(); | 
| 778 |     dbg << ", Factor = "  << sig.factor() << ", Offset = "  << sig.offset() | 
| 779 |         << ", Scaling = "  << sig.scaling(); | 
| 780 |     dbg << ", Minimum = "  << sig.minimum() << ", Maximum = "  << sig.maximum(); | 
| 781 |     dbg << ", Multiplex State = "  << sig.multiplexState(); | 
| 782 |     const auto muxSignals = sig.multiplexSignals(); | 
| 783 |     if (!muxSignals.isEmpty()) { | 
| 784 |         dbg << ", Multiplexor Signals: {" ; | 
| 785 |         for (auto it = muxSignals.cbegin(); it != muxSignals.cend(); ++it) { | 
| 786 |             if (it != muxSignals.cbegin()) | 
| 787 |                 dbg << ", " ; | 
| 788 |             dbg << "("  << it.key() << ", "  << it.value() << ")" ; | 
| 789 |         } | 
| 790 |         dbg << "}" ; | 
| 791 |     } | 
| 792 |     dbg << ")" ; | 
| 793 |     return dbg; | 
| 794 | } | 
| 795 |  | 
| 796 | QDebug QCanSignalDescription::MultiplexValueRange::debugStreaming(QDebug dbg, | 
| 797 |                                                                   const MultiplexValueRange &range) | 
| 798 | { | 
| 799 |     QDebugStateSaver saver(dbg); | 
| 800 |     dbg.nospace() << "MultiplexValueRange("  << range.minimum << ", "  << range.maximum << ")" ; | 
| 801 |     return dbg; | 
| 802 | } | 
| 803 | #endif // QT_NO_DEBUG_STREAM | 
| 804 |  | 
| 805 | template <typename T> | 
| 806 | static bool checkValue(const QVariant &valueVar, | 
| 807 |                        const QCanSignalDescription::MultiplexValues &ranges) | 
| 808 | { | 
| 809 |     const T val = valueVar.value<T>(); | 
| 810 |     for (const auto &pair : ranges) { | 
| 811 |         T min = pair.minimum.value<T>(); | 
| 812 |         T max = pair.maximum.value<T>(); | 
| 813 |         if (min > max) | 
| 814 |             max = std::exchange(min, max); | 
| 815 |         if (val >= min && val <= max) | 
| 816 |             return true; | 
| 817 |     } | 
| 818 |     return false; | 
| 819 | } | 
| 820 |  | 
| 821 | bool QCanSignalDescriptionPrivate::muxValueInRange( | 
| 822 |         const QVariant &value, const QCanSignalDescription::MultiplexValues &ranges) const | 
| 823 | { | 
| 824 |     // Use the current data format to convert QVariant values. | 
| 825 |     // Do we really need it for Float, Double and Ascii? | 
| 826 |     switch (format) { | 
| 827 |     case QtCanBus::DataFormat::SignedInteger: | 
| 828 |         return checkValue<qint64>(valueVar: value, ranges); | 
| 829 |     case QtCanBus::DataFormat::UnsignedInteger: | 
| 830 |         return checkValue<quint64>(valueVar: value, ranges); | 
| 831 |     case QtCanBus::DataFormat::Float: | 
| 832 |         return checkValue<float>(valueVar: value, ranges); | 
| 833 |     case QtCanBus::DataFormat::Double: | 
| 834 |         return checkValue<double>(valueVar: value, ranges); | 
| 835 |     case QtCanBus::DataFormat::AsciiString: | 
| 836 |         return checkValue<QByteArray>(valueVar: value, ranges); | 
| 837 |     } | 
| 838 |  | 
| 839 |     Q_UNREACHABLE_RETURN(false); | 
| 840 | } | 
| 841 |  | 
| 842 | QCanSignalDescriptionPrivate *QCanSignalDescriptionPrivate::get(const QCanSignalDescription &desc) | 
| 843 | { | 
| 844 |     return desc.d.data(); | 
| 845 | } | 
| 846 |  | 
| 847 | QT_END_NAMESPACE | 
| 848 |  |