| 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 "qcanmessagedescription.h" | 
| 5 | #include "qcanmessagedescription_p.h" | 
| 6 | #include "qcansignaldescription.h" | 
| 7 |  | 
| 8 | #include <QtCore/QHash> | 
| 9 | #include <QtCore/QSharedData> | 
| 10 |  | 
| 11 | QT_BEGIN_NAMESPACE | 
| 12 |  | 
| 13 | /*! | 
| 14 |     \class QCanMessageDescription | 
| 15 |     \inmodule QtSerialBus | 
| 16 |     \since 6.5 | 
| 17 |     \preliminary | 
| 18 |  | 
| 19 |     \brief The QCanMessageDescription class describes the rules to process a CAN | 
| 20 |     message and represent it in an application-defined format. | 
| 21 |  | 
| 22 |     A CAN message is basically a \l QCanBusFrame. The description of a CAN | 
| 23 |     message includes the following: | 
| 24 |     \list | 
| 25 |         \li Message ID. | 
| 26 |         \li Message name. | 
| 27 |         \li Message length in bytes. | 
| 28 |         \li Source of the message (transmitter). | 
| 29 |         \li Description of signals in the message. | 
| 30 |     \endlist | 
| 31 |  | 
| 32 |     The QCanMessageDescription class provides methods to control all those | 
| 33 |     parameters. | 
| 34 |  | 
| 35 |     \section2 Message ID | 
| 36 |     The message ID is a unique identifier, which is used to select the proper | 
| 37 |     message description when decoding the incoming \l QCanBusFrame or encoding | 
| 38 |     a \l QCanBusFrame based on the provided data. | 
| 39 |  | 
| 40 |     See \l QCanUniqueIdDescription documentation for more details on the unique | 
| 41 |     identifier description. | 
| 42 |  | 
| 43 |     \section2 Signal Description | 
| 44 |     The signal description is represented by the \l QCanSignalDescription | 
| 45 |     class. The QCanMessageDescription class only provides a list of signals that | 
| 46 |     belong to the message. | 
| 47 |  | 
| 48 |     \sa QCanSignalDescription, QCanUniqueIdDescription | 
| 49 | */ | 
| 50 |  | 
| 51 | /*! | 
| 52 |     Creates an empty message description. | 
| 53 | */ | 
| 54 | QCanMessageDescription::QCanMessageDescription() : d(new QCanMessageDescriptionPrivate) | 
| 55 | { | 
| 56 | } | 
| 57 |  | 
| 58 | /*! | 
| 59 |     Creates a message description with the values copied from \a other. | 
| 60 | */ | 
| 61 | QCanMessageDescription::QCanMessageDescription(const QCanMessageDescription &other) : d(other.d) | 
| 62 | { | 
| 63 | } | 
| 64 |  | 
| 65 | /*! | 
| 66 |     \fn QCanMessageDescription::QCanMessageDescription(QCanMessageDescription &&other) noexcept | 
| 67 |  | 
| 68 |     Creates a message description by moving from \a other. | 
| 69 |  | 
| 70 |     \note The moved-from QCanMessageDescription object can only be destroyed or | 
| 71 |     assigned to. The effect of calling other functions than the destructor or | 
| 72 |     one of the assignment operators is undefined. | 
| 73 | */ | 
| 74 |  | 
| 75 | /*! | 
| 76 |     \fn QCanMessageDescription::~QCanMessageDescription() | 
| 77 |  | 
| 78 |     Destroys this message description. | 
| 79 | */ | 
| 80 |  | 
| 81 | QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QCanMessageDescriptionPrivate) | 
| 82 |  | 
| 83 | /*! | 
| 84 |     Assigns the values from \a other to this message description. | 
| 85 | */ | 
| 86 | QCanMessageDescription &QCanMessageDescription::operator=(const QCanMessageDescription &other) | 
| 87 | { | 
| 88 |     d = other.d; | 
| 89 |     return *this; | 
| 90 | } | 
| 91 |  | 
| 92 | /*! | 
| 93 |     \fn QCanMessageDescription &QCanMessageDescription::operator=(QCanMessageDescription &&other) noexcept | 
| 94 |  | 
| 95 |     Move-assigns the values from \a other to this message description. | 
| 96 |  | 
| 97 |     \note The moved-from QCanMessageDescription object can only be destroyed or | 
| 98 |     assigned to. The effect of calling other functions than the destructor or | 
| 99 |     one of the assignment operators is undefined. | 
| 100 | */ | 
| 101 |  | 
| 102 | /*! | 
| 103 |     Returns \c true when the message description is valid and \c false | 
| 104 |     otherwise. | 
| 105 |  | 
| 106 |     A valid message description \e must have at least one signal description. | 
| 107 |     All signal descriptions \e must be valid as well. | 
| 108 |  | 
| 109 |     \sa signalDescriptions(), QCanSignalDescription::isValid() | 
| 110 | */ | 
| 111 | bool QCanMessageDescription::isValid() const | 
| 112 | { | 
| 113 |     if (d->messageSignals.isEmpty()) | 
| 114 |         return false; | 
| 115 |  | 
| 116 |     for (const auto &sigDesc : d->messageSignals) { | 
| 117 |         if (!sigDesc.isValid()) | 
| 118 |             return false; | 
| 119 |     } | 
| 120 |  | 
| 121 |     return true; | 
| 122 | } | 
| 123 |  | 
| 124 | /*! | 
| 125 |     Returns the unique identifier of the CAN message. | 
| 126 |  | 
| 127 |     See the \l {Message ID} section for more information about the unique | 
| 128 |     identifier. | 
| 129 |  | 
| 130 |     \sa setUniqueId() | 
| 131 | */ | 
| 132 | QtCanBus::UniqueId QCanMessageDescription::uniqueId() const | 
| 133 | { | 
| 134 |     return d->id; | 
| 135 | } | 
| 136 |  | 
| 137 | /*! | 
| 138 |     Sets the unique identifier of the CAN message to \a id. | 
| 139 |  | 
| 140 |     See the \l {Message ID} section for more information about the unique | 
| 141 |     identifier. | 
| 142 |  | 
| 143 |     \sa uniqueId() | 
| 144 | */ | 
| 145 | void QCanMessageDescription::setUniqueId(QtCanBus::UniqueId id) | 
| 146 | { | 
| 147 |     d.detach(); | 
| 148 |     d->id = id; | 
| 149 | } | 
| 150 |  | 
| 151 | /*! | 
| 152 |     Returns the name of the CAN message. | 
| 153 |  | 
| 154 | //! [qcanmessagedesc-aux-parameter] | 
| 155 |     This parameter is introduced only for extra description. It's not used | 
| 156 |     during message encoding or decoding. | 
| 157 | //! [qcanmessagedesc-aux-parameter] | 
| 158 |  | 
| 159 |     \sa setName() | 
| 160 | */ | 
| 161 | QString QCanMessageDescription::name() const | 
| 162 | { | 
| 163 |     return d->name; | 
| 164 | } | 
| 165 |  | 
| 166 | /*! | 
| 167 |     Sets the name of the CAN message to \a name. | 
| 168 |  | 
| 169 |     \include qcanmessagedescription.cpp qcanmessagedesc-aux-parameter | 
| 170 |  | 
| 171 |     \sa name() | 
| 172 | */ | 
| 173 | void QCanMessageDescription::setName(const QString &name) | 
| 174 | { | 
| 175 |     d.detach(); | 
| 176 |     d->name = name; | 
| 177 | } | 
| 178 |  | 
| 179 | /*! | 
| 180 |     Returns the size in bytes of the CAN message. | 
| 181 |  | 
| 182 |     \sa setSize() | 
| 183 | */ | 
| 184 | quint8 QCanMessageDescription::size() const | 
| 185 | { | 
| 186 |     return d->size; | 
| 187 | } | 
| 188 |  | 
| 189 | /*! | 
| 190 |     Sets the size in bytes of the CAN message to \a size. | 
| 191 |  | 
| 192 |     \sa size() | 
| 193 | */ | 
| 194 | void QCanMessageDescription::setSize(quint8 size) | 
| 195 | { | 
| 196 |     d.detach(); | 
| 197 |     d->size = size; | 
| 198 | } | 
| 199 |  | 
| 200 | /*! | 
| 201 |     Returns the transmitter node of the message. | 
| 202 |  | 
| 203 |     \include qcanmessagedescription.cpp qcanmessagedesc-aux-parameter | 
| 204 |  | 
| 205 |     \sa setTransmitter() | 
| 206 | */ | 
| 207 | QString QCanMessageDescription::transmitter() const | 
| 208 | { | 
| 209 |     return d->transmitter; | 
| 210 | } | 
| 211 |  | 
| 212 | /*! | 
| 213 |     Sets the transmitter node of the message to \a transmitter. | 
| 214 |  | 
| 215 |     \include qcanmessagedescription.cpp qcanmessagedesc-aux-parameter | 
| 216 |  | 
| 217 |     \sa transmitter() | 
| 218 | */ | 
| 219 | void QCanMessageDescription::setTransmitter(const QString &transmitter) | 
| 220 | { | 
| 221 |     d.detach(); | 
| 222 |     d->transmitter = transmitter; | 
| 223 | } | 
| 224 |  | 
| 225 | /*! | 
| 226 |     Returns the comment for the message. | 
| 227 |  | 
| 228 |     \include qcanmessagedescription.cpp qcanmessagedesc-aux-parameter | 
| 229 |  | 
| 230 |     \sa setComment() | 
| 231 | */ | 
| 232 | QString QCanMessageDescription::() const | 
| 233 | { | 
| 234 |     return d->comment; | 
| 235 | } | 
| 236 |  | 
| 237 | /*! | 
| 238 |     Sets the comment for the message to \a text. | 
| 239 |  | 
| 240 |     \include qcanmessagedescription.cpp qcanmessagedesc-aux-parameter | 
| 241 |  | 
| 242 |     \sa comment() | 
| 243 | */ | 
| 244 | void QCanMessageDescription::(const QString &text) | 
| 245 | { | 
| 246 |     d.detach(); | 
| 247 |     d->comment = text; | 
| 248 | } | 
| 249 |  | 
| 250 | /*! | 
| 251 |     Returns the list of signal descriptions that belong to this message | 
| 252 |     description. | 
| 253 |  | 
| 254 |     \sa signalDescriptionForName(), addSignalDescription(), | 
| 255 |     setSignalDescriptions(), clearSignalDescriptions() | 
| 256 | */ | 
| 257 | QList<QCanSignalDescription> QCanMessageDescription::signalDescriptions() const | 
| 258 | { | 
| 259 |     return QList<QCanSignalDescription>(d->messageSignals.cbegin(), d->messageSignals.cend()); | 
| 260 | } | 
| 261 |  | 
| 262 | /*! | 
| 263 |     Returns the signal description of a signal with the name \a name. | 
| 264 |  | 
| 265 |     If the message description does not have such signal description, a | 
| 266 |     default-constructed \l QCanSignalDescription object is returned. | 
| 267 |  | 
| 268 |     \sa signalDescriptions(), addSignalDescription(), setSignalDescriptions(), | 
| 269 |     clearSignalDescriptions() | 
| 270 | */ | 
| 271 | QCanSignalDescription QCanMessageDescription::signalDescriptionForName(const QString &name) const | 
| 272 | { | 
| 273 |     return d->messageSignals.value(key: name); | 
| 274 | } | 
| 275 |  | 
| 276 | /*! | 
| 277 |     Clears all the signal descriptions of this message. | 
| 278 |  | 
| 279 |     \sa signalDescriptions(), signalDescriptionForName(), | 
| 280 |     addSignalDescription(), setSignalDescriptions() | 
| 281 | */ | 
| 282 | void QCanMessageDescription::clearSignalDescriptions() | 
| 283 | { | 
| 284 |     d.detach(); | 
| 285 |     d->messageSignals.clear(); | 
| 286 | } | 
| 287 |  | 
| 288 | /*! | 
| 289 |     Adds a new signal description \a description to this message description. | 
| 290 |  | 
| 291 |     If the message description already has a signal description for a signal | 
| 292 |     with the same name, it is overwritten. | 
| 293 |  | 
| 294 |     \sa signalDescriptions(), signalDescriptionForName(), | 
| 295 |     setSignalDescriptions(), clearSignalDescriptions() | 
| 296 | */ | 
| 297 | void QCanMessageDescription::addSignalDescription(const QCanSignalDescription &description) | 
| 298 | { | 
| 299 |     d.detach(); | 
| 300 |     d->messageSignals.insert(key: description.name(), value: description); | 
| 301 | } | 
| 302 |  | 
| 303 | /*! | 
| 304 |     Sets the descriptions of the signals belonging to this message description | 
| 305 |     to \a descriptions. | 
| 306 |  | 
| 307 |     \note Message description \e must have signal descriptions with unique | 
| 308 |     signal names, so if the \a descriptions list contains entries with | 
| 309 |     duplicated names, only the last entry will be added. | 
| 310 |  | 
| 311 |     \sa signalDescriptions(), signalDescriptionForName(), | 
| 312 |     addSignalDescription(), clearSignalDescriptions() | 
| 313 | */ | 
| 314 | void QCanMessageDescription::setSignalDescriptions(const QList<QCanSignalDescription> &descriptions) | 
| 315 | { | 
| 316 |     d.detach(); | 
| 317 |     d->messageSignals.clear(); | 
| 318 |     d->messageSignals.reserve(size: descriptions.size()); | 
| 319 |     for (const auto &desc : descriptions) | 
| 320 |         d->messageSignals.insert(key: desc.name(), value: desc); | 
| 321 | } | 
| 322 |  | 
| 323 | #ifndef QT_NO_DEBUG_STREAM | 
| 324 | QDebug QCanMessageDescription::debugStreaming(QDebug dbg, const QCanMessageDescription &msg) | 
| 325 | { | 
| 326 |     QDebugStateSaver saver(dbg); | 
| 327 |     dbg.nospace() << "QCanMessageDescription("  << msg.name() << ", ID = "  << msg.uniqueId() | 
| 328 |                   << ", Size = "  << msg.size(); | 
| 329 |     if (!msg.transmitter().isEmpty()) | 
| 330 |         dbg << ", Transmitter = "  << msg.transmitter(); | 
| 331 |     if (!msg.comment().isEmpty()) | 
| 332 |         dbg << ", Comment = "  << msg.comment(); | 
| 333 |     const auto msgSignals = msg.signalDescriptions(); | 
| 334 |     if (!msgSignals.isEmpty()) { | 
| 335 |         dbg << ", Signals: {" ; | 
| 336 |         bool first = true; | 
| 337 |         for (const auto &sig : msgSignals) { | 
| 338 |             if (!first) | 
| 339 |                 dbg << ", " ; | 
| 340 |             dbg << sig; | 
| 341 |             first = false; | 
| 342 |         } | 
| 343 |         dbg << "}" ; | 
| 344 |     } | 
| 345 |     dbg << ")" ; | 
| 346 |     return dbg; | 
| 347 | } | 
| 348 | #endif // QT_NO_DEBUG_STREAM | 
| 349 |  | 
| 350 | QCanMessageDescriptionPrivate *QCanMessageDescriptionPrivate::get(const QCanMessageDescription &desc) | 
| 351 | { | 
| 352 |     return desc.d.data(); | 
| 353 | } | 
| 354 |  | 
| 355 | QT_END_NAMESPACE | 
| 356 |  |