1 | // Copyright (C) 2017 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 "qcanbusdevice.h" |
5 | #include "qcanbusdevice_p.h" |
6 | #include "qcanbusdeviceinfo_p.h" |
7 | |
8 | #include "qcanbusframe.h" |
9 | |
10 | #include <QtCore/qdebug.h> |
11 | #include <QtCore/qdatastream.h> |
12 | #include <QtCore/qeventloop.h> |
13 | #include <QtCore/qloggingcategory.h> |
14 | #include <QtCore/qscopedvaluerollback.h> |
15 | #include <QtCore/qtimer.h> |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | Q_LOGGING_CATEGORY(QT_CANBUS, "qt.canbus" ) |
20 | |
21 | /*! |
22 | \class QCanBusDevice |
23 | \inmodule QtSerialBus |
24 | \since 5.8 |
25 | |
26 | \brief The QCanBusDevice class is the interface class for CAN bus. |
27 | |
28 | QCanBusDevice communicates with a CAN plugin providing users with a convenient API. |
29 | The CAN plugin must be specified during the object creation. |
30 | */ |
31 | |
32 | /*! |
33 | \enum QCanBusDevice::CanBusError |
34 | This enum describes all the possible error conditions. |
35 | |
36 | \value NoError No errors have occurred. |
37 | \value ReadError An error occurred during a read operation. |
38 | \value WriteError An error occurred during a write operation. |
39 | \value ConnectionError An error occurred when attempting to open the plugin. |
40 | \value ConfigurationError An error occurred when attempting to set a configuration |
41 | parameter. |
42 | \value UnknownError An unknown error occurred. |
43 | \value OperationError An operation was attempted while the device was in |
44 | a state that did not permit it. This enum was introduced |
45 | in Qt 5.14. |
46 | \value TimeoutError An timeout occurred while waiting for frames written or |
47 | received. This enum was introduced in Qt 5.14. |
48 | */ |
49 | |
50 | /*! |
51 | \enum QCanBusDevice::CanBusDeviceState |
52 | This enum describes all possible device states. |
53 | |
54 | \value UnconnectedState The device is disconnected. |
55 | \value ConnectingState The device is being connected. |
56 | \value ConnectedState The device is connected to the CAN bus. |
57 | \value ClosingState The device is being closed. |
58 | */ |
59 | |
60 | /*! |
61 | \enum QCanBusDevice::ConfigurationKey |
62 | This enum describes the possible configuration options for |
63 | the CAN bus connection. |
64 | |
65 | \value RawFilterKey This configuration determines the type of CAN bus frames |
66 | that the current device accepts. The expected value |
67 | is \c QList<QCanBusDevice::Filter>. Passing an empty list clears |
68 | all previously set filters including default filters. For more details |
69 | see \l QCanBusDevice::Filter. |
70 | \value ErrorFilterKey This key defines the type of error that should be |
71 | forwarded via the current connection. The associated |
72 | value should be of type \l QCanBusFrame::FrameErrors. |
73 | \value LoopbackKey This key defines whether the CAN bus device should operate in loopback |
74 | mode. Loopback means, whenever a CAN frame is transmitted on the CAN |
75 | bus, a local echo of this frame is sent to all applications connected to |
76 | this CAN device. The expected value for this key is \c bool. |
77 | \value ReceiveOwnKey This key defines whether this CAN device receives its own send frames. |
78 | This can be used to check if the transmission was successful. |
79 | The expected value for this key is \c bool. |
80 | \value BitRateKey This key defines the CAN bitrate in bits per second. With CAN FD, |
81 | the payload can be transmitted at a higher data bitrate, |
82 | if \l QCanBusFrame::hasBitrateSwitch() is set. In this case, |
83 | \c QCanBusDevice::BitRateKey is only used for the CAN ID arbitration |
84 | phase. See also \c QCanBusDevice::DataBitRateKey |
85 | \value CanFdKey This key defines whether sending and receiving of CAN FD frames |
86 | should be enabled. The expected value for this key is \c bool. |
87 | \value DataBitRateKey This key defines the CAN FD payload bitrate in bits per second. |
88 | CAN FD allows to transmit the payload of frames with |
89 | \l QCanBusFrame::hasBitrateSwitch() flag at a higher data bitrate, |
90 | after the arbitration phase at the nominal bitrate is finished. |
91 | This enum value was introduced in Qt 5.9. |
92 | See also \c QCanBusDevice::BitRateKey |
93 | \value ProtocolKey This key allows to specify another protocol. For now, this |
94 | parameter can only be set and used in the SocketCAN plugin. |
95 | This enum value was introduced in Qt 5.14. |
96 | \value UserKey This key defines the range where custom keys start. Its most |
97 | common purpose is to permit platform-specific configuration |
98 | options. |
99 | |
100 | \sa configurationParameter() |
101 | */ |
102 | |
103 | /*! |
104 | \class QCanBusDevice::Filter |
105 | \inmodule QtSerialBus |
106 | \since 5.8 |
107 | |
108 | \brief The QCanBusDevice::Filter struct defines a filter for CAN bus frames. |
109 | |
110 | A list of QCanBusDevice::Filter instances is passed to |
111 | \l QCanBusDevice::setConfigurationParameter() to enable filtering. If a received CAN frame |
112 | matches at least one of the filters in the list, the QCanBusDevice will accept it. |
113 | |
114 | The example below demonstrates how to use the struct: |
115 | |
116 | \snippet snippetmain.cpp Filter Examples |
117 | */ |
118 | |
119 | /*! |
120 | \fn bool QCanBusDevice::Filter::operator==(const QCanBusDevice::Filter &a, const QCanBusDevice::Filter &b) |
121 | |
122 | Returns \c true, if the filter \a a is equal to the filter \a b, |
123 | otherwise returns \c false. |
124 | */ |
125 | |
126 | /*! |
127 | \fn bool QCanBusDevice::Filter::operator!=(const QCanBusDevice::Filter &a, const QCanBusDevice::Filter &b) |
128 | |
129 | Returns \c true, if the filter \a a is not equal to the filter \a b, |
130 | otherwise returns \c false. |
131 | */ |
132 | |
133 | /*! |
134 | \enum QCanBusDevice::Filter::FormatFilter |
135 | This enum describes the format pattern, which is used to filter incoming |
136 | CAN bus frames. |
137 | |
138 | \value MatchBaseFormat The CAN bus frame must use the base frame format |
139 | (11 bit identifier). |
140 | \value MatchExtendedFormat The CAN bus frame must use the extended frame format |
141 | (29 bit identifier). |
142 | \value MatchBaseAndExtendedFormat The CAN bus frame can have a base or an extended |
143 | frame format. |
144 | */ |
145 | |
146 | /*! |
147 | \variable QCanBusDevice::Filter::frameId |
148 | |
149 | \brief The frame id used to filter the incoming frames. |
150 | |
151 | The frameId is used in conjunction with \a frameIdMask. |
152 | The matching is successful if the following evaluates to \c true: |
153 | |
154 | \code |
155 | (receivedFrameId & frameIdMask) == (frameId & frameIdMask) |
156 | \endcode |
157 | |
158 | By default this field is set to \c 0x0. |
159 | |
160 | \sa frameIdMask |
161 | */ |
162 | |
163 | /*! |
164 | \variable QCanBusDevice::Filter::frameIdMask |
165 | |
166 | \brief The bit mask that is applied to the frame id of the filter and the received frame. |
167 | |
168 | The two frame ids are matching if the following evaluates to \c true: |
169 | |
170 | \code |
171 | (receivedFrameId & frameIdMask) == (frameId & frameIdMask) |
172 | \endcode |
173 | |
174 | By default this field is set to \c 0x0. |
175 | |
176 | \sa frameId |
177 | */ |
178 | |
179 | /*! |
180 | \variable QCanBusDevice::Filter::type |
181 | |
182 | \brief The type of the frame to be filtered. |
183 | |
184 | Any CAN bus frame type can be matched by setting this variable |
185 | to \l QCanBusFrame::InvalidFrame. The filter object is invalid if |
186 | type is equal to \l QCanBusFrame::UnknownFrame. |
187 | |
188 | By default this field is set to \l QCanBusFrame::InvalidFrame. |
189 | |
190 | \sa QCanBusFrame::FrameType |
191 | */ |
192 | |
193 | /*! |
194 | \variable QCanBusDevice::Filter::format |
195 | |
196 | \brief The frame format of the matching CAN bus frame. |
197 | |
198 | By default this field is set to \l QCanBusDevice::Filter::MatchBaseAndExtendedFormat. |
199 | */ |
200 | |
201 | /*! |
202 | \fn void QCanBusDevice::errorOccurred(CanBusError) |
203 | |
204 | This signal is emitted when an error occurs. |
205 | */ |
206 | |
207 | /*! |
208 | Constructs a serial bus device with the specified \a parent. |
209 | */ |
210 | QCanBusDevice::QCanBusDevice(QObject *parent) : |
211 | QObject(*new QCanBusDevicePrivate, parent) |
212 | { |
213 | } |
214 | |
215 | |
216 | /*! |
217 | Sets the human readable description of the last device error to |
218 | \a errorText. \a errorId categorizes the type of error. |
219 | |
220 | CAN bus implementations must use this function to update the device's |
221 | error state. |
222 | |
223 | \sa error(), errorOccurred(), clearError() |
224 | */ |
225 | void QCanBusDevice::setError(const QString &errorText, CanBusError errorId) |
226 | { |
227 | Q_D(QCanBusDevice); |
228 | |
229 | d->errorText = errorText; |
230 | d->lastError = errorId; |
231 | |
232 | emit errorOccurred(errorId); |
233 | } |
234 | |
235 | /*! |
236 | \since 5.14 |
237 | Clears the error id and the human readable description of the last |
238 | device error. |
239 | |
240 | CAN bus implementations must use this function to update the device's |
241 | error state. |
242 | |
243 | \sa error(), errorOccurred(), setError() |
244 | */ |
245 | void QCanBusDevice::clearError() |
246 | { |
247 | Q_D(QCanBusDevice); |
248 | |
249 | d->errorText.clear(); |
250 | d->lastError = NoError; |
251 | } |
252 | |
253 | /*! |
254 | Appends \a newFrames to the internal list of frames which can be |
255 | accessed using \l readFrame() and emits the \l framesReceived() |
256 | signal. |
257 | |
258 | Subclasses must call this function when they receive frames. |
259 | |
260 | */ |
261 | void QCanBusDevice::enqueueReceivedFrames(const QList<QCanBusFrame> &newFrames) |
262 | { |
263 | Q_D(QCanBusDevice); |
264 | |
265 | if (Q_UNLIKELY(newFrames.isEmpty())) |
266 | return; |
267 | |
268 | d->incomingFramesGuard.lock(); |
269 | d->incomingFrames.append(l: newFrames); |
270 | d->incomingFramesGuard.unlock(); |
271 | emit framesReceived(); |
272 | } |
273 | |
274 | /*! |
275 | Appends \a newFrame to the internal list of outgoing frames which |
276 | can be accessed by \l writeFrame(). |
277 | |
278 | Subclasses must call this function when they write a new frame. |
279 | */ |
280 | void QCanBusDevice::enqueueOutgoingFrame(const QCanBusFrame &newFrame) |
281 | { |
282 | Q_D(QCanBusDevice); |
283 | |
284 | d->outgoingFrames.append(t: newFrame); |
285 | } |
286 | |
287 | /*! |
288 | Returns the next \l QCanBusFrame from the internal list of outgoing frames; |
289 | otherwise returns an invalid QCanBusFrame. The returned frame is removed |
290 | from the internal list. |
291 | */ |
292 | QCanBusFrame QCanBusDevice::dequeueOutgoingFrame() |
293 | { |
294 | Q_D(QCanBusDevice); |
295 | |
296 | if (Q_UNLIKELY(d->outgoingFrames.isEmpty())) |
297 | return QCanBusFrame(QCanBusFrame::InvalidFrame); |
298 | return d->outgoingFrames.takeFirst(); |
299 | } |
300 | |
301 | /*! |
302 | Returns \c true if the internal list of outgoing frames is not |
303 | empty; otherwise returns \c false. |
304 | */ |
305 | bool QCanBusDevice::hasOutgoingFrames() const |
306 | { |
307 | Q_D(const QCanBusDevice); |
308 | |
309 | return !d->outgoingFrames.isEmpty(); |
310 | } |
311 | |
312 | /*! |
313 | Sets the configuration parameter \a key for the CAN bus connection |
314 | to \a value. The potential keys are represented by \l ConfigurationKey. |
315 | |
316 | A parameter can be unset by setting an invalid \l QVariant. |
317 | Unsetting a parameter implies that the configuration is reset to |
318 | its default setting. |
319 | |
320 | \note In most cases, configuration changes only take effect |
321 | after a reconnect. |
322 | |
323 | \sa configurationParameter() |
324 | */ |
325 | void QCanBusDevice::setConfigurationParameter(ConfigurationKey key, const QVariant &value) |
326 | { |
327 | Q_D(QCanBusDevice); |
328 | |
329 | for (int i = 0; i < d->configOptions.size(); i++) { |
330 | if (d->configOptions.at(i).first == key) { |
331 | if (value.isValid()) { |
332 | ConfigEntry entry = d->configOptions.at(i); |
333 | entry.second = value; |
334 | d->configOptions.replace(i, t: entry); |
335 | } else { |
336 | d->configOptions.remove(i); |
337 | } |
338 | return; |
339 | } |
340 | } |
341 | |
342 | if (!value.isValid()) |
343 | return; |
344 | |
345 | ConfigEntry newEntry(key, value); |
346 | d->configOptions.append(t: newEntry); |
347 | } |
348 | |
349 | /*! |
350 | Returns the current value assigned to the \l ConfigurationKey \a key; otherwise |
351 | an invalid \l QVariant. |
352 | |
353 | \sa setConfigurationParameter(), configurationKeys() |
354 | */ |
355 | QVariant QCanBusDevice::configurationParameter(ConfigurationKey key) const |
356 | { |
357 | Q_D(const QCanBusDevice); |
358 | |
359 | for (const ConfigEntry &e : d->configOptions) { |
360 | if (e.first == key) |
361 | return e.second; |
362 | } |
363 | |
364 | return QVariant(); |
365 | } |
366 | |
367 | /*! |
368 | Returns the list of keys used by the CAN bus connection. |
369 | |
370 | The meaning of the keys is equivalent to \l ConfigurationKey. |
371 | If a key is not explicitly mentioned, the platform's |
372 | default setting for the relevant key is used. |
373 | */ |
374 | QList<QCanBusDevice::ConfigurationKey> QCanBusDevice::configurationKeys() const |
375 | { |
376 | Q_D(const QCanBusDevice); |
377 | |
378 | QList<ConfigurationKey> result; |
379 | for (const ConfigEntry &e : d->configOptions) |
380 | result.append(t: e.first); |
381 | |
382 | return result; |
383 | } |
384 | |
385 | /*! |
386 | Returns the last error that has occurred. The error value is always set to last error that |
387 | occurred and it is never reset. |
388 | |
389 | \sa errorString() |
390 | */ |
391 | QCanBusDevice::CanBusError QCanBusDevice::error() const |
392 | { |
393 | return d_func()->lastError; |
394 | } |
395 | |
396 | /*! |
397 | Returns a human-readable description of the last device error that occurred. |
398 | |
399 | \sa error() |
400 | */ |
401 | QString QCanBusDevice::errorString() const |
402 | { |
403 | Q_D(const QCanBusDevice); |
404 | |
405 | if (d->lastError == QCanBusDevice::NoError) |
406 | return QString(); |
407 | |
408 | return d->errorText; |
409 | } |
410 | |
411 | /*! |
412 | Returns the number of available frames. If no frames are available, |
413 | this function returns 0. |
414 | |
415 | \sa clear(), readFrame(), readAllFrames() |
416 | */ |
417 | qint64 QCanBusDevice::framesAvailable() const |
418 | { |
419 | return d_func()->incomingFrames.size(); |
420 | } |
421 | |
422 | /*! |
423 | For buffered devices, this function returns the number of frames waiting to be written. |
424 | For unbuffered devices, this function always returns zero. |
425 | |
426 | \note There may be additional buffering in the CAN driver and CAN hardware layer. |
427 | Therefore, if this function returns zero, that does not mean all CAN frames are |
428 | already written to the CAN bus. |
429 | |
430 | \sa clear(), writeFrame() |
431 | */ |
432 | qint64 QCanBusDevice::framesToWrite() const |
433 | { |
434 | return d_func()->outgoingFrames.size(); |
435 | } |
436 | |
437 | /*! |
438 | \since 5.14 |
439 | |
440 | Performs a CAN controller reset to release the CAN controller from |
441 | bus off state, if possible. |
442 | |
443 | \note CAN controller resets disturb the running communication and |
444 | may take up to one second to complete. Only call this function to |
445 | recover from bus errors. |
446 | |
447 | \note This function may not be implemented in all CAN plugins. |
448 | Please refer to the plugins help pages for more information. |
449 | |
450 | \sa busStatus() |
451 | */ |
452 | void QCanBusDevice::resetController() |
453 | { |
454 | const char error[] = QT_TRANSLATE_NOOP("QCanBusDevice" , |
455 | "This CAN bus plugin does not support hardware controller reset." ); |
456 | qCWarning(QT_CANBUS, error); |
457 | setError(errorText: tr(s: error), errorId: QCanBusDevice::CanBusError::ConfigurationError); |
458 | } |
459 | |
460 | /*! |
461 | \since 5.14 |
462 | |
463 | Return true, if the CAN plugin supports requesting the CAN bus status. |
464 | |
465 | \sa busStatus() |
466 | */ |
467 | bool QCanBusDevice::hasBusStatus() const |
468 | { |
469 | return false; |
470 | } |
471 | |
472 | /*! |
473 | \since 5.14 |
474 | \enum QCanBusDevice::CanBusStatus |
475 | |
476 | This enum describes possible CAN bus status values. |
477 | |
478 | \value Unknown The CAN bus status is unknown |
479 | (e.g. not supported by the CAN plugin). |
480 | \value Good The CAN controller is fully operational |
481 | \value Warning The CAN controller is in warning status |
482 | \value Error The CAN controller is in error status |
483 | (no longer sending CAN frames) |
484 | \value BusOff The CAN controller is in bus off status |
485 | (disconnected from the CAN bus) |
486 | */ |
487 | |
488 | /*! |
489 | \since 5.14 |
490 | |
491 | Returns the current CAN bus status. If the status cannot be requested, |
492 | QCanBusDevice::UnknownStatus is returned. |
493 | |
494 | \note This function may not be implemented in all CAN plugins. |
495 | Please refer to the plugins help pages for more information. |
496 | The function hasBusStatus() can be used at runtime to check if |
497 | the used CAN plugin has support for requesting the CAN bus status. |
498 | |
499 | \sa hasBusStatus(), resetController() |
500 | */ |
501 | QCanBusDevice::CanBusStatus QCanBusDevice::busStatus() |
502 | { |
503 | return QCanBusDevice::CanBusStatus::Unknown; |
504 | } |
505 | |
506 | /*! |
507 | \since 5.12 |
508 | \enum QCanBusDevice::Direction |
509 | |
510 | This enum describes possible data transmission directions. |
511 | |
512 | \value Input Input direction. |
513 | \value Output Output direction. |
514 | \value AllDirections Both directions, input and output. |
515 | */ |
516 | |
517 | /*! |
518 | \since 5.12 |
519 | Clears the devices input or output buffers, depending on \a direction. |
520 | |
521 | This function only operates on QCanBusDevice buffers. Frames that are |
522 | already written to the CAN driver or CAN hardware layer, or that are |
523 | not yet read from these layers, are not cleared by this function. |
524 | |
525 | \note Clearing the output buffers is only possible for buffered devices. |
526 | |
527 | \sa framesAvailable(), readFrame(), framesToWrite(), writeFrame(), |
528 | */ |
529 | void QCanBusDevice::clear(QCanBusDevice::Directions direction) |
530 | { |
531 | Q_D(QCanBusDevice); |
532 | |
533 | if (Q_UNLIKELY(d->state != ConnectedState)) { |
534 | const QString error = tr(s: "Cannot clear buffers as device is not connected." ); |
535 | qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); |
536 | setError(errorText: error, errorId: CanBusError::OperationError); |
537 | return; |
538 | } |
539 | |
540 | clearError(); |
541 | |
542 | if (direction & Direction::Input) { |
543 | QMutexLocker locker(&d->incomingFramesGuard); |
544 | d->incomingFrames.clear(); |
545 | } |
546 | |
547 | if (direction & Direction::Output) |
548 | d->outgoingFrames.clear(); |
549 | } |
550 | |
551 | /*! |
552 | For buffered devices, this function waits until all buffered frames |
553 | have been written to the device and the \l framesWritten() signal has been emitted, |
554 | or until \a msecs milliseconds have passed. If \a msecs is -1, |
555 | this function will not time out. For unbuffered devices, it returns immediately with \c false |
556 | as \l writeFrame() does not require a write buffer. |
557 | |
558 | Returns \c true if the \l framesWritten() signal is emitted; |
559 | otherwise returns \c false (i.e. if the operation timed out, or if an error occurred). |
560 | |
561 | \note This function will start a local event loop. This may lead to scenarios whereby |
562 | other application slots may be called while the execution of this function scope is blocking. |
563 | To avoid problems, the signals for this class should not be connected to slots. |
564 | Similarly this function must never be called in response to the \l framesWritten() |
565 | or \l errorOccurred() signals. |
566 | |
567 | \sa waitForFramesReceived() |
568 | */ |
569 | bool QCanBusDevice::waitForFramesWritten(int msecs) |
570 | { |
571 | // do not enter this function recursively |
572 | if (Q_UNLIKELY(d_func()->waitForWrittenEntered)) { |
573 | qCWarning(QT_CANBUS, "QCanBusDevice::waitForFramesWritten() must not be called " |
574 | "recursively. Check that no slot containing waitForFramesReceived() " |
575 | "is called in response to framesWritten(qint64) or " |
576 | "errorOccurred(CanBusError) signals." ); |
577 | setError(errorText: tr(s: "QCanBusDevice::waitForFramesWritten() must not be called recursively." ), |
578 | errorId: CanBusError::OperationError); |
579 | return false; |
580 | } |
581 | |
582 | if (Q_UNLIKELY(d_func()->state != ConnectedState)) { |
583 | const QString error = tr(s: "Cannot wait for frames written as device is not connected." ); |
584 | qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); |
585 | setError(errorText: error, errorId: CanBusError::OperationError); |
586 | return false; |
587 | } |
588 | |
589 | if (!framesToWrite()) |
590 | return false; // nothing pending, nothing to wait upon |
591 | |
592 | QScopedValueRollback<bool> guard(d_func()->waitForWrittenEntered); |
593 | d_func()->waitForWrittenEntered = true; |
594 | |
595 | enum { Written = 0, Error, Timeout }; |
596 | QEventLoop loop; |
597 | connect(sender: this, signal: &QCanBusDevice::framesWritten, context: &loop, slot: [&]() { loop.exit(returnCode: Written); }); |
598 | connect(sender: this, signal: &QCanBusDevice::errorOccurred, context: &loop, slot: [&]() { loop.exit(returnCode: Error); }); |
599 | if (msecs >= 0) |
600 | QTimer::singleShot(interval: msecs, receiver: &loop, slot: [&]() { loop.exit(returnCode: Timeout); }); |
601 | |
602 | int result = Written; |
603 | while (framesToWrite() > 0) { |
604 | // wait till all written or time out |
605 | result = loop.exec(flags: QEventLoop::ExcludeUserInputEvents); |
606 | if (Q_UNLIKELY(result == Timeout)) { |
607 | const QString error = tr(s: "Timeout (%1 ms) during wait for frames written." ).arg(a: msecs); |
608 | setError(errorText: error, errorId: CanBusError::TimeoutError); |
609 | qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); |
610 | } |
611 | |
612 | if (result > Written) |
613 | return false; |
614 | } |
615 | |
616 | clearError(); |
617 | return true; |
618 | } |
619 | |
620 | /*! |
621 | Blocks until new frames are available for reading and the \l framesReceived() |
622 | signal has been emitted, or until \a msecs milliseconds have passed. If |
623 | \a msecs is \c -1, this function will not time out. |
624 | |
625 | Returns \c true if new frames are available for reading and the \l framesReceived() |
626 | signal is emitted; otherwise returns \c false (if the operation timed out |
627 | or if an error occurred). |
628 | |
629 | \note This function will start a local event loop. This may lead to scenarios whereby |
630 | other application slots may be called while the execution of this function scope is blocking. |
631 | To avoid problems, the signals for this class should not be connected to slots. |
632 | Similarly this function must never be called in response to the \l framesReceived() |
633 | or \l errorOccurred() signals. |
634 | |
635 | \sa waitForFramesWritten() |
636 | */ |
637 | bool QCanBusDevice::waitForFramesReceived(int msecs) |
638 | { |
639 | // do not enter this function recursively |
640 | if (Q_UNLIKELY(d_func()->waitForReceivedEntered)) { |
641 | qCWarning(QT_CANBUS, "QCanBusDevice::waitForFramesReceived() must not be called " |
642 | "recursively. Check that no slot containing waitForFramesReceived() " |
643 | "is called in response to framesReceived() or " |
644 | "errorOccurred(CanBusError) signals." ); |
645 | setError(errorText: tr(s: "QCanBusDevice::waitForFramesReceived() must not be called recursively." ), |
646 | errorId: CanBusError::OperationError); |
647 | return false; |
648 | } |
649 | |
650 | if (Q_UNLIKELY(d_func()->state != ConnectedState)) { |
651 | const QString error = tr(s: "Cannot wait for frames received as device is not connected." ); |
652 | qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); |
653 | setError(errorText: error, errorId: CanBusError::OperationError); |
654 | return false; |
655 | } |
656 | |
657 | QScopedValueRollback<bool> guard(d_func()->waitForReceivedEntered); |
658 | d_func()->waitForReceivedEntered = true; |
659 | |
660 | enum { Received = 0, Error, Timeout }; |
661 | QEventLoop loop; |
662 | connect(sender: this, signal: &QCanBusDevice::framesReceived, context: &loop, slot: [&]() { loop.exit(returnCode: Received); }); |
663 | connect(sender: this, signal: &QCanBusDevice::errorOccurred, context: &loop, slot: [&]() { loop.exit(returnCode: Error); }); |
664 | if (msecs >= 0) |
665 | QTimer::singleShot(interval: msecs, receiver: &loop, slot: [&]() { loop.exit(returnCode: Timeout); }); |
666 | |
667 | int result = loop.exec(flags: QEventLoop::ExcludeUserInputEvents); |
668 | |
669 | if (Q_UNLIKELY(result == Timeout)) { |
670 | const QString error = tr(s: "Timeout (%1 ms) during wait for frames received." ).arg(a: msecs); |
671 | setError(errorText: error, errorId: CanBusError::TimeoutError); |
672 | qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); |
673 | } |
674 | |
675 | if (result == Received) |
676 | clearError(); |
677 | return result == Received; |
678 | } |
679 | |
680 | /*! |
681 | \fn bool QCanBusDevice::open() |
682 | |
683 | This function is called by connectDevice(). Subclasses must provide |
684 | an implementation which returns \c true if the CAN bus connection |
685 | could be established; otherwise \c false. The QCanBusDevice implementation |
686 | ensures upon entry of this function that the device's \l state() is set |
687 | to \l QCanBusDevice::ConnectingState already. |
688 | |
689 | The implementation must ensure that upon success the instance's \l state() |
690 | is set to \l QCanBusDevice::ConnectedState; otherwise |
691 | \l QCanBusDevice::UnconnectedState. \l setState() must be used to set the new |
692 | device state. |
693 | |
694 | The custom implementation is responsible for opening the socket, instanciation |
695 | of a potentially required \l QSocketNotifier and the application of custom and default |
696 | \l QCanBusDevice::configurationParameter(). |
697 | |
698 | \sa connectDevice() |
699 | */ |
700 | |
701 | /*! |
702 | \fn void QCanBusDevice::close() |
703 | |
704 | This function is responsible for closing the CAN bus connection. |
705 | The implementation must ensure that the instance's |
706 | \l state() is set to \l QCanBusDevice::UnconnectedState. |
707 | |
708 | This function's most important task is to close the socket to the CAN device |
709 | and to call \l QCanBusDevice::setState(). |
710 | |
711 | \sa disconnectDevice() |
712 | */ |
713 | |
714 | /*! |
715 | \fn void QCanBusDevice::framesReceived() |
716 | |
717 | This signal is emitted when one or more frames have been received. |
718 | The frames should be read using \l readFrame() and \l framesAvailable(). |
719 | */ |
720 | |
721 | /*! |
722 | Returns the next \l QCanBusFrame from the queue; otherwise returns |
723 | an empty QCanBusFrame. The returned frame is removed from the queue. |
724 | |
725 | The queue operates according to the FIFO principle. |
726 | |
727 | \sa clear(), framesAvailable(), readAllFrames() |
728 | */ |
729 | QCanBusFrame QCanBusDevice::readFrame() |
730 | { |
731 | Q_D(QCanBusDevice); |
732 | |
733 | if (Q_UNLIKELY(d->state != ConnectedState)) { |
734 | const QString error = tr(s: "Cannot read frame as device is not connected." ); |
735 | qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); |
736 | setError(errorText: error, errorId: CanBusError::OperationError); |
737 | return QCanBusFrame(QCanBusFrame::InvalidFrame); |
738 | } |
739 | |
740 | clearError(); |
741 | |
742 | QMutexLocker locker(&d->incomingFramesGuard); |
743 | |
744 | if (Q_UNLIKELY(d->incomingFrames.isEmpty())) |
745 | return QCanBusFrame(QCanBusFrame::InvalidFrame); |
746 | |
747 | return d->incomingFrames.takeFirst(); |
748 | } |
749 | |
750 | /*! |
751 | \since 5.12 |
752 | Returns all \l{QCanBusFrame}s from the queue; otherwise returns |
753 | an empty QList. The returned frames are removed from the queue. |
754 | |
755 | The queue operates according to the FIFO principle. |
756 | |
757 | \sa clear(), framesAvailable(), readFrame() |
758 | */ |
759 | QList<QCanBusFrame> QCanBusDevice::readAllFrames() |
760 | { |
761 | Q_D(QCanBusDevice); |
762 | |
763 | if (Q_UNLIKELY(d->state != ConnectedState)) { |
764 | const QString error = tr(s: "Cannot read frame as device is not connected." ); |
765 | qCWarning(QT_CANBUS, "%ls" , qUtf16Printable(error)); |
766 | setError(errorText: error, errorId: CanBusError::OperationError); |
767 | return QList<QCanBusFrame>(); |
768 | } |
769 | |
770 | clearError(); |
771 | |
772 | QMutexLocker locker(&d->incomingFramesGuard); |
773 | |
774 | QList<QCanBusFrame> result; |
775 | result.swap(other&: d->incomingFrames); |
776 | return result; |
777 | } |
778 | |
779 | /*! |
780 | \fn void QCanBusDevice::framesWritten(qint64 framesCount) |
781 | |
782 | This signal is emitted every time a payload of frames has been |
783 | written to the CAN bus. The \a framesCount argument is set to |
784 | the number of frames that were written in this payload. |
785 | */ |
786 | |
787 | /*! |
788 | \fn bool QCanBusDevice::writeFrame(const QCanBusFrame &frame) |
789 | |
790 | Writes \a frame to the CAN bus and returns \c true on success; |
791 | otherwise \c false. |
792 | |
793 | On some platforms, the frame may be put into a queue and the return |
794 | value may only indicate a successful insertion into the queue. |
795 | The actual frame will be send later on. Therefore the \l framesWritten() |
796 | signal is the final confirmation that the frame has been handed off to |
797 | the transport layer. If an error occurs the \l errorOccurred() is emitted. |
798 | |
799 | As per CAN bus specification, frames of type |
800 | \l {QCanBusFrame::RemoteRequestFrame} {remote transfer request (RTR)} |
801 | do not have a payload, but a length from 0 to 8 (including). This length |
802 | indicates the expected response payload length from the remote party. |
803 | Therefore when sending a RTR frame using this function it may still |
804 | be required to set an arbitrary payload on \a frame. The length of |
805 | the arbitrary payload is what is set as size expectation for the RTR frame. |
806 | |
807 | \sa QCanBusFrame::setPayload() |
808 | */ |
809 | |
810 | /*! |
811 | \fn QString QCanBusDevice::interpretErrorFrame(const QCanBusFrame &frame) |
812 | |
813 | Interprets \a frame as error frame and returns a human readable |
814 | description of the error. |
815 | |
816 | If \a frame is not an error frame, the returned string is empty. |
817 | */ |
818 | |
819 | /*! |
820 | Connects the device to the CAN bus. Returns \c true on success; |
821 | otherwise \c false. |
822 | |
823 | This function calls \l open() as part of its implementation. |
824 | |
825 | \sa disconnectDevice() |
826 | */ |
827 | bool QCanBusDevice::connectDevice() |
828 | { |
829 | Q_D(QCanBusDevice); |
830 | |
831 | if (Q_UNLIKELY(d->state != QCanBusDevice::UnconnectedState)) { |
832 | const char error[] = QT_TRANSLATE_NOOP("QCanBusDevice" , |
833 | "Can not connect an already connected device." ); |
834 | qCWarning(QT_CANBUS, error); |
835 | setError(errorText: tr(s: error), errorId: QCanBusDevice::ConnectionError); |
836 | return false; |
837 | } |
838 | |
839 | setState(ConnectingState); |
840 | |
841 | if (!open()) { |
842 | setState(UnconnectedState); |
843 | return false; |
844 | } |
845 | |
846 | clearError(); |
847 | |
848 | //Connected is set by backend -> might be delayed by event loop |
849 | return true; |
850 | } |
851 | |
852 | |
853 | /*! |
854 | Disconnects the device from the CAN bus. |
855 | |
856 | This function calls \l close() as part of its implementation. |
857 | |
858 | \note This function should only be called, if connectDevice() |
859 | returned \c true. |
860 | |
861 | \sa connectDevice() |
862 | */ |
863 | void QCanBusDevice::disconnectDevice() |
864 | { |
865 | Q_D(QCanBusDevice); |
866 | |
867 | if (Q_UNLIKELY(d->state == QCanBusDevice::UnconnectedState |
868 | || d->state == QCanBusDevice::ClosingState)) { |
869 | qCWarning(QT_CANBUS, "Can not disconnect an unconnected device." ); |
870 | return; |
871 | } |
872 | |
873 | setState(QCanBusDevice::ClosingState); |
874 | |
875 | //Unconnected is set by backend -> might be delayed by event loop |
876 | close(); |
877 | } |
878 | |
879 | /*! |
880 | \fn void QCanBusDevice::stateChanged(QCanBusDevice::CanBusDeviceState state) |
881 | |
882 | This signal is emitted every time the state of the device changes. |
883 | The new state is represented by \a state. |
884 | |
885 | \sa setState(), state() |
886 | */ |
887 | |
888 | /*! |
889 | Returns the current state of the device. |
890 | |
891 | \sa setState(), stateChanged() |
892 | */ |
893 | QCanBusDevice::CanBusDeviceState QCanBusDevice::state() const |
894 | { |
895 | return d_func()->state; |
896 | } |
897 | |
898 | /*! |
899 | Sets the state of the device to \a newState. CAN bus implementations |
900 | must use this function to update the device state. |
901 | */ |
902 | void QCanBusDevice::setState(QCanBusDevice::CanBusDeviceState newState) |
903 | { |
904 | Q_D(QCanBusDevice); |
905 | |
906 | if (newState == d->state) |
907 | return; |
908 | |
909 | d->state = newState; |
910 | emit stateChanged(state: newState); |
911 | } |
912 | |
913 | /*! |
914 | * \since 6.2 |
915 | * Returns a QCanBusDeviceInfo created from the given parameters \a plugin, |
916 | * \a name, \a isVirtual, and \a isFlexibleDataRateCapable. |
917 | * \internal |
918 | */ |
919 | QCanBusDeviceInfo QCanBusDevice::createDeviceInfo(const QString &plugin, const QString &name, |
920 | bool isVirtual, |
921 | bool isFlexibleDataRateCapable) |
922 | { |
923 | return createDeviceInfo(plugin, name, serialNumber: QString(), description: QString(), alias: QString(), |
924 | channel: 0, isVirtual, isFlexibleDataRateCapable); |
925 | } |
926 | |
927 | /*! |
928 | \since 6.2 |
929 | Returns a QCanBusDeviceInfo created from the given parameters \a plugin, |
930 | \a name, \a serialNumber, \a description, \a alias, \a channel, \a isVirtual, |
931 | and \a isFlexibleDataRateCapable. |
932 | \internal |
933 | */ |
934 | QCanBusDeviceInfo QCanBusDevice::createDeviceInfo(const QString &plugin, |
935 | const QString &name, |
936 | const QString &serialNumber, |
937 | const QString &description, |
938 | const QString &alias, |
939 | int channel, |
940 | bool isVirtual, |
941 | bool isFlexibleDataRateCapable) |
942 | { |
943 | std::unique_ptr<QCanBusDeviceInfoPrivate> info(new QCanBusDeviceInfoPrivate); |
944 | info->plugin = plugin; |
945 | info->name = name; |
946 | info->serialNumber = serialNumber; |
947 | info->description = description; |
948 | info->alias = alias; |
949 | info->channel = channel; |
950 | info->hasFlexibleDataRate = isFlexibleDataRateCapable; |
951 | info->isVirtual = isVirtual; |
952 | return QCanBusDeviceInfo(*info.release()); |
953 | } |
954 | |
955 | /*! |
956 | \since 6.2 |
957 | |
958 | Returns a QCanBusDeviceInfo for the current QCanBusDevice. If the function |
959 | is not implemented by a sub-class of QCanBusDevice, a default constructed |
960 | object is returned. |
961 | */ |
962 | QCanBusDeviceInfo QCanBusDevice::deviceInfo() const |
963 | { |
964 | return QCanBusDeviceInfo(*(new QCanBusDeviceInfoPrivate)); |
965 | } |
966 | |
967 | QT_END_NAMESPACE |
968 | |