diff options
Diffstat (limited to 'src')
41 files changed, 1096 insertions, 157 deletions
diff --git a/src/plugins/canbus/peakcan/peakcan_symbols_p.h b/src/plugins/canbus/peakcan/peakcan_symbols_p.h index b42deb7..12de76f 100644 --- a/src/plugins/canbus/peakcan/peakcan_symbols_p.h +++ b/src/plugins/canbus/peakcan/peakcan_symbols_p.h @@ -267,21 +267,28 @@ #define PCAN_TYPE_DNG_SJA_EPP 0x06U // PCAN-Dongle EPP SJA1000 // Type definitions -#define TPCANHandle quint16 // Represents a PCAN hardware channel handle -#define TPCANStatus quint32 // Represents a PCAN status/error code -#define TPCANParameter quint8 // Represents a PCAN parameter to be read or set -#define TPCANDevice quint8 // Represents a PCAN device -#define TPCANMessageType quint8 // Represents the type of a PCAN message -#define TPCANType quint8 // Represents the type of PCAN hardware to be initialized -#define TPCANMode quint8 // Represents a PCAN filter mode -#define TPCANBaudrate quint16 // Represents a PCAN Baud rate register value -#define TPCANBitrateFD char * // Represents a PCAN-FD bit rate string -#define TPCANTimestampFD quint64 // Represents a timestamp of a received PCAN FD message +#ifdef Q_OS_MACOS +#define TPCANLong quint64 +#define TPCANLongToFrameID(a) static_cast<quint32>(a) +#else +#define TPCANLong quint32 +#define TPCANLongToFrameID(a) a +#endif +#define TPCANHandle quint16 // Represents a PCAN hardware channel handle +#define TPCANStatus TPCANLong // Represents a PCAN status/error code +#define TPCANParameter quint8 // Represents a PCAN parameter to be read or set +#define TPCANDevice quint8 // Represents a PCAN device +#define TPCANMessageType quint8 // Represents the type of a PCAN message +#define TPCANType quint8 // Represents the type of PCAN hardware to be initialized +#define TPCANMode quint8 // Represents a PCAN filter mode +#define TPCANBaudrate quint16 // Represents a PCAN Baud rate register value +#define TPCANBitrateFD char * // Represents a PCAN-FD bit rate string +#define TPCANTimestampFD quint64 // Represents a timestamp of a received PCAN FD message // Represents a PCAN message typedef struct tagTPCANMsg { - quint32 ID; // 11/29-bit message identifier + TPCANLong ID; // 11/29-bit message identifier TPCANMessageType MSGTYPE; // Type of the message quint8 LEN; // Data Length Code of the message (0..8) quint8 DATA[8]; // Data of the message (DATA[0]..DATA[7]) @@ -291,15 +298,15 @@ typedef struct tagTPCANMsg // Total Microseconds = micros + 1000 * millis + 0xFFFFFFFF * 1000 * millis_overflow typedef struct tagTPCANTimestamp { - quint32 millis; // Base-value: milliseconds: 0.. 2^32-1 - quint16 millis_overflow; // Roll-arounds of millis - quint16 micros; // Microseconds: 0..999 + TPCANLong millis; // Base-value: milliseconds: 0.. 2^32-1 + quint16 millis_overflow; // Roll-arounds of millis + quint16 micros; // Microseconds: 0..999 } TPCANTimestamp; // Represents a PCAN message from a FD capable hardware typedef struct tagTPCANMsgFD { - quint32 ID; // 11/29-bit message identifier + TPCANLong ID; // 11/29-bit message identifier TPCANMessageType MSGTYPE; // Type of the message quint8 DLC; // Data Length Code of the message (0..15) quint8 DATA[64]; // Data of the message (DATA[0]..DATA[63]) @@ -314,7 +321,7 @@ typedef struct tagTPCANMsgFD if (!symbolName) \ return false; -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Initialize, TPCANHandle, TPCANBaudrate, TPCANType, quint32, quint16) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Initialize, TPCANHandle, TPCANBaudrate, TPCANType, TPCANLong, quint16) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_InitializeFD, TPCANHandle, TPCANBitrateFD) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Uninitialize, TPCANHandle) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Reset, TPCANHandle) @@ -323,15 +330,19 @@ GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Read, TPCANHandle, TPCANMsg *, TPCANTi GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_ReadFD, TPCANHandle, TPCANMsgFD *, TPCANTimestampFD *) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Write, TPCANHandle, TPCANMsg *) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_WriteFD, TPCANHandle, TPCANMsgFD *) -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_FilterMessages, TPCANHandle, quint32, quint32, TPCANMode) -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetValue, TPCANHandle, TPCANParameter, void *, quint32) -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_SetValue, TPCANHandle, TPCANParameter, void *, quint32) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_FilterMessages, TPCANHandle, TPCANLong, TPCANLong, TPCANMode) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetValue, TPCANHandle, TPCANParameter, void *, TPCANLong) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_SetValue, TPCANHandle, TPCANParameter, void *, TPCANLong) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetErrorText, TPCANStatus, quint16, char *) inline bool resolvePeakCanSymbols(QLibrary *pcanLibrary) { if (!pcanLibrary->isLoaded()) { - pcanLibrary->setFileName(QStringLiteral("pcanbasic")); + #ifdef Q_OS_MACOS + pcanLibrary->setFileName(QStringLiteral("PCBUSB")); + #else + pcanLibrary->setFileName(QStringLiteral("pcanbasic")); + #endif if (!pcanLibrary->load()) return false; } diff --git a/src/plugins/canbus/peakcan/peakcanbackend.cpp b/src/plugins/canbus/peakcan/peakcanbackend.cpp index 172de14..5a17b22 100644 --- a/src/plugins/canbus/peakcan/peakcanbackend.cpp +++ b/src/plugins/canbus/peakcan/peakcanbackend.cpp @@ -68,6 +68,8 @@ bool PeakCanBackend::canCreate(QString *errorReason) #else static bool symbolsResolved = resolvePeakCanSymbols(pcanLibrary()); if (Q_UNLIKELY(!symbolsResolved)) { + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot load library: %ls", + qUtf16Printable(pcanLibrary()->errorString())); *errorReason = pcanLibrary()->errorString(); return false; } @@ -331,15 +333,21 @@ bool PeakCanBackendPrivate::open() } if (Q_UNLIKELY(st != PCAN_ERROR_OK)) { - q->setError(systemErrorString(st), QCanBusDevice::ConnectionError); + const QString errorString = systemErrorString(st); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot initialize hardware: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); return false; } #if defined(Q_OS_WIN32) if (readHandle == INVALID_HANDLE_VALUE) { readHandle = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (!readHandle) { - q->setError(qt_error_string(::GetLastError()), QCanBusDevice::ConnectionError); + if (Q_UNLIKELY(!readHandle)) { + const QString errorString = qt_error_string(::GetLastError()); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot create receive event handler: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); return false; } } @@ -353,7 +361,10 @@ bool PeakCanBackendPrivate::open() #else const TPCANStatus err = ::CAN_GetValue(channelIndex, PCAN_RECEIVE_EVENT, &readHandle, sizeof(readHandle)); if (Q_UNLIKELY(err != PCAN_ERROR_OK)) { - q->setError(systemErrorString(err), QCanBusDevice::ConnectionError); + const QString errorString = systemErrorString(err); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot create receive event handler: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); return false; } #endif @@ -380,8 +391,12 @@ void PeakCanBackendPrivate::close() quint32 value = 0; const TPCANStatus err = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &value, sizeof(value)); - if (Q_UNLIKELY(err != PCAN_ERROR_OK)) - q->setError(systemErrorString(err), QCanBusDevice::ConnectionError); + if (Q_UNLIKELY(err != PCAN_ERROR_OK)) { + const QString errorString = systemErrorString(err); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot unregister receive event handler: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); + } const TPCANStatus st = ::CAN_Uninitialize(channelIndex); if (Q_UNLIKELY(st != PCAN_ERROR_OK)) @@ -389,8 +404,12 @@ void PeakCanBackendPrivate::close() #if defined(Q_OS_WIN32) if (readHandle && (readHandle != INVALID_HANDLE_VALUE)) { - if (Q_UNLIKELY(!::CloseHandle(readHandle))) - q->setError(qt_error_string(::GetLastError()), QCanBusDevice::ConnectionError); + const QString errorString = qt_error_string(::GetLastError()); + if (Q_UNLIKELY(!::CloseHandle(readHandle))) { + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot close read handle: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); + } readHandle = INVALID_HANDLE_VALUE; } #else @@ -413,6 +432,7 @@ bool PeakCanBackendPrivate::setConfigurationParameter(int key, const QVariant &v case QCanBusDevice::DataBitRateKey: { const int dataBitrate = value.toInt(); if (Q_UNLIKELY(dataBitrateString(dataBitrate).isEmpty())) { + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported data bitrate value: %d", dataBitrate); q->setError(PeakCanBackend::tr("Unsupported data bitrate value: %1.").arg(dataBitrate), QCanBusDevice::ConfigurationError); return false; @@ -420,6 +440,7 @@ bool PeakCanBackendPrivate::setConfigurationParameter(int key, const QVariant &v return true; } default: + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported configuration key: %d", key); q->setError(PeakCanBackend::tr("Unsupported configuration key: %1").arg(key), QCanBusDevice::ConfigurationError); return false; @@ -558,8 +579,9 @@ void PeakCanBackendPrivate::startWrite() ::memcpy(message.DATA, payload.constData(), sizeof(message.DATA)); st = ::CAN_WriteFD(channelIndex, &message); } else if (frame.hasFlexibleDataRateFormat()) { - q->setError(PeakCanBackend::tr("Cannot send CAN FD frame format as CAN FD is not enabled."), - QCanBusDevice::WriteError); + const char errorString[] = "Cannot send CAN FD frame format as CAN FD is not enabled."; + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN(), errorString); + q->setError(PeakCanBackend::tr(errorString), QCanBusDevice::WriteError); } else { TPCANMsg message; ::memset(&message, 0, sizeof(message)); @@ -576,10 +598,14 @@ void PeakCanBackendPrivate::startWrite() st = ::CAN_Write(channelIndex, &message); } - if (Q_UNLIKELY(st != PCAN_ERROR_OK)) - q->setError(systemErrorString(st), QCanBusDevice::WriteError); - else + if (Q_UNLIKELY(st != PCAN_ERROR_OK)) { + const QString errorString = systemErrorString(st); + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot write frame: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::WriteError); + } else { emit q->framesWritten(qint64(1)); + } if (q->hasOutgoingFrames() && !writeNotifier->isActive()) writeNotifier->start(); @@ -611,7 +637,7 @@ void PeakCanBackendPrivate::startRead() continue; const int size = dlcToSize(static_cast<CanFrameDlc>(message.DLC)); - QCanBusFrame frame(message.ID, + QCanBusFrame frame(TPCANLongToFrameID(message.ID), QByteArray(reinterpret_cast<const char *>(message.DATA), size)); frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(static_cast<qint64>(timestamp))); frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED); @@ -641,7 +667,7 @@ void PeakCanBackendPrivate::startRead() continue; const int size = static_cast<int>(message.LEN); - QCanBusFrame frame(message.ID, + QCanBusFrame frame(TPCANLongToFrameID(message.ID), QByteArray(reinterpret_cast<const char *>(message.DATA), size)); const quint64 millis = timestamp.millis + Q_UINT64_C(0xFFFFFFFF) * timestamp.millis_overflow; const quint64 micros = Q_UINT64_C(1000) * millis + timestamp.micros; @@ -662,8 +688,9 @@ bool PeakCanBackendPrivate::verifyBitRate(int bitrate) Q_Q(PeakCanBackend); if (Q_UNLIKELY(isOpen)) { - q->setError(PeakCanBackend::tr("Cannot change bitrate for already opened device."), - QCanBusDevice::ConfigurationError); + const char errorString[] = "Cannot change bitrate for already opened device."; + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, errorString); + q->setError(PeakCanBackend::tr(errorString), QCanBusDevice::ConfigurationError); return false; } @@ -674,6 +701,7 @@ bool PeakCanBackendPrivate::verifyBitRate(int bitrate) isValidBitrate = bitrateCodeFromBitrate(bitrate) != PCAN_BAUD_INVALID; if (Q_UNLIKELY(!isValidBitrate)) { + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported bitrate value: %d.", bitrate); q->setError(PeakCanBackend::tr("Unsupported bitrate value: %1.").arg(bitrate), QCanBusDevice::ConfigurationError); } @@ -689,6 +717,12 @@ PeakCanBackend::PeakCanBackend(const QString &name, QObject *parent) d->setupChannel(name.toLatin1()); d->setupDefaultConfigurations(); + + std::function<void()> f = std::bind(&PeakCanBackend::resetController, this); + setResetControllerFunction(f); + + std::function<CanBusStatus()> g = std::bind(&PeakCanBackend::busStatus, this); + setCanBusStatusGetter(g); } PeakCanBackend::~PeakCanBackend() @@ -709,11 +743,11 @@ bool PeakCanBackend::open() if (Q_UNLIKELY(!d->open())) return false; - // apply all stored configurations except bitrate, because - // the bitrate can not be applied after opening of device + // Apply all stored configurations except bitrate, because + // the bitrate cannot be changed after opening the device const auto keys = configurationKeys(); for (int key : keys) { - if (key == QCanBusDevice::BitRateKey) + if (key == QCanBusDevice::BitRateKey || key == QCanBusDevice::DataBitRateKey) continue; const QVariant param = configurationParameter(key); const bool success = d->setConfigurationParameter(key, param); @@ -780,4 +814,29 @@ QString PeakCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +void PeakCanBackend::resetController() +{ + close(); + open(); +} + +QCanBusDevice::CanBusStatus PeakCanBackend::busStatus() const +{ + const TPCANStatus status = ::CAN_GetStatus(d_ptr->channelIndex); + + switch (status & PCAN_ERROR_ANYBUSERR) { + case PCAN_ERROR_OK: + return QCanBusDevice::CanBusStatus::Good; + case PCAN_ERROR_BUSWARNING: + return QCanBusDevice::CanBusStatus::Warning; + case PCAN_ERROR_BUSPASSIVE: + return QCanBusDevice::CanBusStatus::Error; + case PCAN_ERROR_BUSOFF: + return QCanBusDevice::CanBusStatus::BusOff; + default: + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unknown CAN bus status: %lu.", ulong(status)); + return QCanBusDevice::CanBusStatus::Unknown; + } +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/peakcan/peakcanbackend.h b/src/plugins/canbus/peakcan/peakcanbackend.h index 7f7083e..a9e108e 100644 --- a/src/plugins/canbus/peakcan/peakcanbackend.h +++ b/src/plugins/canbus/peakcan/peakcanbackend.h @@ -72,6 +72,9 @@ public: static QList<QCanBusDeviceInfo> interfaces(); private: + void resetController(); + CanBusStatus busStatus() const; + PeakCanBackendPrivate * const d_ptr; }; diff --git a/src/plugins/canbus/socketcan/libsocketcan.cpp b/src/plugins/canbus/socketcan/libsocketcan.cpp new file mode 100644 index 0000000..c6144db --- /dev/null +++ b/src/plugins/canbus/socketcan/libsocketcan.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Andre Hartmann <aha_1980@gmx.de> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtSerialBus module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "libsocketcan.h" + +#include <QtCore/qloggingcategory.h> + +#if QT_CONFIG(library) +# include <QtCore/qlibrary.h> +#endif + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_SOCKETCAN) + +#define GENERATE_SYMBOL(returnType, symbolName, ...) \ + typedef returnType (*fp_##symbolName)(__VA_ARGS__); \ + static fp_##symbolName symbolName = nullptr; + +#define RESOLVE_SYMBOL(symbolName) \ + symbolName = reinterpret_cast<fp_##symbolName>(library->resolve(#symbolName)); \ + if (!symbolName) \ + return false; + +struct can_bittiming { + quint32 bitrate = 0; /* Bit-rate in bits/second */ + quint32 sample_point = 0; /* Sample point in one-tenth of a percent */ + quint32 tq = 0; /* Time quanta (TQ) in nanoseconds */ + quint32 prop_seg = 0; /* Propagation segment in TQs */ + quint32 phase_seg1 = 0; /* Phase buffer segment 1 in TQs */ + quint32 phase_seg2 = 0; /* Phase buffer segment 2 in TQs */ + quint32 sjw = 0; /* Synchronization jump width in TQs */ + quint32 brp = 0; /* Bit-rate prescaler */ +}; + +enum can_state { + CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */ + CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */ + CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */ + CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */ + CAN_STATE_STOPPED, /* Device is stopped */ + CAN_STATE_SLEEPING, /* Device is sleeping */ + CAN_STATE_MAX +}; + +GENERATE_SYMBOL(int, can_do_restart, const char * /* name */) +GENERATE_SYMBOL(int, can_do_stop, const char * /* name */) +GENERATE_SYMBOL(int, can_do_start, const char * /* name */) +GENERATE_SYMBOL(int, can_set_bitrate, const char * /* name */, quint32 /* bitrate */) +GENERATE_SYMBOL(int, can_get_bittiming, const char * /* name */, struct can_bittiming * /* bt */) +GENERATE_SYMBOL(int, can_get_state, const char * /* name */, int * /* state */) + +LibSocketCan::LibSocketCan(QString *errorString) +{ +#if QT_CONFIG(library) + auto resolveSymbols = [](QLibrary *library) { + if (!library->isLoaded()) { + library->setFileName(QStringLiteral("socketcan")); + if (!library->load()) + return false; + } + + RESOLVE_SYMBOL(can_do_start); + RESOLVE_SYMBOL(can_do_stop); + RESOLVE_SYMBOL(can_do_restart); + RESOLVE_SYMBOL(can_set_bitrate); + RESOLVE_SYMBOL(can_get_bittiming); + RESOLVE_SYMBOL(can_get_state); + + return true; + }; + + QLibrary lib; + if (Q_UNLIKELY(!resolveSymbols(&lib))) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(lib.errorString())); + if (errorString) + *errorString = lib.errorString(); + } +#else + const QString error = + QObject::tr("Cannot load library libsocketcan as Qt was built without QLibrary."); + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(error)); + if (errorString) + *errorString = error; +#endif +} + +/*! + Brings the CAN \a interface up. + + \internal + \note Requires appropriate permissions. +*/ +bool LibSocketCan::start(const QString &interface) +{ + if (!::can_do_start) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_start() is not available."); + return false; + } + + return ::can_do_start(interface.toLatin1().constData()) == 0; +} + +/*! + Brings the CAN \a interface down. + + \internal + \note Requires appropriate permissions. +*/ +bool LibSocketCan::stop(const QString &interface) +{ + if (!::can_do_stop) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_stop() is not available."); + return false; + } + + return ::can_do_stop(interface.toLatin1().constData()) == 0; +} + +/*! + Performs a CAN controller reset on the CAN \a interface. + + \internal + \note Reset can only be triggerd if the controller is in bus off + and the auto restart not turned on. + \note Requires appropriate permissions. + */ +bool LibSocketCan::restart(const QString &interface) +{ + if (!::can_do_restart) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_restart() is not available."); + return false; + } + + return ::can_do_restart(interface.toLatin1().constData()) == 0; +} + +/*! + Returns the configured bitrate for \a interface. + \internal +*/ +quint32 LibSocketCan::bitrate(const QString &interface) const +{ + if (!::can_get_bittiming) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_get_bittiming() is not available."); + return 0; + } + + struct can_bittiming bt; + if (::can_get_bittiming(interface.toLatin1().constData(), &bt) == 0) + return bt.bitrate; + + return 0; +} + +/*! + Sets the bitrate for the CAN \a interface. + + \internal + \note Requires appropriate permissions. + */ +bool LibSocketCan::setBitrate(const QString &interface, quint32 bitrate) +{ + if (!::can_set_bitrate) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_set_bitrate() is not available."); + return false; + } + + return ::can_set_bitrate(interface.toLatin1().constData(), bitrate) == 0; +} + +bool LibSocketCan::hasBusStatus() const +{ + return ::can_get_state != nullptr; +} + +QCanBusDevice::CanBusStatus LibSocketCan::busStatus(const QString &interface) const +{ + if (!::can_get_state) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_get_state() is not available."); + return QCanBusDevice::CanBusStatus::Unknown; + } + + int status = 0; + int result = ::can_get_state(interface.toLatin1().constData(), &status); + + if (result < 0) + return QCanBusDevice::CanBusStatus::Unknown; + + switch (status) { + case CAN_STATE_ERROR_ACTIVE: + return QCanBusDevice::CanBusStatus::Good; + case CAN_STATE_ERROR_WARNING: + return QCanBusDevice::CanBusStatus::Warning; + case CAN_STATE_ERROR_PASSIVE: + return QCanBusDevice::CanBusStatus::Error; + case CAN_STATE_BUS_OFF: + return QCanBusDevice::CanBusStatus::BusOff; + default: + // Device is stopped or sleeping, so status is unknown + return QCanBusDevice::CanBusStatus::Unknown; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/canbus/socketcan/libsocketcan.h b/src/plugins/canbus/socketcan/libsocketcan.h new file mode 100644 index 0000000..b77afa1 --- /dev/null +++ b/src/plugins/canbus/socketcan/libsocketcan.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Andre Hartmann <aha_1980@gmx.de> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtSerialBus module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LIBSOCKETCAN_H +#define LIBSOCKETCAN_H + +#include <QtCore/qglobal.h> +#include <QtSerialBus/qcanbusdevice.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QString; + +class LibSocketCan final +{ +public: + explicit LibSocketCan(QString *errorString = nullptr); + + bool start(const QString &interface); + bool stop(const QString &interface); + bool restart(const QString &interface); + + quint32 bitrate(const QString &interface) const; + bool setBitrate(const QString &interface, quint32 bitrate); + + bool hasBusStatus() const; + QCanBusDevice::CanBusStatus busStatus(const QString &interface) const; +}; + +QT_END_NAMESPACE + +#endif // LIBSOCKETCAN_H diff --git a/src/plugins/canbus/socketcan/socketcan.pro b/src/plugins/canbus/socketcan/socketcan.pro index 20a2c5d..cd731fd 100644 --- a/src/plugins/canbus/socketcan/socketcan.pro +++ b/src/plugins/canbus/socketcan/socketcan.pro @@ -3,9 +3,11 @@ TARGET = qtsocketcanbus QT = core serialbus HEADERS += \ + libsocketcan.h \ socketcanbackend.h SOURCES += \ + libsocketcan.cpp \ main.cpp \ socketcanbackend.cpp diff --git a/src/plugins/canbus/socketcan/socketcanbackend.cpp b/src/plugins/canbus/socketcan/socketcanbackend.cpp index a2da146..2ed1310 100644 --- a/src/plugins/canbus/socketcan/socketcanbackend.cpp +++ b/src/plugins/canbus/socketcan/socketcanbackend.cpp @@ -36,6 +36,10 @@ #include "socketcanbackend.h" +#include "libsocketcan.h" + +#include <QtSerialBus/qcanbusdevice.h> + #include <QtCore/qdatastream.h> #include <QtCore/qdebug.h> #include <QtCore/qdiriterator.h> @@ -182,7 +186,25 @@ QList<QCanBusDeviceInfo> SocketCanBackend::interfaces() SocketCanBackend::SocketCanBackend(const QString &name) : canSocketName(name) { + QString errorString; + libSocketCan.reset(new LibSocketCan(&errorString)); + if (Q_UNLIKELY(!errorString.isEmpty())) { + qCInfo(QT_CANBUS_PLUGINS_SOCKETCAN, + "Cannot load library libsocketcan, some functionality will not be available.\n%ls", + qUtf16Printable(errorString)); + } + resetConfigurations(); + + std::function<void()> f = std::bind(&SocketCanBackend::resetController, this); + setResetControllerFunction(f); + + if (hasBusStatus()) { + // Only register busStatus when libsocketcan is available + // QCanBusDevice::hasBusStatus() will return false otherwise + std::function<CanBusStatus()> g = std::bind(&SocketCanBackend::busStatus, this); + setCanBusStatusGetter(g); + } } SocketCanBackend::~SocketCanBackend() @@ -201,6 +223,8 @@ void SocketCanBackend::resetConfigurations() QVariant::fromValue(QCanBusFrame::FrameErrors(QCanBusFrame::AnyError))); QCanBusDevice::setConfigurationParameter( QCanBusDevice::CanFdKey, false); + QCanBusDevice::setConfigurationParameter( + QCanBusDevice::BitRateKey, 500000); } bool SocketCanBackend::open() @@ -346,6 +370,12 @@ bool SocketCanBackend::applyConfigurationParameter(int key, const QVariant &valu success = true; break; } + case QCanBusDevice::BitRateKey: + { + const quint32 bitRate = value.toUInt(); + libSocketCan->setBitrate(canSocketName, bitRate); + break; + } default: setError(tr("SocketCanBackend: No such configuration as %1 in SocketCanBackend").arg(key), QCanBusDevice::CanBusError::ConfigurationError); @@ -359,7 +389,7 @@ bool SocketCanBackend::connectSocket() { struct ifreq interface; - if (Q_UNLIKELY((canSocket = socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, CAN_RAW)) < 0)) { + if (Q_UNLIKELY((canSocket = socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, protocol)) < 0)) { setError(qt_error_string(errno), QCanBusDevice::CanBusError::ConnectionError); return false; @@ -434,6 +464,16 @@ void SocketCanBackend::setConfigurationParameter(int key, const QVariant &value) return; } } + } else if (key == QCanBusDevice::ProtocolKey) { + bool ok = false; + const int newProtocol = value.toInt(&ok); + if (Q_UNLIKELY(!ok || (newProtocol < 0))) { + const QString errorString = tr("Cannot set protocol to value %1.").arg(value.toString()); + setError(errorString, QCanBusDevice::ConfigurationError); + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(errorString)); + return; + } + protocol = newProtocol; } // connected & params not applyable/invalid if (canSocket != -1 && !applyConfigurationParameter(key, value)) @@ -731,4 +771,19 @@ void SocketCanBackend::readSocket() enqueueReceivedFrames(newFrames); } +void SocketCanBackend::resetController() +{ + libSocketCan->restart(canSocketName); +} + +bool SocketCanBackend::hasBusStatus() const +{ + return libSocketCan->hasBusStatus(); +} + +QCanBusDevice::CanBusStatus SocketCanBackend::busStatus() const +{ + return libSocketCan->busStatus(canSocketName); +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/socketcan/socketcanbackend.h b/src/plugins/canbus/socketcan/socketcanbackend.h index b49d267..0497244 100644 --- a/src/plugins/canbus/socketcan/socketcanbackend.h +++ b/src/plugins/canbus/socketcan/socketcanbackend.h @@ -52,8 +52,12 @@ #include <linux/can.h> #include <sys/time.h> +#include <memory> + QT_BEGIN_NAMESPACE +class LibSocketCan; + class SocketCanBackend : public QCanBusDevice { Q_OBJECT @@ -79,7 +83,11 @@ private: void resetConfigurations(); bool connectSocket(); bool applyConfigurationParameter(int key, const QVariant &value); + void resetController(); + bool hasBusStatus() const; + QCanBusDevice::CanBusStatus busStatus() const; + int protocol = CAN_RAW; canfd_frame m_frame; sockaddr_can m_address; msghdr m_msg; @@ -89,6 +97,7 @@ private: qint64 canSocket = -1; QSocketNotifier *notifier = nullptr; + std::unique_ptr<LibSocketCan> libSocketCan; QString canSocketName; bool canFdOptionEnabled = false; }; diff --git a/src/plugins/canbus/systeccan/systeccan_symbols_p.h b/src/plugins/canbus/systeccan/systeccan_symbols_p.h index 0d9f246..037f994 100644 --- a/src/plugins/canbus/systeccan/systeccan_symbols_p.h +++ b/src/plugins/canbus/systeccan/systeccan_symbols_p.h @@ -81,6 +81,20 @@ typedef void (DRV_CALLBACK_TYPE *tCallbackFktEx) (tUcanHandle handle, quint32 ev #define USBCAN_EVENT_FATALDISCON 8 // a USB-CANmodul has been disconnected during operation #define USBCAN_EVENT_RESERVED1 0x80 +// CAN status flags (is returned with function UcanGetStatus() or UcanGetStatusEx() ) +#define USBCAN_CANERR_OK 0x0000 // no error +#define USBCAN_CANERR_XMTFULL 0x0001 // Tx-buffer of the CAN controller is full +#define USBCAN_CANERR_OVERRUN 0x0002 // Rx-buffer of the CAN controller is full +#define USBCAN_CANERR_BUSLIGHT 0x0004 // Bus error: Error Limit 1 exceeded (refer to SJA1000 manual) +#define USBCAN_CANERR_BUSHEAVY 0x0008 // Bus error: Error Limit 2 exceeded (refer to SJA1000 manual) +#define USBCAN_CANERR_BUSOFF 0x0010 // Bus error: CAN controller has gone into Bus-Off state +#define USBCAN_CANERR_QRCVEMPTY 0x0020 // RcvQueue is empty +#define USBCAN_CANERR_QOVERRUN 0x0040 // RcvQueue overrun +#define USBCAN_CANERR_QXMTFULL 0x0080 // transmit queue is full +#define USBCAN_CANERR_REGTEST 0x0100 // Register test of the SJA1000 failed +#define USBCAN_CANERR_MEMTEST 0x0200 // Memory test failed +#define USBCAN_CANERR_TXMSGLOST 0x0400 // transmit CAN message was automatically deleted by firmware + #define kUcanModeNormal 0x00 // normal mode (send and receive) #define kUcanModeListenOnly 0x01 // listen only mode (only receive) #define kUcanModeTxEcho 0x02 // CAN messages which was sent will be received at UcanReadCanMsg.. @@ -88,8 +102,8 @@ typedef void (DRV_CALLBACK_TYPE *tCallbackFktEx) (tUcanHandle handle, quint32 ev #define kUcanModeHighResTimer 0x08 // high resolution time stamps in received CAN messages (only available with STM derivates) // ABR and ACR for mode "receive all CAN messages" -#define USBCAN_AMR_ALL (quint32) 0xffffffff -#define USBCAN_ACR_ALL (quint32) 0x00000000 +#define USBCAN_AMR_ALL 0xffffffffU +#define USBCAN_ACR_ALL 0x00000000U #define USBCAN_OCR_DEFAULT 0x1A // default OCR for standard GW-002 #define USBCAN_OCR_RS485_ISOLATED 0x1E // OCR for RS485 interface and galvanic isolation @@ -143,6 +157,11 @@ typedef struct _tCanMsgStruct { quint32 m_dwTime; // Time in ms } tCanMsgStruct; +typedef struct _tStatusStruct { + quint16 m_wCanStatus; // current CAN status + quint16 m_wUsbStatus; // current USB status +} tStatusStruct; + // Function return codes (encoding) #define USBCAN_SUCCESSFUL 0x00 // no error #define USBCAN_ERR 0x01 // error in library; function has not been executed @@ -280,7 +299,9 @@ GENERATE_SYMBOL_VARIABLE(UCANRET, UcanDeinitHardware, tUcanHandle) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanInitCanEx2, tUcanHandle, quint8 /* channel */, tUcanInitCanParam *) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanDeinitCanEx, tUcanHandle, quint8 /* channel */) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanReadCanMsgEx, tUcanHandle, quint8 *, tCanMsgStruct *, quint32 *) +GENERATE_SYMBOL_VARIABLE(UCANRET, UcanResetCan, tUcanHandle) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanWriteCanMsgEx, tUcanHandle, quint8, tCanMsgStruct *, quint32 *) +GENERATE_SYMBOL_VARIABLE(UCANRET, UcanGetStatus, tUcanHandle, tStatusStruct *) inline bool resolveSystecCanSymbols(QLibrary *systecLibrary) { @@ -300,7 +321,9 @@ inline bool resolveSystecCanSymbols(QLibrary *systecLibrary) RESOLVE_SYMBOL(UcanInitCanEx2); RESOLVE_SYMBOL(UcanDeinitCanEx); RESOLVE_SYMBOL(UcanReadCanMsgEx); + RESOLVE_SYMBOL(UcanResetCan); RESOLVE_SYMBOL(UcanWriteCanMsgEx); + RESOLVE_SYMBOL(UcanGetStatus); return true; } diff --git a/src/plugins/canbus/systeccan/systeccanbackend.cpp b/src/plugins/canbus/systeccan/systeccanbackend.cpp index d6942aa..a7de557 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend.cpp +++ b/src/plugins/canbus/systeccan/systeccanbackend.cpp @@ -114,7 +114,7 @@ QList<QCanBusDeviceInfo> SystecCanBackend::interfaces() { QList<QCanBusDeviceInfo> result; - ::UcanEnumerateHardware(&ucanEnumCallback, &result, false, 0, ~0, 0, ~0, 0, ~0); + ::UcanEnumerateHardware(&ucanEnumCallback, &result, false, 0, quint8(~0), 0, quint32(~0), 0, quint32(~0)); return result; } @@ -281,8 +281,8 @@ bool SystecCanBackendPrivate::setupChannel(const QString &interfaceName) const QRegularExpressionMatch match = re.match(interfaceName); if (Q_LIKELY(match.hasMatch())) { - device = match.captured(1).toInt(); - channel = match.captured(2).toInt(); + device = quint8(match.captured(1).toUShort()); + channel = quint8(match.captured(2).toUShort()); } else { q->setError(SystecCanBackend::tr("Invalid interface '%1'.") .arg(interfaceName), QCanBusDevice::ConnectionError); @@ -377,7 +377,7 @@ void SystecCanBackendPrivate::startWrite() ::memset(&message, 0, sizeof(message)); message.m_dwID = frame.frameId(); - message.m_bDLC = payload.size(); + message.m_bDLC = quint8(payload.size()); message.m_bFF = frame.hasExtendedFrameFormat() ? USBCAN_MSG_FF_EXT : USBCAN_MSG_FF_STD; @@ -454,6 +454,40 @@ bool SystecCanBackendPrivate::verifyBitRate(int bitrate) return true; } +void SystecCanBackendPrivate::resetController() +{ + ::UcanResetCan(handle); +} + +QCanBusDevice::CanBusStatus SystecCanBackendPrivate::busStatus() +{ + Q_Q(SystecCanBackend); + + tStatusStruct status; + ::memset(&status, 0, sizeof(status)); + const UCANRET result = ::UcanGetStatus(handle, &status); + + if (Q_UNLIKELY(result != USBCAN_SUCCESSFUL)) { + qCWarning(QT_CANBUS_PLUGINS_SYSTECCAN, "Can not query CAN bus status."); + q->setError(SystecCanBackend::tr("Can not query CAN bus status."), QCanBusDevice::ConfigurationError); + return QCanBusDevice::CanBusStatus::Unknown; + } + + if (status.m_wCanStatus & USBCAN_CANERR_BUSOFF) + return QCanBusDevice::CanBusStatus::BusOff; + + if (status.m_wCanStatus & USBCAN_CANERR_BUSHEAVY) + return QCanBusDevice::CanBusStatus::Error; + + if (status.m_wCanStatus & USBCAN_CANERR_BUSLIGHT) + return QCanBusDevice::CanBusStatus::Warning; + + if (status.m_wCanStatus == USBCAN_CANERR_OK) + return QCanBusDevice::CanBusStatus::Good; + + return QCanBusDevice::CanBusStatus::Unknown; +} + SystecCanBackend::SystecCanBackend(const QString &name, QObject *parent) : QCanBusDevice(parent), d_ptr(new SystecCanBackendPrivate(this)) @@ -462,6 +496,12 @@ SystecCanBackend::SystecCanBackend(const QString &name, QObject *parent) : d->setupChannel(name); d->setupDefaultConfigurations(); + + std::function<void()> f = std::bind(&SystecCanBackend::resetController, this); + setResetControllerFunction(f); + + std::function<CanBusStatus()> g = std::bind(&SystecCanBackend::busStatus, this); + setCanBusStatusGetter(g); } SystecCanBackend::~SystecCanBackend() @@ -553,4 +593,17 @@ QString SystecCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +void SystecCanBackend::resetController() +{ + Q_D(SystecCanBackend); + d->resetController(); +} + +QCanBusDevice::CanBusStatus SystecCanBackend::busStatus() +{ + Q_D(SystecCanBackend); + + return d->busStatus(); +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/systeccan/systeccanbackend.h b/src/plugins/canbus/systeccan/systeccanbackend.h index cb62808..22c1193 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend.h +++ b/src/plugins/canbus/systeccan/systeccanbackend.h @@ -76,6 +76,9 @@ public: int channelNumber); private: + void resetController(); + QCanBusDevice::CanBusStatus busStatus(); + SystecCanBackendPrivate * const d_ptr; }; diff --git a/src/plugins/canbus/systeccan/systeccanbackend_p.h b/src/plugins/canbus/systeccan/systeccanbackend_p.h index 28eb7ff..b2da322 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend_p.h +++ b/src/plugins/canbus/systeccan/systeccanbackend_p.h @@ -94,6 +94,8 @@ public: void startWrite(); void readAllReceivedMessages(); bool verifyBitRate(int bitrate); + void resetController(); + QCanBusDevice::CanBusStatus busStatus(); SystecCanBackend * const q_ptr; diff --git a/src/plugins/canbus/tinycan/tinycanbackend.cpp b/src/plugins/canbus/tinycan/tinycanbackend.cpp index 6453826..fdd5aaa 100644 --- a/src/plugins/canbus/tinycan/tinycanbackend.cpp +++ b/src/plugins/canbus/tinycan/tinycanbackend.cpp @@ -405,8 +405,7 @@ void TinyCanBackendPrivate::startRead() } else { if (status.CanStatus == CAN_STATUS_BUS_OFF) { qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "CAN bus is in off state, trying to reset the bus."); - if (::CanSetMode(channelIndex, OP_CAN_RESET, CAN_CMD_NONE) < 0) - q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ReadError); + resetController(); } } @@ -446,7 +445,8 @@ void TinyCanBackendPrivate::startupDriver() ::CanSetEvents(EVENT_ENABLE_RX_MESSAGES); } else if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_TINYCAN, "Wrong driver reference counter: %d", + driverRefCount); return; } @@ -458,7 +458,8 @@ void TinyCanBackendPrivate::cleanupDriver() --driverRefCount; if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_TINYCAN, "Wrong driver reference counter: %d", + driverRefCount); driverRefCount = 0; } else if (driverRefCount == 0) { ::CanSetEvents(EVENT_DISABLE_ALL); @@ -466,6 +467,18 @@ void TinyCanBackendPrivate::cleanupDriver() } } +void TinyCanBackendPrivate::resetController() +{ + Q_Q(TinyCanBackend); + qint32 ret = ::CanSetMode(channelIndex, OP_CAN_RESET, CAN_CMD_NONE); + if (Q_UNLIKELY(ret < 0)) { + const QString errorString = systemErrorString(ret); + qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "Cannot perform hardware reset: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::CanBusError::ConfigurationError); + } +} + bool TinyCanBackendPrivate::setBitRate(int bitrate) { Q_Q(TinyCanBackend); @@ -496,6 +509,9 @@ TinyCanBackend::TinyCanBackend(const QString &name, QObject *parent) d->setupChannel(name); d->setupDefaultConfigurations(); + + std::function<void()> f = std::bind(&TinyCanBackend::resetController, this); + setResetControllerFunction(f); } TinyCanBackend::~TinyCanBackend() @@ -589,4 +605,10 @@ QString TinyCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +void TinyCanBackend::resetController() +{ + Q_D(TinyCanBackend); + d->resetController(); +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/tinycan/tinycanbackend.h b/src/plugins/canbus/tinycan/tinycanbackend.h index 5f504ca..428e9bc 100644 --- a/src/plugins/canbus/tinycan/tinycanbackend.h +++ b/src/plugins/canbus/tinycan/tinycanbackend.h @@ -72,6 +72,8 @@ public: static QList<QCanBusDeviceInfo> interfaces(); private: + void resetController(); + TinyCanBackendPrivate * const d_ptr; }; diff --git a/src/plugins/canbus/tinycan/tinycanbackend_p.h b/src/plugins/canbus/tinycan/tinycanbackend_p.h index 905175c..25316a2 100644 --- a/src/plugins/canbus/tinycan/tinycanbackend_p.h +++ b/src/plugins/canbus/tinycan/tinycanbackend_p.h @@ -75,6 +75,7 @@ public: void startRead(); void startupDriver(); void cleanupDriver(); + void resetController(); bool setBitRate(int bitrate); diff --git a/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h index 6e35ba4..9d6e841 100644 --- a/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h +++ b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h @@ -249,9 +249,27 @@ static_assert(sizeof(s_xl_can_msg) == 32, "Invalid size of s_xl_can_msg structur #define XL_TRANSCEIVER_EVENT_ERROR 1 #define XL_TRANSCEIVER_EVENT_CHANGED 2 +#define XL_CHIPSTAT_BUSOFF 0x01 +#define XL_CHIPSTAT_ERROR_PASSIVE 0x02 +#define XL_CHIPSTAT_ERROR_WARNING 0x04 +#define XL_CHIPSTAT_ERROR_ACTIVE 0x08 + +#define XL_CAN_STATE_FLAG_SJA_MODE 0x00000001 + +// CAN Chip status +struct s_xl_chip_state { + unsigned char busStatus; + unsigned char txErrorCounter; + unsigned char rxErrorCounter; + unsigned char chipState; // raw Status Register Value + unsigned int flags; +}; +static_assert(sizeof(s_xl_chip_state) == 8, "Invalid size of s_xl_chip_state structure"); + // basic bus message structure union s_xl_tag_data { struct s_xl_can_msg msg; + struct s_xl_chip_state chipState; }; // event type definition (48 bytes) @@ -456,6 +474,7 @@ GENERATE_SYMBOL_VARIABLE(XLstatus, xlCanSetChannelBitrate, XLportHandle, XLacces GENERATE_SYMBOL_VARIABLE(XLstatus, xlCanTransmit, XLportHandle, XLaccess, quint32 *, void *) GENERATE_SYMBOL_VARIABLE(XLstatus, xlReceive, XLportHandle, quint32 *, XLevent *) GENERATE_SYMBOL_VARIABLE(XLstatus, xlSetNotification, XLportHandle, XLhandle *, int) +GENERATE_SYMBOL_VARIABLE(XLstatus, xlCanRequestChipState, XLportHandle, XLaccess) GENERATE_SYMBOL_VARIABLE(char *, xlGetErrorString, XLstatus) inline bool resolveVectorCanSymbols(QLibrary *vectorcanLibrary) @@ -481,6 +500,7 @@ inline bool resolveVectorCanSymbols(QLibrary *vectorcanLibrary) RESOLVE_SYMBOL(xlCanTransmit) RESOLVE_SYMBOL(xlReceive) RESOLVE_SYMBOL(xlSetNotification) + RESOLVE_SYMBOL(xlCanRequestChipState) RESOLVE_SYMBOL(xlGetErrorString) return true; diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend.cpp b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp index 0e6d3ae..0382d66 100644 --- a/src/plugins/canbus/vectorcan/vectorcanbackend.cpp +++ b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp @@ -271,7 +271,8 @@ void VectorCanBackendPrivate::setupChannel(const QString &interfaceName) } } - qCritical("Unable to parse the channel %ls", qUtf16Printable(interfaceName)); + qCCritical(QT_CANBUS_PLUGINS_VECTORCAN, "Unable to parse the channel %ls", + qUtf16Printable(interfaceName)); } void VectorCanBackendPrivate::setupDefaultConfigurations() @@ -388,7 +389,8 @@ XLstatus VectorCanBackendPrivate::loadDriver() return status; } else if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_VECTORCAN, "Wrong driver reference counter: %d", + driverRefCount); return XL_ERR_CANNOT_OPEN_DRIVER; } @@ -412,7 +414,8 @@ void VectorCanBackendPrivate::cleanupDriver() --driverRefCount; if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_VECTORCAN, "Wrong driver reference counter: %d", + driverRefCount); driverRefCount = 0; } else if (driverRefCount == 0) { ::xlCloseDriver(); @@ -443,6 +446,9 @@ VectorCanBackend::VectorCanBackend(const QString &name, QObject *parent) d->setupChannel(name); d->setupDefaultConfigurations(); + + std::function<CanBusStatus()> g = std::bind(&VectorCanBackend::busStatus, this); + setCanBusStatusGetter(g); } VectorCanBackend::~VectorCanBackend() @@ -537,4 +543,48 @@ QString VectorCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +QCanBusDevice::CanBusStatus VectorCanBackend::busStatus() +{ + Q_D(VectorCanBackend); + + const XLstatus requestStatus = ::xlCanRequestChipState(d->portHandle, d->channelMask); + if (Q_UNLIKELY(requestStatus != XL_SUCCESS)) { + const QString errorString = d->systemErrorString(requestStatus); + qCWarning(QT_CANBUS_PLUGINS_VECTORCAN, "Can not query CAN bus status: %ls.", + qUtf16Printable(errorString)); + setError(errorString, QCanBusDevice::CanBusError::ReadError); + return QCanBusDevice::CanBusStatus::Unknown; + } + + quint32 eventCount = 1; + XLevent event; + ::memset(&event, 0, sizeof(event)); + + const XLstatus receiveStatus = ::xlReceive(d->portHandle, &eventCount, &event); + if (Q_UNLIKELY(receiveStatus != XL_SUCCESS)) { + const QString errorString = d->systemErrorString(receiveStatus); + qCWarning(QT_CANBUS_PLUGINS_VECTORCAN, "Can not query CAN bus status: %ls.", + qUtf16Printable(errorString)); + setError(errorString, QCanBusDevice::CanBusError::ReadError); + return QCanBusDevice::CanBusStatus::Unknown; + } + + if (Q_LIKELY(event.tag == XL_CHIP_STATE)) { + switch (event.tagData.chipState.busStatus) { + case XL_CHIPSTAT_BUSOFF: + return QCanBusDevice::CanBusStatus::BusOff; + case XL_CHIPSTAT_ERROR_PASSIVE: + return QCanBusDevice::CanBusStatus::Error; + case XL_CHIPSTAT_ERROR_WARNING: + return QCanBusDevice::CanBusStatus::Warning; + case XL_CHIPSTAT_ERROR_ACTIVE: + return QCanBusDevice::CanBusStatus::Good; + } + } + + qCWarning(QT_CANBUS_PLUGINS_VECTORCAN, "Unknown CAN bus status: %u", + uint(event.tagData.chipState.busStatus)); + return QCanBusDevice::CanBusStatus::Unknown; +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend.h b/src/plugins/canbus/vectorcan/vectorcanbackend.h index 165d369..89b9da6 100644 --- a/src/plugins/canbus/vectorcan/vectorcanbackend.h +++ b/src/plugins/canbus/vectorcan/vectorcanbackend.h @@ -71,6 +71,8 @@ public: static QList<QCanBusDeviceInfo> interfaces(); private: + QCanBusDevice::CanBusStatus busStatus(); + VectorCanBackendPrivate * const d_ptr; }; diff --git a/src/serialbus/doc/src/peakcan.qdoc b/src/serialbus/doc/src/peakcan.qdoc index fd12ab7..a38ddf7 100644 --- a/src/serialbus/doc/src/peakcan.qdoc +++ b/src/serialbus/doc/src/peakcan.qdoc @@ -34,8 +34,9 @@ \l{http://www.peak-system.com/}{PEAK-System} CAN adapters. This plugin requires the PCAN device drivers and the PCAN-Basic library - version 4.0.0 or higher. Supported platforms are Windows (pcanbasic.dll) - and Linux (libpcanbasic.so). + version 4.0.0 or higher on Windows (pcanbasic.dll) and Linux (libpcanbasic.so). + On macOS the plugin requires the PCBUSB library from + \l{http://www.mac-can.com}{UV Software}. \section1 Creating CAN Bus Devices @@ -70,6 +71,8 @@ \note Only the USB and PCI adapters are currently supported by this plugin. + \note On macOS, only USB adapters are currently supported by this plugin. + The device is now open for writing and reading CAN frames: \code @@ -117,4 +120,12 @@ this configuration parameter can only be adjusted while the QCanBusDevice is not connected. \endtable + + PeakCAN supports the following additional functions: + + \list + \li QCanBusDevice::resetController() + \li QCanBusDevice::busStatus() + \endlist + */ diff --git a/src/serialbus/doc/src/socketcan.qdoc b/src/serialbus/doc/src/socketcan.qdoc index df58dae..e578969 100644 --- a/src/serialbus/doc/src/socketcan.qdoc +++ b/src/serialbus/doc/src/socketcan.qdoc @@ -179,9 +179,13 @@ By default, the connection is configured to accept any CAN bus message. \row \li QCanBusDevice::BitRateKey - \li This configuration is not supported by the socketcan plugin. However - it is possible to set the rate when configuring the CAN network interface using - the \c {ip link} command. + \li Determines the bit rate of the CAN bus connection. The following bit rates + are supported: 5000, 10000, 20000, 33000, 47000, 50000, 83000, 95000, + 100000, 125000, 250000, 500000, 800000, 1000000. Note that this configuration + parameter can only be adjusted while the QCanBusDevice is not connected. To set + this configuration parameter, the library libsocketcan is needed at runtime + http://www.pengutronix.de/software/libsocketcan + Usually, root rights are needed to set the CAN bus bitrate. \row \li QCanBusDevice::CanFdKey \li This configuration option determines whether CANFD frames may be sent or received. @@ -192,6 +196,10 @@ \li This configuration is not supported by the socketcan plugin. However it is possible to set the data rate when configuring the CAN network interface using the \c {ip link} command. + \row + \li QCanBusDevice::ProtocolKey + \li Allows to use another protocol inside the protocol family PF_CAN. The default + value for this configuration option is CAN_RAW (1). \endtable For example: @@ -199,4 +207,12 @@ \snippet snippetmain.cpp SocketCan Filter Example Extended frame format and flexible data-rate are supported in SocketCAN. - */ + + SocketCAN supports the following additional functions: + + \list + \li QCanBusDevice::resetController() (needs libsocketcan) + \li QCanBusDevice::busStatus() (needs libsocketcan) + \endlist + +*/ diff --git a/src/serialbus/doc/src/systeccan.qdoc b/src/serialbus/doc/src/systeccan.qdoc index af34665..dc66023 100644 --- a/src/serialbus/doc/src/systeccan.qdoc +++ b/src/serialbus/doc/src/systeccan.qdoc @@ -109,4 +109,12 @@ is disabled by default. If this option is enabled, the therefore received frames are marked with QCanBusFrame::hasLocalEcho() \endtable + + SystecCAN supports the following additional functions: + + \list + \li QCanBusDevice::resetController() + \li QCanBusDevice::busStatus() + \endlist + */ diff --git a/src/serialbus/doc/src/tinycan.qdoc b/src/serialbus/doc/src/tinycan.qdoc index 43349dc..8479576 100644 --- a/src/serialbus/doc/src/tinycan.qdoc +++ b/src/serialbus/doc/src/tinycan.qdoc @@ -95,4 +95,11 @@ \li Determines the bit rate of the CAN bus connection. The following bit rates are supported: 10000, 20000, 50000, 100000, 125000, 250000, 500000, 800000, 1000000. \endtable - */ + + TinyCAN supports the following additional functions: + + \list + \li QCanBusDevice::resetController() + \endlist + +*/ diff --git a/src/serialbus/doc/src/vectorcan.qdoc b/src/serialbus/doc/src/vectorcan.qdoc index eb4acdd..70b6521 100644 --- a/src/serialbus/doc/src/vectorcan.qdoc +++ b/src/serialbus/doc/src/vectorcan.qdoc @@ -106,4 +106,11 @@ was successful. If this option is enabled, the therefore received frames are marked with QCanBusFrame::hasLocalEcho() \endtable + + VectorCAN supports the following additional functions: + + \list + \li QCanBusDevice::busStatus() + \endlist + */ 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; } diff --git a/src/serialbus/qcanbusdevice.h b/src/serialbus/qcanbusdevice.h index a47fac4..5d2d976 100644 --- a/src/serialbus/qcanbusdevice.h +++ b/src/serialbus/qcanbusdevice.h @@ -41,6 +41,8 @@ #include <QtSerialBus/qcanbusframe.h> #include <QtSerialBus/qcanbusdeviceinfo.h> +#include <functional> + QT_BEGIN_NAMESPACE class QCanBusDevicePrivate; @@ -58,7 +60,9 @@ public: WriteError, ConnectionError, ConfigurationError, - UnknownError + UnknownError, + OperationError, + TimeoutError }; Q_ENUM(CanBusError) @@ -70,6 +74,15 @@ public: }; Q_ENUM(CanBusDeviceState) + enum class CanBusStatus { + Unknown, + Good, + Warning, + Error, + BusOff + }; + Q_ENUM(CanBusStatus) + enum ConfigurationKey { RawFilterKey = 0, ErrorFilterKey, @@ -78,12 +91,24 @@ public: BitRateKey, CanFdKey, DataBitRateKey, + ProtocolKey, UserKey = 30 }; Q_ENUM(ConfigurationKey) struct Filter { + friend constexpr bool operator==(const Filter &a, const Filter &b) noexcept + { + return a.frameId == b.frameId && a.frameIdMask == b.frameIdMask + && a.type == b.type && a.format == b.format; + } + + friend constexpr bool operator!=(const Filter &a, const Filter &b) noexcept + { + return !operator==(a, b); + } + enum FormatFilter { MatchBaseFormat = 0x0001, MatchExtendedFormat = 0x0002, @@ -109,6 +134,10 @@ public: qint64 framesAvailable() const; qint64 framesToWrite() const; + void resetController(); + bool hasBusStatus() const; + QCanBusDevice::CanBusStatus busStatus() const; + enum Direction { Input = 1, Output = 2, @@ -140,6 +169,7 @@ Q_SIGNALS: protected: void setState(QCanBusDevice::CanBusDeviceState newState); void setError(const QString &errorText, QCanBusDevice::CanBusError); + void clearError(); void enqueueReceivedFrames(const QVector<QCanBusFrame> &newFrames); @@ -152,6 +182,9 @@ protected: virtual bool open() = 0; virtual void close() = 0; + void setResetControllerFunction(std::function<void()> resetter); + void setCanBusStatusGetter(std::function<CanBusStatus()> busStatusGetter); + static QCanBusDeviceInfo createDeviceInfo(const QString &name, bool isVirtual = false, bool isFlexibleDataRateCapable = false); diff --git a/src/serialbus/qcanbusdevice_p.h b/src/serialbus/qcanbusdevice_p.h index faeae65..e74168c 100644 --- a/src/serialbus/qcanbusdevice_p.h +++ b/src/serialbus/qcanbusdevice_p.h @@ -74,6 +74,9 @@ public: bool waitForReceivedEntered = false; bool waitForWrittenEntered = false; + + std::function<void()> m_resetControllerFunction; + std::function<QCanBusDevice::CanBusStatus()> m_busStatusGetter; }; QT_END_NAMESPACE diff --git a/src/serialbus/qmodbusdevice.cpp b/src/serialbus/qmodbusdevice.cpp index 9891d16..ab8e126 100644 --- a/src/serialbus/qmodbusdevice.cpp +++ b/src/serialbus/qmodbusdevice.cpp @@ -98,8 +98,8 @@ QModbusDevice::~QModbusDevice() User options: - \value UserParameter The first parameter that can be used for user-specific - purposes. \c QVariant + \value UserParameter This enum value has been deprecated. There + will be no replacement. */ /*! @@ -143,7 +143,7 @@ QVariant QModbusDevice::connectionParameter(int parameter) const default: break; } - return d->m_userConnectionParams.value(parameter); + return d->m_userConnectionParams.value(parameter); // ### Qt6: remove } /*! @@ -182,7 +182,7 @@ void QModbusDevice::setConnectionParameter(int parameter, const QVariant &value) d->m_networkAddress = value.toString(); break; default: - d->m_userConnectionParams.insert(parameter, value); + d->m_userConnectionParams.insert(parameter, value); // ### Qt6: remove break; } } @@ -337,6 +337,20 @@ QString QModbusDevice::errorString() const } /*! + \since 5.14 + + Returns the underlying \l QIODevice used for ModBus communication or + \c nullptr if the device was not yet fully initialized. + + \note Do not store a pointer to the underlying device, because it can be + invalidated at any point in time. +*/ +QIODevice *QModbusDevice::device() const +{ + return d_func()->device(); +} + +/*! \fn bool QModbusDevice::open() This function is called by connectDevice(). Subclasses must provide diff --git a/src/serialbus/qmodbusdevice.h b/src/serialbus/qmodbusdevice.h index 468fdf4..0e6dad7 100644 --- a/src/serialbus/qmodbusdevice.h +++ b/src/serialbus/qmodbusdevice.h @@ -82,7 +82,7 @@ public: NetworkAddressParameter, // Reserved - UserParameter = 0x100 + UserParameter = 0x100 // ### Qt6: remove }; Q_ENUM(ConnectionParameter) @@ -100,6 +100,8 @@ public: Error error() const; QString errorString() const; + QIODevice *device() const; + Q_SIGNALS: void errorOccurred(QModbusDevice::Error error); void stateChanged(QModbusDevice::State state); diff --git a/src/serialbus/qmodbusdevice_p.h b/src/serialbus/qmodbusdevice_p.h index a845a89..1526397 100644 --- a/src/serialbus/qmodbusdevice_p.h +++ b/src/serialbus/qmodbusdevice_p.h @@ -78,7 +78,9 @@ public: int m_networkPort = 502; QString m_networkAddress = QStringLiteral("127.0.0.1"); - QHash<int, QVariant> m_userConnectionParams; + QHash<int, QVariant> m_userConnectionParams; // ### Qt6: remove + + virtual QIODevice *device() const { return nullptr; } }; QT_END_NAMESPACE diff --git a/src/serialbus/qmodbuspdu.cpp b/src/serialbus/qmodbuspdu.cpp index c42352d..9ca9027 100644 --- a/src/serialbus/qmodbuspdu.cpp +++ b/src/serialbus/qmodbuspdu.cpp @@ -106,9 +106,9 @@ static int minimumDataSize(const QModbusPdu &pdu, Type type) static QDataStream &pduFromStream(QDataStream &stream, QModbusPdu &pdu, Type type) { quint8 codeByte = 0; - if (stream.readRawData((char *) (&codeByte), sizeof(quint8)) != sizeof(quint8)) + if (stream.readRawData(reinterpret_cast<char *>(&codeByte), sizeof(quint8)) != sizeof(quint8)) return stream; - QModbusPdu::FunctionCode code = (QModbusPdu::FunctionCode) codeByte; + QModbusPdu::FunctionCode code = QModbusPdu::FunctionCode(codeByte); pdu.setFunctionCode(code); auto needsAdditionalRead = [](QModbusPdu &pdu, int size) -> bool { @@ -157,13 +157,13 @@ static QDataStream &pduFromStream(QDataStream &stream, QModbusPdu &pdu, Type typ return stream; // early return to avoid second read } } else { - data.resize(stream.device()->size() - 1); // One byte for the function code. + data.resize(int(stream.device()->size() - 1)); // One byte for the function code. } } else if (pdu.functionCode() == QModbusPdu::Diagnostics) { quint16 subCode; pdu.decodeData(&subCode); if (subCode == Diagnostics::ReturnQueryData) - data.resize(stream.device()->size() - 1); // One byte for the function code. + data.resize(int(stream.device()->size() - 1)); // One byte for the function code. } // reset what we have so far, next read might fail as well @@ -439,7 +439,7 @@ static QDataStream &pduFromStream(QDataStream &stream, QModbusPdu &pdu, Type typ QDebug operator<<(QDebug debug, const QModbusPdu &pdu) { QDebugStateSaver _(debug); - debug.nospace().noquote() << "0x" << hex << qSetFieldWidth(2) << qSetPadChar('0') + debug.nospace().noquote() << "0x" << Qt::hex << qSetFieldWidth(2) << qSetPadChar('0') << (pdu.isException() ? pdu.functionCode() | QModbusPdu::ExceptionByte : pdu.functionCode()) << qSetFieldWidth(0) << pdu.data().toHex(); return debug; @@ -547,7 +547,7 @@ int QModbusRequest::minimumDataSize(const QModbusRequest &request) int QModbusRequest::calculateDataSize(const QModbusRequest &request) { if (requestSizeCalculators.exists()) { - if (auto ptr = requestSizeCalculators()->value(request.functionCode(), nullptr)) + if (auto ptr = requestSizeCalculators()->value(quint8(request.functionCode()), nullptr)) return ptr(request); } @@ -604,7 +604,7 @@ int QModbusRequest::calculateDataSize(const QModbusRequest &request) */ void QModbusRequest::registerDataSizeCalculator(FunctionCode fc, CalcFuncPtr calculator) { - requestSizeCalculators()->insert(fc, calculator); + requestSizeCalculators()->insert(quint8(fc), calculator); } /*! @@ -703,7 +703,7 @@ int QModbusResponse::minimumDataSize(const QModbusResponse &response) int QModbusResponse::calculateDataSize(const QModbusResponse &response) { if (responseSizeCalculators.exists()) { - if (auto ptr = responseSizeCalculators()->value(response.functionCode(), nullptr)) + if (auto ptr = responseSizeCalculators()->value(quint8(response.functionCode()), nullptr)) return ptr(response); } @@ -765,7 +765,7 @@ int QModbusResponse::calculateDataSize(const QModbusResponse &response) for (int i = 1; i < numOfObjects; ++i) { if (data.size() <= nextSizeField) break; - objectSize = data[nextSizeField]; + objectSize = quint8(data[nextSizeField]); size += objectSize; nextSizeField += objectSize + 2; // object size + object id field + object size field } @@ -790,7 +790,7 @@ int QModbusResponse::calculateDataSize(const QModbusResponse &response) */ void QModbusResponse::registerDataSizeCalculator(FunctionCode fc, CalcFuncPtr calculator) { - responseSizeCalculators()->insert(fc, calculator); + responseSizeCalculators()->insert(quint8(fc), calculator); } /*! diff --git a/src/serialbus/qmodbuspdu.h b/src/serialbus/qmodbuspdu.h index 14cef59..30904a7 100644 --- a/src/serialbus/qmodbuspdu.h +++ b/src/serialbus/qmodbuspdu.h @@ -101,7 +101,7 @@ public: bool isException() const { return m_code & ExceptionByte; } qint16 size() const { return dataSize() + 1; } - qint16 dataSize() const { return m_data.size(); } + qint16 dataSize() const { return qint16(m_data.size()); } FunctionCode functionCode() const { return FunctionCode(quint8(m_code) &~ ExceptionByte); diff --git a/src/serialbus/qmodbusrtuserialmaster_p.h b/src/serialbus/qmodbusrtuserialmaster_p.h index 9672684..d206eec 100644 --- a/src/serialbus/qmodbusrtuserialmaster_p.h +++ b/src/serialbus/qmodbusrtuserialmaster_p.h @@ -310,7 +310,7 @@ public: onBytesWritten(bytes); }); - QObject::connect(m_serialPort, QOverload<QSerialPort::SerialPortError>::of(&QSerialPort::error), + QObject::connect(m_serialPort, &QSerialPort::errorOccurred, q, [this](QSerialPort::SerialPortError error) { onError(error); }); @@ -427,6 +427,8 @@ public: return false; } + QIODevice *device() const override { return m_serialPort; } + Timer m_responseTimer; QByteArray m_responseBuffer; diff --git a/src/serialbus/qmodbusrtuserialslave_p.h b/src/serialbus/qmodbusrtuserialslave_p.h index da59e5e..d6ff8d6 100644 --- a/src/serialbus/qmodbusrtuserialslave_p.h +++ b/src/serialbus/qmodbusrtuserialslave_p.h @@ -280,8 +280,7 @@ public: storeModbusCommEvent(event); // store the final event after processing }); - using TypeId = void (QSerialPort::*)(QSerialPort::SerialPortError); - QObject::connect(m_serialPort, static_cast<TypeId>(&QSerialPort::error), q, + QObject::connect(m_serialPort, &QSerialPort::errorOccurred, q, [this](QSerialPort::SerialPortError error) { if (error == QSerialPort::NoError) return; @@ -355,6 +354,8 @@ public: m_requestBuffer.clear(); } + QIODevice *device() const override { return m_serialPort; } + QByteArray m_requestBuffer; bool m_processesBroadcast = false; QSerialPort *m_serialPort = nullptr; diff --git a/src/serialbus/qmodbustcpclient_p.h b/src/serialbus/qmodbustcpclient_p.h index db1d4e4..649b48e 100644 --- a/src/serialbus/qmodbustcpclient_p.h +++ b/src/serialbus/qmodbustcpclient_p.h @@ -86,8 +86,8 @@ public: cleanupTransactionStore(); }); - using TypeId = void (QAbstractSocket::*)(QAbstractSocket::SocketError); - QObject::connect(m_socket, static_cast<TypeId>(&QAbstractSocket::error), q, + QObject::connect(m_socket, + QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), q, [this](QAbstractSocket::SocketError /*error*/) { Q_Q(QModbusTcpClient); @@ -121,7 +121,7 @@ public: if (knownTransaction && m_transactionStore[transactionId].timer) m_transactionStore[transactionId].timer->stop(); - qCDebug(QT_MODBUS) << "(TCP client) tid:" << hex << transactionId << "size:" + qCDebug(QT_MODBUS) << "(TCP client) tid:" << Qt::hex << transactionId << "size:" << bytesPdu << "server address:" << serverAddress; // The length field is the byte count of the following fields, including the Unit @@ -169,7 +169,7 @@ public: return false; } qCDebug(QT_MODBUS_LOW) << "(TCP client) Sent TCP ADU:" << buffer.toHex(); - qCDebug(QT_MODBUS) << "(TCP client) Sent TCP PDU:" << request << "with tId:" << hex + qCDebug(QT_MODBUS) << "(TCP client) Sent TCP PDU:" << request << "with tId:" <<Qt:: hex << tId; return true; }; @@ -193,9 +193,8 @@ public: }); if (element.timer) { - using TypeId = void (QTimer::*)(int); q->connect(q, &QModbusClient::timeoutChanged, - element.timer.data(), static_cast<TypeId>(&QTimer::setInterval)); + element.timer.data(), QOverload<int>::of(&QTimer::setInterval)); QObject::connect(element.timer.data(), &QTimer::timeout, q, [this, writeToSocket, tId]() { if (!m_transactionStore.contains(tId)) return; @@ -210,9 +209,9 @@ public: return; m_transactionStore.insert(tId, elem); elem.timer->start(); - qCDebug(QT_MODBUS) << "(TCP client) Resend request with tId:" << hex << tId; + qCDebug(QT_MODBUS) << "(TCP client) Resend request with tId:" << Qt::hex << tId; } else { - qCDebug(QT_MODBUS) << "(TCP client) Timeout of request with tId:" << hex << tId; + qCDebug(QT_MODBUS) << "(TCP client) Timeout of request with tId:" <<Qt::hex << tId; elem.reply->setError(QModbusDevice::TimeoutError, QModbusClient::tr("Request timeout.")); } @@ -220,7 +219,7 @@ public: element.timer->start(); } else { qCWarning(QT_MODBUS) << "(TCP client) No response timeout timer for request with tId:" - << hex << tId << ". Expected timeout:" << m_responseTimeoutDuration; + << Qt::hex << tId << ". Expected timeout:" << m_responseTimeoutDuration; } incrementTransactionId(); @@ -255,6 +254,8 @@ public: inline void incrementTransactionId() { m_transactionId++; } inline int transactionId() const { return m_transactionId; } + QIODevice *device() const override { return m_socket; } + QTcpSocket *m_socket = nullptr; QByteArray responseBuffer; QHash<quint16, QueueElement> m_transactionStore; diff --git a/src/serialbus/qmodbustcpserver_p.h b/src/serialbus/qmodbustcpserver_p.h index 56c45e9..3e73de3 100644 --- a/src/serialbus/qmodbustcpserver_p.h +++ b/src/serialbus/qmodbustcpserver_p.h @@ -167,7 +167,7 @@ public: input >> transactionId >> protocolId >> bytesPdu >> unitId; qCDebug(QT_MODBUS_LOW) << "(TCP server) Request MBPA:" << "Transaction Id:" - << hex << transactionId << "Protocol Id:" << protocolId << "PDU bytes:" + << Qt::hex << transactionId << "Protocol Id:" << protocolId << "PDU bytes:" << bytesPdu << "Unit Id:" << unitId; // The length field is the byte count of the following fields, including the Unit @@ -224,7 +224,7 @@ public: }); } - QTcpServer *m_tcpServer; + QTcpServer *m_tcpServer { nullptr }; QVector<QTcpSocket *> connections; std::unique_ptr<QModbusTcpConnectionObserver> m_observer; diff --git a/src/tools/canbusutil/canbusutil.cpp b/src/tools/canbusutil/canbusutil.cpp index a4e1a58..b07d97c 100644 --- a/src/tools/canbusutil/canbusutil.cpp +++ b/src/tools/canbusutil/canbusutil.cpp @@ -53,9 +53,9 @@ void CanBusUtil::setShowTimeStamp(bool showTimeStamp) m_readTask->setShowTimeStamp(showTimeStamp); } -void CanBusUtil::setShowFdFlags(bool showFdFlags) +void CanBusUtil::setShowFlags(bool showFlags) { - m_readTask->setShowFdFlags(showFdFlags); + m_readTask->setShowFlags(showFlags); } void CanBusUtil::setConfigurationParameter(QCanBusDevice::ConfigurationKey key, @@ -67,7 +67,7 @@ void CanBusUtil::setConfigurationParameter(QCanBusDevice::ConfigurationKey key, bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, const QString &data) { if (!m_canBus) { - m_output << tr("Error: Cannot create QCanBus.") << endl; + m_output << tr("Error: Cannot create QCanBus.") << Qt::endl; return false; } @@ -80,7 +80,7 @@ bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, con return false; if (m_listening) { - if (m_readTask->isShowFdFlags()) + if (m_readTask->isShowFlags()) m_canDevice->setConfigurationParameter(QCanBusDevice::CanFdKey, true); connect(m_canDevice.data(), &QCanBusDevice::framesReceived, m_readTask, &ReadTask::handleFrames); @@ -96,32 +96,32 @@ bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, con int CanBusUtil::printPlugins() { if (!m_canBus) { - m_output << tr("Error: Cannot create QCanBus.") << endl; + m_output << tr("Error: Cannot create QCanBus.") << Qt::endl; return 1; } const QStringList plugins = m_canBus->plugins(); for (const QString &plugin : plugins) - m_output << plugin << endl; + m_output << plugin << Qt::endl; return 0; } int CanBusUtil::printDevices(const QString &pluginName) { if (!m_canBus) { - m_output << tr("Error: Cannot create QCanBus.") << endl; + m_output << tr("Error: Cannot create QCanBus.") << Qt::endl; return 1; } QString errorMessage; const QList<QCanBusDeviceInfo> devices = m_canBus->availableDevices(pluginName, &errorMessage); if (!errorMessage.isEmpty()) { - m_output << tr("Error gathering available devices: '%1'").arg(errorMessage) << endl; + m_output << tr("Error gathering available devices: '%1'").arg(errorMessage) << Qt::endl; return 1; } for (const QCanBusDeviceInfo &info : devices) - m_output << info.name() << endl; + m_output << info.name() << Qt::endl; return 0; } @@ -129,7 +129,7 @@ bool CanBusUtil::parseDataField(quint32 &id, QString &payload) { int hashMarkPos = m_data.indexOf('#'); if (hashMarkPos < 0) { - m_output << tr("Data field invalid: No hash mark found!") << endl; + m_output << tr("Data field invalid: No hash mark found!") << Qt::endl; return false; } @@ -154,7 +154,7 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) return true; } - m_output << tr("Error: RTR frame length must be between 0 and 8 (including).") << endl; + m_output << tr("Error: RTR frame length must be between 0 and 8 (including).") << Qt::endl; return false; } @@ -165,7 +165,7 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) const QRegularExpression re(QStringLiteral("^[0-9A-Fa-f]*$")); if (!re.match(payload).hasMatch()) { - m_output << tr("Data field invalid: Only hex numbers allowed.") << endl; + m_output << tr("Data field invalid: Only hex numbers allowed.") << Qt::endl; return false; } @@ -177,7 +177,7 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) frame->setErrorStateIndicator(flags & ErrorStateIndicatorFlag); payload.remove(0, 1); } else { - m_output << tr("Data field invalid: Size is not multiple of two.") << endl; + m_output << tr("Data field invalid: Size is not multiple of two.") << Qt::endl; return false; } } @@ -186,7 +186,7 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) const int maxSize = frame->hasFlexibleDataRateFormat() ? 64 : 8; if (bytes.size() > maxSize) { - m_output << tr("Data field invalid: Size is longer than %1 bytes.").arg(maxSize) << endl; + m_output << tr("Data field invalid: Size is longer than %1 bytes.").arg(maxSize) << Qt::endl; return false; } @@ -198,13 +198,13 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) bool CanBusUtil::connectCanDevice() { if (!m_canBus->plugins().contains(m_pluginName)) { - m_output << tr("Cannot find CAN bus plugin '%1'.").arg(m_pluginName) << endl; + m_output << tr("Cannot find CAN bus plugin '%1'.").arg(m_pluginName) << Qt::endl; return false; } m_canDevice.reset(m_canBus->createDevice(m_pluginName, m_deviceName)); if (!m_canDevice) { - m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << endl; + m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << Qt::endl; return false; } @@ -214,7 +214,7 @@ bool CanBusUtil::connectCanDevice() connect(m_canDevice.data(), &QCanBusDevice::errorOccurred, m_readTask, &ReadTask::handleError); if (!m_canDevice->connectDevice()) { - m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << endl; + m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << Qt::endl; return false; } @@ -234,7 +234,7 @@ bool CanBusUtil::sendData() return false; if (id > 0x1FFFFFFF) { // 29 bits - m_output << tr("Cannot send invalid frame ID: '%1'").arg(id, 0, 16) << endl; + m_output << tr("Cannot send invalid frame ID: '%1'").arg(id, 0, 16) << Qt::endl; return false; } diff --git a/src/tools/canbusutil/canbusutil.h b/src/tools/canbusutil/canbusutil.h index 19b7868..4d1b7f4 100644 --- a/src/tools/canbusutil/canbusutil.h +++ b/src/tools/canbusutil/canbusutil.h @@ -57,7 +57,7 @@ public: explicit CanBusUtil(QTextStream &output, QCoreApplication &app, QObject *parent = nullptr); void setShowTimeStamp(bool showTimeStamp); - void setShowFdFlags(bool showFdFlags); + void setShowFlags(bool showFlags); void setConfigurationParameter(QCanBusDevice::ConfigurationKey key, const QVariant &value); bool start(const QString &pluginName, const QString &deviceName, const QString &data = QString()); int printPlugins(); diff --git a/src/tools/canbusutil/main.cpp b/src/tools/canbusutil/main.cpp index 3a71192..cbb7d7f 100644 --- a/src/tools/canbusutil/main.cpp +++ b/src/tools/canbusutil/main.cpp @@ -94,9 +94,10 @@ int main(int argc, char *argv[]) CanBusUtil::tr("Show timestamp for each received CAN bus frame.")); parser.addOption(showTimeStampOption); - const QCommandLineOption showFdFlagsOption({"i", "info"}, - CanBusUtil::tr("Show extra info (CAN FD flags) for each received CAN bus frame.")); - parser.addOption(showFdFlagsOption); + const QCommandLineOption showFlagsOption({"i", "info"}, + CanBusUtil::tr("Show flags bitrate switch, error indicator, and local echo" + " for each received CAN bus frame.")); + parser.addOption(showFlagsOption); const QCommandLineOption listDevicesOption({"d", "devices"}, CanBusUtil::tr("Show available CAN bus devices for the given plugin.")); @@ -149,14 +150,16 @@ int main(int argc, char *argv[]) if (parser.isSet(listeningOption)) { util.setShowTimeStamp(parser.isSet(showTimeStampOption)); - util.setShowFdFlags(parser.isSet(showFdFlagsOption)); + util.setShowFlags(parser.isSet(showFlagsOption)); } else if (args.size() == 3) { data = args.at(2); } else if (args.size() == 1 && parser.isSet(listDevicesOption)) { return util.printDevices(args.at(0)); - } else if (args.size() != 2) { + } + + if (args.size() < 2 || args.size() > 3) { output << CanBusUtil::tr("Invalid number of arguments (%1 given).").arg(args.size()); - output << endl << endl << parser.helpText(); + output << Qt::endl << Qt::endl << parser.helpText(); return 1; } diff --git a/src/tools/canbusutil/readtask.cpp b/src/tools/canbusutil/readtask.cpp index 2bde509..1b07fe8 100644 --- a/src/tools/canbusutil/readtask.cpp +++ b/src/tools/canbusutil/readtask.cpp @@ -45,14 +45,14 @@ void ReadTask::setShowTimeStamp(bool showTimeStamp) m_showTimeStamp = showTimeStamp; } -bool ReadTask::isShowFdFlags() const +bool ReadTask::isShowFlags() const { - return m_showFdFlags; + return m_showFlags; } -void ReadTask::setShowFdFlags(bool showFlags) +void ReadTask::setShowFlags(bool showFlags) { - m_showFdFlags = showFlags; + m_showFlags = showFlags; } void ReadTask::handleFrames() { @@ -68,17 +68,21 @@ void ReadTask::handleFrames() { QString view; if (m_showTimeStamp) { - view = QString::fromLatin1("%1.%2 ") + view = QStringLiteral("%1.%2 ") .arg(frame.timeStamp().seconds(), 10, 10, QLatin1Char(' ')) .arg(frame.timeStamp().microSeconds() / 100, 4, 10, QLatin1Char('0')); } - if (m_showFdFlags) { - QString flags = QLatin1String("- - "); + if (m_showFlags) { + QLatin1String flags("- - - "); + if (frame.hasBitrateSwitch()) flags[0] = QLatin1Char('B'); if (frame.hasErrorStateIndicator()) flags[2] = QLatin1Char('E'); + if (frame.hasLocalEcho()) + flags[4] = QLatin1Char('L'); + view += flags; } @@ -87,7 +91,7 @@ void ReadTask::handleFrames() { else view += frame.toString(); - m_output << view << endl; + m_output << view << Qt::endl; } } @@ -99,5 +103,5 @@ void ReadTask::handleError(QCanBusDevice::CanBusError /*error*/) return; } - m_output << tr("Read error: '%1'").arg(canDevice->errorString()) << endl; + m_output << tr("Read error: '%1'").arg(canDevice->errorString()) << Qt::endl; } diff --git a/src/tools/canbusutil/readtask.h b/src/tools/canbusutil/readtask.h index a866d3d..345af35 100644 --- a/src/tools/canbusutil/readtask.h +++ b/src/tools/canbusutil/readtask.h @@ -47,8 +47,8 @@ class ReadTask : public QObject public: explicit ReadTask(QTextStream &m_output, QObject *parent = nullptr); void setShowTimeStamp(bool showStamp); - bool isShowFdFlags() const; - void setShowFdFlags(bool isShowFdFlags); + bool isShowFlags() const; + void setShowFlags(bool isShowFlags); public slots: void handleFrames(); @@ -57,7 +57,7 @@ public slots: private: QTextStream &m_output; bool m_showTimeStamp = false; - bool m_showFdFlags = false; + bool m_showFlags = false; }; #endif // READTASK_H diff --git a/src/tools/canbusutil/sigtermhandler.cpp b/src/tools/canbusutil/sigtermhandler.cpp index 8611916..a882a55 100644 --- a/src/tools/canbusutil/sigtermhandler.cpp +++ b/src/tools/canbusutil/sigtermhandler.cpp @@ -47,7 +47,7 @@ SigTermHandler *SigTermHandler::instance() void SigTermHandler::handle(int s) { QTextStream out(stdout); - out << " Caught signal " << s << endl; + out << " Caught signal " << s << Qt::endl; emit instance()->sigTermSignal(); } |