diff options
Diffstat (limited to 'src/serialbus/qcanbusdevice.cpp')
-rw-r--r-- | src/serialbus/qcanbusdevice.cpp | 246 |
1 files changed, 220 insertions, 26 deletions
diff --git a/src/serialbus/qcanbusdevice.cpp b/src/serialbus/qcanbusdevice.cpp index 6723f77..222f19c 100644 --- a/src/serialbus/qcanbusdevice.cpp +++ b/src/serialbus/qcanbusdevice.cpp @@ -73,6 +73,11 @@ Q_LOGGING_CATEGORY(QT_CANBUS, "qt.canbus") \value ConfigurationError An error occurred when attempting to set a configuration parameter. \value UnknownError An unknown error occurred. + \value OperationError An operation was attempted while the device was in + a state that did not permit it. This enum was introduced + in Qt 5.14. + \value TimeoutError An timeout occurred while waiting for frames written or + received. This enum was introduced in Qt 5.14. */ /*! @@ -118,6 +123,9 @@ Q_LOGGING_CATEGORY(QT_CANBUS, "qt.canbus") after the arbitration phase at the nominal bitrate is finished. This enum value was introduced in Qt 5.9. See also \c QCanBusDevice::BitRateKey + \value ProtocolKey This key allows to specify another protocol. For now, this + parameter can only be set and used in the SocketCAN plugin. + This enum value was introduced in Qt 5.14. \value UserKey This key defines the range where custom keys start. Its most common purpose is to permit platform-specific configuration options. @@ -142,6 +150,22 @@ Q_LOGGING_CATEGORY(QT_CANBUS, "qt.canbus") */ /*! + \fn bool operator==(const QCanBusDevice::Filter &a, const QCanBusDevice::Filter &b) + \relates QCanBusDevice::Filter + + Returns true, if the filter \a a is equal to the filter \a b, + otherwise returns false. +*/ + +/*! + \fn bool operator!=(const QCanBusDevice::Filter &a, const QCanBusDevice::Filter &b) + \relates QCanBusDevice::Filter + + Returns true, if the filter \a a is not equal to the filter \a b, + otherwise returns false. +*/ + +/*! \enum QCanBusDevice::Filter::FormatFilter This enum describes the format pattern, which is used to filter incoming CAN bus frames. @@ -231,7 +255,7 @@ QCanBusDevice::QCanBusDevice(QObject *parent) : CAN bus implementations must use this function to update the device's error state. - \sa error(), errorOccurred() + \sa error(), errorOccurred(), clearError() */ void QCanBusDevice::setError(const QString &errorText, CanBusError errorId) { @@ -244,6 +268,24 @@ void QCanBusDevice::setError(const QString &errorText, CanBusError errorId) } /*! + \since 5.14 + Clears the error id and the human readable description of the last + device error. + + CAN bus implementations must use this function to update the device's + error state. + + \sa error(), errorOccurred(), setError() +*/ +void QCanBusDevice::clearError() +{ + Q_D(QCanBusDevice); + + d->errorText.clear(); + d->lastError = NoError; +} + +/*! Appends \a newFrames to the internal list of frames which can be accessed using \l readFrame() and emits the \l framesReceived() signal. @@ -303,6 +345,30 @@ bool QCanBusDevice::hasOutgoingFrames() const } /*! + * \since 5.14 + * Called from the derived plugin to register a function that performs the + * CAN controller hardware reset when \a resetController() is called. + */ +void QCanBusDevice::setResetControllerFunction(std::function<void()> resetter) +{ + Q_D(QCanBusDevice); + + d->m_resetControllerFunction = std::move(resetter); +} + +/*! + * \since 5.14 + * Called from the derived plugin to register a function that returns the + * CAN controller bus status when \a busStatus() is called. + */ +void QCanBusDevice::setCanBusStatusGetter(std::function<CanBusStatus()> busStatusGetter) +{ + Q_D(QCanBusDevice); + + d->m_busStatusGetter = std::move(busStatusGetter); +} + +/*! Sets the configuration parameter \a key for the CAN bus connection to \a value. The potential keys are represented by \l ConfigurationKey. @@ -428,6 +494,82 @@ qint64 QCanBusDevice::framesToWrite() const } /*! + \since 5.14 + + Performs a CAN controller reset to release the CAN controller from + bus off state, if possible. + + \note CAN controller resets disturb the running communication and + may take up to one second to complete. Only call this function to + recover from bus errors. + + \note This function may not be implemented in all CAN plugins. + Please refer to the plugins help pages for more information. + + \sa busStatus() +*/ +void QCanBusDevice::resetController() +{ + if (d_func()->m_resetControllerFunction) { + d_func()->m_resetControllerFunction(); + } else { + const char error[] = QT_TRANSLATE_NOOP("QCanBusDevice", + "This CAN bus plugin does not support hardware controller reset."); + qCWarning(QT_CANBUS, error); + setError(tr(error), QCanBusDevice::CanBusError::ConfigurationError); + } +} + +/*! + \since 5.14 + + Return true, if the CAN plugin supports requesting the CAN bus status. + + \sa busStatus() + */ +bool QCanBusDevice::hasBusStatus() const +{ + return d_func()->m_busStatusGetter != nullptr; +} + +/*! + \since 5.14 + \enum QCanBusDevice::CanBusStatus + + This enum describes possible CAN bus status values. + + \value Unknown The CAN bus status is unknown + (e.g. not supported by the CAN plugin). + \value Good The CAN controller is fully operational + \value Warning The CAN controller is in warning status + \value Error The CAN controller is in error status + (no longer sending CAN frames) + \value BusOff The CAN controller is in bus off status + (disconnected from the CAN bus) +*/ + +/*! + \since 5.14 + + Returns the current CAN bus status. If the status cannot be requested, + QCanBusDevice::UnknownStatus is returned. + + \note This function may not be implemented in all CAN plugins. + Please refer to the plugins help pages for more information. + The function hasBusStatus() can be used at runtime to check if + the used CAN plugin has support for requesting the CAN bus status. + + \sa hasBusStatus(), hardwareControllerReset() +*/ +QCanBusDevice::CanBusStatus QCanBusDevice::busStatus() const +{ + if (d_func()->m_busStatusGetter) + return d_func()->m_busStatusGetter(); + + return QCanBusDevice::CanBusStatus::Unknown; +} + +/*! \since 5.12 \enum QCanBusDevice::Direction @@ -454,8 +596,14 @@ void QCanBusDevice::clear(QCanBusDevice::Directions direction) { Q_D(QCanBusDevice); - if (Q_UNLIKELY(d->state != ConnectedState)) + if (Q_UNLIKELY(d->state != ConnectedState)) { + const QString error = tr("Cannot clear buffers as device is not connected."); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + setError(error, CanBusError::OperationError); return; + } + + clearError(); if (direction & Direction::Input) { QMutexLocker(&d->incomingFramesGuard); @@ -492,31 +640,46 @@ bool QCanBusDevice::waitForFramesWritten(int msecs) "recursively. Check that no slot containing waitForFramesReceived() " "is called in response to framesWritten(qint64) or " "errorOccurred(CanBusError) signals."); + setError(tr("QCanBusDevice::waitForFramesWritten() must not be called recursively."), + CanBusError::OperationError); return false; } - QScopedValueRollback<bool> guard(d_func()->waitForWrittenEntered); - d_func()->waitForWrittenEntered = true; - - if (d_func()->state != ConnectedState) + if (Q_UNLIKELY(d_func()->state != ConnectedState)) { + const QString error = tr("Cannot wait for frames written as device is not connected."); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + setError(error, CanBusError::OperationError); return false; + } if (!framesToWrite()) return false; // nothing pending, nothing to wait upon + QScopedValueRollback<bool> guard(d_func()->waitForWrittenEntered); + d_func()->waitForWrittenEntered = true; + + enum { Written = 0, Error, Timeout }; QEventLoop loop; - connect(this, &QCanBusDevice::framesWritten, &loop, [&]() { loop.exit(0); }); - connect(this, &QCanBusDevice::errorOccurred, &loop, [&]() { loop.exit(1); }); + connect(this, &QCanBusDevice::framesWritten, &loop, [&]() { loop.exit(Written); }); + connect(this, &QCanBusDevice::errorOccurred, &loop, [&]() { loop.exit(Error); }); if (msecs >= 0) - QTimer::singleShot(msecs, &loop, [&]() { loop.exit(2); }); + QTimer::singleShot(msecs, &loop, [&]() { loop.exit(Timeout); }); - int result = 0; + int result = Written; while (framesToWrite() > 0) { // wait till all written or time out result = loop.exec(QEventLoop::ExcludeUserInputEvents); - if (result > 0) + if (Q_UNLIKELY(result == Timeout)) { + const QString error = tr("Timeout (%1 ms) during wait for frames written.").arg(msecs); + setError(error, CanBusError::TimeoutError); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + } + + if (result > Written) return false; } + + clearError(); return true; } @@ -545,25 +708,39 @@ bool QCanBusDevice::waitForFramesReceived(int msecs) "recursively. Check that no slot containing waitForFramesReceived() " "is called in response to framesReceived() or " "errorOccurred(CanBusError) signals."); + setError(tr("QCanBusDevice::waitForFramesReceived() must not be called recursively."), + CanBusError::OperationError); + return false; + } + + if (Q_UNLIKELY(d_func()->state != ConnectedState)) { + const QString error = tr("Cannot wait for frames received as device is not connected."); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + setError(error, CanBusError::OperationError); return false; } QScopedValueRollback<bool> guard(d_func()->waitForReceivedEntered); d_func()->waitForReceivedEntered = true; - if (d_func()->state != ConnectedState) - return false; - + enum { Received = 0, Error, Timeout }; QEventLoop loop; - - connect(this, &QCanBusDevice::framesReceived, &loop, [&]() { loop.exit(0); }); - connect(this, &QCanBusDevice::errorOccurred, &loop, [&]() { loop.exit(1); }); + connect(this, &QCanBusDevice::framesReceived, &loop, [&]() { loop.exit(Received); }); + connect(this, &QCanBusDevice::errorOccurred, &loop, [&]() { loop.exit(Error); }); if (msecs >= 0) - QTimer::singleShot(msecs, &loop, [&]() { loop.exit(2); }); + QTimer::singleShot(msecs, &loop, [&]() { loop.exit(Timeout); }); int result = loop.exec(QEventLoop::ExcludeUserInputEvents); - return result == 0; + if (Q_UNLIKELY(result == Timeout)) { + const QString error = tr("Timeout (%1 ms) during wait for frames received.").arg(msecs); + setError(error, CanBusError::TimeoutError); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + } + + if (result == Received) + clearError(); + return result == Received; } /*! @@ -619,8 +796,14 @@ QCanBusFrame QCanBusDevice::readFrame() { Q_D(QCanBusDevice); - if (Q_UNLIKELY(d->state != ConnectedState)) + if (Q_UNLIKELY(d->state != ConnectedState)) { + const QString error = tr("Cannot read frame as device is not connected."); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + setError(error, CanBusError::OperationError); return QCanBusFrame(QCanBusFrame::InvalidFrame); + } + + clearError(); QMutexLocker locker(&d->incomingFramesGuard); @@ -643,8 +826,14 @@ QVector<QCanBusFrame> QCanBusDevice::readAllFrames() { Q_D(QCanBusDevice); - if (Q_UNLIKELY(d->state != ConnectedState)) + if (Q_UNLIKELY(d->state != ConnectedState)) { + const QString error = tr("Cannot read frame as device is not connected."); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + setError(error, CanBusError::OperationError); return QVector<QCanBusFrame>(); + } + + clearError(); QMutexLocker locker(&d->incomingFramesGuard); @@ -675,9 +864,10 @@ QVector<QCanBusFrame> QCanBusDevice::readAllFrames() As per CAN bus specification, frames of type \l {QCanBusFrame::RemoteRequestFrame} {remote transfer request (RTR)} - do not have a payload, but a length from 0 to 8 (including). This length indicates - the expected response payload length from the remote party. Therefore when sending a RTR frame using - this function it may still be required to set an arbitrary payload on \a frame. The length of + do not have a payload, but a length from 0 to 8 (including). This length + indicates the expected response payload length from the remote party. + Therefore when sending a RTR frame using this function it may still + be required to set an arbitrary payload on \a frame. The length of the arbitrary payload is what is set as size expectation for the RTR frame. \sa QCanBusFrame::setPayload() @@ -705,8 +895,10 @@ bool QCanBusDevice::connectDevice() Q_D(QCanBusDevice); if (Q_UNLIKELY(d->state != QCanBusDevice::UnconnectedState)) { - setError(tr("Can not connect an already connected device"), - QCanBusDevice::ConnectionError); + const char error[] = QT_TRANSLATE_NOOP("QCanBusDevice", + "Can not connect an already connected device."); + qCWarning(QT_CANBUS, error); + setError(tr(error), QCanBusDevice::ConnectionError); return false; } @@ -717,6 +909,8 @@ bool QCanBusDevice::connectDevice() return false; } + clearError(); + //Connected is set by backend -> might be delayed by event loop return true; } |