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