diff options
53 files changed, 2106 insertions, 303 deletions
diff --git a/.qmake.conf b/.qmake.conf index 006073e..c9d5d20 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -2,4 +2,4 @@ load(qt_build_config) CONFIG += warning_clean DEFINES += QT_NO_FOREACH -MODULE_VERSION = 5.10.1 +MODULE_VERSION = 5.11.0 diff --git a/examples/serialbus/can/connectdialog.cpp b/examples/serialbus/can/connectdialog.cpp index 5a12592..601ef2c 100644 --- a/examples/serialbus/can/connectdialog.cpp +++ b/examples/serialbus/can/connectdialog.cpp @@ -115,6 +115,12 @@ void ConnectDialog::interfaceChanged(const QString &interface) for (const QCanBusDeviceInfo &info : qAsConst(m_interfaces)) { if (info.name() == interface) { + m_ui->descriptionLabel->setText(info.description()); + QString serialNumber = info.serialNumber(); + if (serialNumber.isEmpty()) + serialNumber = tr("n/a"); + m_ui->serialNumberLabel->setText(tr("Serial: %1").arg(serialNumber)); + m_ui->channelLabel->setText(tr("Channel: %1").arg(info.channel())); m_ui->isVirtual->setChecked(info.isVirtual()); m_ui->isFlexibleDataRateCapable->setChecked(info.hasFlexibleDataRate()); break; diff --git a/examples/serialbus/can/connectdialog.ui b/examples/serialbus/can/connectdialog.ui index def6e59..6d4f261 100644 --- a/examples/serialbus/can/connectdialog.ui +++ b/examples/serialbus/can/connectdialog.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>441</width> - <height>341</height> + <width>446</width> + <height>395</height> </rect> </property> <property name="windowTitle"> @@ -192,6 +192,27 @@ </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> + <widget class="QLabel" name="descriptionLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="serialNumberLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="channelLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> <widget class="QCheckBox" name="isFlexibleDataRateCapable"> <property name="text"> <string>Flexible Data Rate</string> diff --git a/examples/serialbus/can/main.cpp b/examples/serialbus/can/main.cpp index 28cb478..757f3aa 100644 --- a/examples/serialbus/can/main.cpp +++ b/examples/serialbus/can/main.cpp @@ -49,10 +49,13 @@ ****************************************************************************/ #include "mainwindow.h" + #include <QApplication> +#include <QLoggingCategory> int main(int argc, char *argv[]) { + QLoggingCategory::setFilterRules(QStringLiteral("qt.canbus* = true")); QApplication a(argc, argv); MainWindow w; w.show(); diff --git a/src/plugins/canbus/canbus.pro b/src/plugins/canbus/canbus.pro index 37db8a3..0eddffc 100644 --- a/src/plugins/canbus/canbus.pro +++ b/src/plugins/canbus/canbus.pro @@ -7,6 +7,6 @@ qtConfig(socketcan) { } qtConfig(library) { - SUBDIRS += peakcan tinycan + SUBDIRS += passthrucan peakcan tinycan win32:SUBDIRS += systeccan vectorcan } diff --git a/src/plugins/canbus/passthrucan/j2534passthru.cpp b/src/plugins/canbus/passthrucan/j2534passthru.cpp new file mode 100644 index 0000000..22bf996 --- /dev/null +++ b/src/plugins/canbus/passthrucan/j2534passthru.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Ford Motor Company. +** 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 "j2534passthru.h" + +#include <QtEndian> + +#include <cstring> + +namespace { + +enum Ioctl { + GetConfig = 1, + SetConfig = 2 +}; + +// Template to model the structs SCONFIG_LIST, SBYTE_ARRAY etc as defined +// in the J2534 spec. +template <typename T> +struct SArray +{ + SArray(ulong n, T *p) : num(n), ptr(p) {} + // On Windows x64, ulong is 32 bit wide and thus the value would normally + // be padded so that the pointer begins on a 64-bit boundary. It is not + // clear from the J2534 spec whether structs should be packed or not on + // x64. Most vendors still only provide a 32-bit DLL, but there is at + // least one x64 implementation (from Hatteland Display) out there which + // does not pack this struct. + ulong num; + T *ptr; +}; + +// Fixed-length string buffers must be at least 80 bytes according to the spec. +// The example code in the spec document uses 256 bytes though -- let's play it +// safe and do so, too. +const int StringBufferSize = 256; + +} // anonymous namespace + +Q_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PASSTHRU, "qt.canbus.plugins.passthru", QtWarningMsg) + +namespace J2534 { + +Message::Message() +{ + std::memset(m_data, 0, sizeof(m_data)); +} + +Message::Message(Protocol proto) + : m_protocolId (ulong(proto)) +{ + std::memset(m_data, 0, sizeof(m_data)); +} + +PassThru::PassThru(const QString &libraryPath, QObject *parent) + : QObject(parent) + , m_libJ2534 (libraryPath, this) +{ + if (!m_libJ2534.load() + || !resolveApiFunction(&m_ptOpen, "PassThruOpen") + || !resolveApiFunction(&m_ptClose, "PassThruClose") + || !resolveApiFunction(&m_ptConnect, "PassThruConnect") + || !resolveApiFunction(&m_ptDisconnect, "PassThruDisconnect") + || !resolveApiFunction(&m_ptReadMsgs, "PassThruReadMsgs") + || !resolveApiFunction(&m_ptWriteMsgs, "PassThruWriteMsgs") + || !resolveApiFunction(&m_ptStartMsgFilter, "PassThruStartMsgFilter") + || !resolveApiFunction(&m_ptGetLastError, "PassThruGetLastError") + || !resolveApiFunction(&m_ptIoctl, "PassThruIoctl")) { + + m_lastError = LoadFailed; + m_lastErrorString = m_libJ2534.errorString(); + + qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "%ls", qUtf16Printable(m_lastErrorString)); + } +} + +PassThru::~PassThru() +{ + m_libJ2534.unload(); +} + +PassThru::Status PassThru::open(const QByteArray &name, Handle *deviceId) +{ + Q_ASSERT(m_ptOpen); + + const char *const devName = (name.isEmpty()) ? nullptr : name.data(); + const long status = (*m_ptOpen)(devName, deviceId); + return handleResult(status); +} + +PassThru::Status PassThru::close(Handle deviceId) +{ + Q_ASSERT(m_ptClose); + + const long status = (*m_ptClose)(deviceId); + return handleResult(status); +} + +PassThru::Status PassThru::connect(Handle deviceId, Protocol protocolId, + ConnectFlags flags, uint baudRate, Handle *channelId) +{ + Q_ASSERT(m_ptConnect); + + const long status = (*m_ptConnect)(deviceId, ulong(protocolId), + flags, baudRate, channelId); + return handleResult(status); +} + +PassThru::Status PassThru::disconnect(Handle channelId) +{ + Q_ASSERT(m_ptDisconnect); + + const long status = (*m_ptDisconnect)(channelId); + return handleResult(status); +} + +PassThru::Status PassThru::readMsgs(Handle channelId, Message *msgs, + ulong *numMsgs, uint timeout) +{ + Q_ASSERT(m_ptReadMsgs); + + const long status = (*m_ptReadMsgs)(channelId, msgs, numMsgs, timeout); + return handleResult(status); +} + +PassThru::Status PassThru::writeMsgs(Handle channelId, const Message *msgs, + ulong *numMsgs, uint timeout) +{ + Q_ASSERT(m_ptWriteMsgs); + + const long status = (*m_ptWriteMsgs)(channelId, msgs, numMsgs, timeout); + return handleResult(status); +} + +PassThru::Status PassThru::startMsgFilter(Handle channelId, FilterType filterType, + const Message &maskMsg, const Message &patternMsg) +{ + Q_ASSERT(m_ptStartMsgFilter); + + // The CAN pass-thru plugin implementation does not need the filter ID. + Handle filterId = 0; + + const long status = (*m_ptStartMsgFilter)(channelId, filterType, &maskMsg, + &patternMsg, nullptr, &filterId); + return handleResult(status); +} + +PassThru::Status PassThru::setConfig(Handle channelId, const Config *params, ulong numParams) +{ + Q_ASSERT(m_ptIoctl); + + const SArray<const Config> configList {numParams, params}; + const long status = (*m_ptIoctl)(channelId, SetConfig, &configList, nullptr); + return handleResult(status); +} + +PassThru::Status PassThru::clear(Handle channelId, ClearTarget target) +{ + Q_ASSERT(m_ptIoctl); + + const long status = (*m_ptIoctl)(channelId, target, nullptr, nullptr); + return handleResult(status); +} + +QString PassThru::lastErrorString() const +{ + return m_lastErrorString; +} + +PassThru::Status PassThru::handleResult(long statusCode) +{ + if (Q_UNLIKELY(statusCode != NoError)) { + m_lastError = Status(statusCode); + + QByteArray description (StringBufferSize, 0); + Q_ASSERT(m_ptGetLastError); + const long descStatus = (*m_ptGetLastError)(description.data()); + + if (Q_LIKELY(descStatus == NoError)) { + m_lastErrorString = QString::fromLatin1(description); + } else { + m_lastErrorString = tr("Command failed with status code %1").arg(statusCode); + qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "GetLastError failed with code %ld", descStatus); + } + } + return Status(statusCode); +} + +} // namespace J2534 diff --git a/src/plugins/canbus/passthrucan/j2534passthru.h b/src/plugins/canbus/passthrucan/j2534passthru.h new file mode 100644 index 0000000..a9b88f4 --- /dev/null +++ b/src/plugins/canbus/passthrucan/j2534passthru.h @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Ford Motor Company. +** 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 PASSTHRUCAN_J2534PASSTHRU_H +#define PASSTHRUCAN_J2534PASSTHRU_H + +#include <QByteArray> +#include <QLibrary> +#include <QLoggingCategory> +#include <QObject> +#include <QString> + +#ifdef Q_OS_WIN32 +# define J2534_API __stdcall +#else +# define J2534_API +#endif + +Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PASSTHRU) + +namespace J2534 { + +class Message; + +extern "C" { + +typedef long (J2534_API *PassThruOpenFunc)(const void *pName, ulong *pDeviceId); +typedef long (J2534_API *PassThruCloseFunc)(ulong deviceId); +typedef long (J2534_API *PassThruConnectFunc)(ulong deviceId, ulong protocolId, ulong flags, + ulong baudRate, ulong *pChannelId); +typedef long (J2534_API *PassThruDisconnectFunc)(ulong channelId); +typedef long (J2534_API *PassThruReadMsgsFunc)(ulong channelId, Message *pMsg, + ulong *pNumMsgs, ulong timeout); +typedef long (J2534_API *PassThruWriteMsgsFunc)(ulong channelId, const Message *pMsg, + ulong *pNumMsgs, ulong timeout); +typedef long (J2534_API *PassThruStartMsgFilterFunc)(ulong channelID, ulong filterType, + const Message *pMaskMsg, + const Message *pPatternMsg, + const Message *pFlowControlMsg, + ulong *pFilterId); +typedef long (J2534_API *PassThruGetLastErrorFunc)(char *pErrorDescription); +typedef long (J2534_API *PassThruIoctlFunc)(ulong channelId, ulong ioctlId, + const void *pInput, void *pOutput); +} // extern "C" + +enum class Protocol : uint { + J1850VPW = 1, + J1850PWM, + ISO9141, + ISO14230, + CAN, + ISO15765, + SCIAEngine, + SCIATrans, + SCIBEngine, + SCIBTrans +}; + +class Message +{ +public: + static const ulong maxSize = 4128; + + enum RxStatusBit { + InTxMsgType = 1 << 0, + InStartOfMessage = 1 << 1, + InRxBreak = 1 << 2, + InTxIndication = 1 << 3, + InISO15765PaddingError = 1 << 4, + InISO15765AddrType = 1 << 7, + InCAN29BitID = 1 << 8 + }; + Q_DECLARE_FLAGS(RxStatus, RxStatusBit) + + enum TxFlag { + OutISO15765FramePad = 1 << 6, + OutISO15765AddrType = 1 << 7, + OutCAN29BitID = 1 << 8, + OutWaitP3MinOnly = 1 << 9 + }; + Q_DECLARE_FLAGS(TxFlags, TxFlag) + + Message(); + explicit Message(Protocol proto); + + Protocol protocolId() const { return Protocol(m_protocolId); } + void setProtocolId(Protocol proto) { m_protocolId = uint(proto); } + + RxStatus rxStatus() const { return RxStatus(uint(m_rxStatus)); } + void setRxStatus(RxStatus status) { m_rxStatus = uint(status); } + + TxFlags txFlags() const { return TxFlags(uint(m_txFlags)); } + void setTxFlags(TxFlags flags) { m_txFlags = uint(flags); } + + ulong timestamp() const { return m_timestamp; } + void setTimestamp(ulong stamp) { m_timestamp = stamp; } + + ulong size() const { return m_dataSize; } + void setSize(ulong dataSize) { m_dataSize = dataSize; } + + ulong extraDataIndex() const { return m_extraDataIndex; } + void setExtraDataIndex(ulong index) { m_extraDataIndex = index; } + + char *data() { return m_data; } + const char *data() const { return m_data; } + +private: + ulong m_protocolId = 0; + ulong m_rxStatus = 0; + ulong m_txFlags = 0; + ulong m_timestamp = 0; + ulong m_dataSize = 0; + ulong m_extraDataIndex = 0; + char m_data[maxSize]; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Message::RxStatus) +Q_DECLARE_OPERATORS_FOR_FLAGS(Message::TxFlags) + +class Config +{ +public: + enum Parameter { + DataRate = 1, + + Loopback = 3, + NodeAddress, + NetworkLine, + P1Min, + P1Max, + P2Min, + P2Max, + P3Min, + P3Max, + P4Min, + P4Max, + W1, + W2, + W3, + W4, + W5, + Tidle, + Tinil, + Twup, + Parity, + BitSamplePoint, + SyncJumpWidth, + W0, + T1Max, + T2Max, + T4Max, + T5Max, + ISO15765BS, + ISO15765STmin, + DataBits, + FiveBaudMod, + BSTx, + STminTx, + T3Max, + ISO15765WFTMax, + + CanMixedFormat = 0x8000, + J1962Pins, + + SWCANHSDataRate = 0x8010, + SWCANSpeedchangeEnable, + SWCANResSwitch, + + ActiveChannels = 0x8020, + SampleRate, + SamplesPerReading, + ReadingsPerMsg, + AveragingMethod, + SampleResolution, + InputRangeLow, + InputRangeHigh + }; + + Config() : m_parameter(0), m_value(0) {} + explicit Config(Parameter param, ulong val = 0) : m_parameter(param), m_value(val) {} + + Parameter parameter() const { return Parameter(m_parameter); } + ulong value() const { return m_value; } + +private: + ulong m_parameter; + ulong m_value; +}; + +/** + * @brief J2534 pass-through interface, version 04.04. + * @internal + * @see http://www.drewtech.com/support/passthru.html + */ +class PassThru : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(PassThru) +public: + typedef ulong Handle; + + enum Status { + LoadFailed = -1, + NoError = 0, + NotSupported, + InvalidChannelID, + InvalidProtocolID, + NullParameter, + InvalidIoctlValue, + InvalidFlags, + Failed, + DeviceNotConnected, + Timeout, + InvalidMsg, + InvalidTimeInterval, + ExceededLimit, + InvalidMsgID, + DeviceInUse, + InvalidIoctlID, + BufferEmpty, + BufferFull, + BufferOverflow, + PinInvalid, + ChannelInUse, + MsgProtocolID, + InvalidFilterID, + NoFlowControl, + NotUnique, + InvalidBaudrate, + InvalidDeviceID + }; + + enum ConnectFlag { + CAN29BitID = 1 << 8, + ISO9141NoChecksum = 1 << 9, + CANIDBoth = 1 << 11, + ISO9141KLineOnly = 1 << 12 + }; + Q_DECLARE_FLAGS(ConnectFlags, ConnectFlag) + + enum FilterType { + PassFilter = 1, + BlockFilter, + FlowControlFilter + }; + + enum ClearTarget { + TxBuffer = 7, + RxBuffer, + PeriodicMsgs, + MsgFilters + }; + + explicit PassThru(const QString &libraryPath, QObject *parent = nullptr); + virtual ~PassThru(); + + Status open(const QByteArray &name, Handle *deviceId); + Status close(Handle deviceId); + Status connect(Handle deviceId, Protocol protocolId, ConnectFlags flags, + uint baudRate, Handle *channelId); + Status disconnect(Handle channelId); + Status readMsgs(Handle channelId, Message *msgs, ulong *numMsgs, uint timeout = 0); + Status writeMsgs(Handle channelId, const Message *msgs, ulong *numMsgs, uint timeout = 0); + Status startMsgFilter(Handle channelId, FilterType filterType, + const Message &maskMsg, const Message &patternMsg); + Status setConfig(Handle channelId, const Config *params, ulong numParams = 1); + Status clear(Handle channelId, ClearTarget target); + + Status lastError() const { return m_lastError; } + QString lastErrorString() const; + +private: + Status handleResult(long statusCode); + + template <typename Func> + Func resolveApiFunction(Func *funcPtr, const char *name) { + *funcPtr = reinterpret_cast<Func>(m_libJ2534.resolve(name)); + return *funcPtr; + } + + QLibrary m_libJ2534; + PassThruOpenFunc m_ptOpen = nullptr; + PassThruCloseFunc m_ptClose = nullptr; + PassThruConnectFunc m_ptConnect = nullptr; + PassThruDisconnectFunc m_ptDisconnect = nullptr; + PassThruReadMsgsFunc m_ptReadMsgs = nullptr; + PassThruWriteMsgsFunc m_ptWriteMsgs = nullptr; + PassThruStartMsgFilterFunc m_ptStartMsgFilter = nullptr; + PassThruGetLastErrorFunc m_ptGetLastError = nullptr; + PassThruIoctlFunc m_ptIoctl = nullptr; + QString m_lastErrorString; + Status m_lastError = NoError; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(PassThru::ConnectFlags) + +} // namespace J2534 + +#endif // PASSTHRUCAN_J2534PASSTHRU_H diff --git a/src/plugins/canbus/passthrucan/main.cpp b/src/plugins/canbus/passthrucan/main.cpp new file mode 100644 index 0000000..5017597 --- /dev/null +++ b/src/plugins/canbus/passthrucan/main.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Ford Motor Company. +** 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 "passthrucanbackend.h" + +#include <QtSerialBus/qcanbus.h> +#include <QtSerialBus/qcanbusdevice.h> +#include <QtSerialBus/qcanbusfactory.h> + +QT_BEGIN_NAMESPACE + +class PassThruCanBusPlugin : public QObject, public QCanBusFactoryV2 +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QCanBusFactory" FILE "plugin.json") + Q_INTERFACES(QCanBusFactoryV2) + +public: + PassThruCanBusPlugin() + { + qRegisterMetaType<QCanBusDevice::CanBusError>(); + qRegisterMetaType<QVector<QCanBusFrame>>(); + } + + QList<QCanBusDeviceInfo> availableDevices(QString *) const override + { + return PassThruCanBackend::interfaces(); + } + + QCanBusDevice *createDevice(const QString &interfaceName, QString *) const override + { + return new PassThruCanBackend(interfaceName); + } +}; + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/canbus/passthrucan/passthrucan.pro b/src/plugins/canbus/passthrucan/passthrucan.pro new file mode 100644 index 0000000..96ed090 --- /dev/null +++ b/src/plugins/canbus/passthrucan/passthrucan.pro @@ -0,0 +1,21 @@ +QT = serialbus + +TARGET = qtpassthrucanbus + +SOURCES += \ + j2534passthru.cpp \ + main.cpp \ + passthrucanio.cpp \ + passthrucanbackend.cpp + +HEADERS += \ + j2534passthru.h \ + passthrucanio.h \ + passthrucanbackend.h + +DISTFILES = plugin.json + +PLUGIN_TYPE = canbus +PLUGIN_EXTENDS = serialbus +PLUGIN_CLASS_NAME = PassThruCanBusPlugin +load(qt_plugin) diff --git a/src/plugins/canbus/passthrucan/passthrucanbackend.cpp b/src/plugins/canbus/passthrucan/passthrucanbackend.cpp new file mode 100644 index 0000000..2c18160 --- /dev/null +++ b/src/plugins/canbus/passthrucan/passthrucanbackend.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Ford Motor Company. +** 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 "passthrucanbackend.h" +#include "passthrucanio.h" + +#include <QEventLoop> +#include <QSettings> + +namespace { + +#ifdef Q_OS_WIN32 + +static inline QString registryPath() +{ + return QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\PassThruSupport.04.04"); +} + +static QString canAdapterName(const QSettings &entries) +{ + const int supportsCan = entries.value(QStringLiteral("CAN")).toInt(); + if (supportsCan) + return entries.value(QStringLiteral("Name")).toString(); + return {}; +} + +static QString libraryForAdapter(const QString &adapterName) +{ + QString library; + QSettings entries (registryPath(), QSettings::NativeFormat); + const QStringList groups = entries.childGroups(); + + for (const auto &group : groups) { + entries.beginGroup(group); + + const QString name = canAdapterName(entries); + if (!name.isEmpty() && (adapterName.isEmpty() || + name.compare(adapterName, Qt::CaseInsensitive) == 0)) + library = entries.value(QStringLiteral("FunctionLibrary")).toString(); + + entries.endGroup(); + + if (!library.isEmpty()) + break; + } + return library; +} + +#else // !Q_OS_WIN32 + +static QString libraryForAdapter(const QString &adapterName) +{ + // Insert system-specific device name to J2534 library name mapping here. + // For now, allow the path to the J2534 library to be specified directly + // as the adapter name. + return adapterName; +} + +#endif // !Q_OS_WIN32 + +} // anonymous namespace + +PassThruCanBackend::PassThruCanBackend(const QString &name, QObject *parent) + : QCanBusDevice(parent) + , m_deviceName (name) + , m_canIO (new PassThruCanIO()) +{ + m_canIO->moveToThread(&m_ioThread); + + // Signals emitted by the I/O thread, to be queued. + connect(m_canIO, &PassThruCanIO::errorOccurred, + this, &PassThruCanBackend::setError); + connect(m_canIO, &PassThruCanIO::openFinished, + this, &PassThruCanBackend::ackOpenFinished); + connect(m_canIO, &PassThruCanIO::closeFinished, + this, &PassThruCanBackend::ackCloseFinished); + connect(m_canIO, &PassThruCanIO::messagesReceived, + this, &PassThruCanBackend::enqueueReceivedFrames); + connect(m_canIO, &PassThruCanIO::messagesSent, + this, &QCanBusDevice::framesWritten); +} + +PassThruCanBackend::~PassThruCanBackend() +{ + if (state() != UnconnectedState) { + // If the I/O thread is still active at this point, we will have to + // wait for it to finish. + QEventLoop loop; + connect(&m_ioThread, &QThread::finished, &loop, &QEventLoop::quit); + + if (state() != ClosingState) + disconnectDevice(); + + while (!m_ioThread.isFinished()) + loop.exec(QEventLoop::ExcludeUserInputEvents); + } + m_canIO->deleteLater(); +} + +void PassThruCanBackend::setConfigurationParameter(int key, const QVariant &value) +{ + QCanBusDevice::setConfigurationParameter(key, value); + + if (state() == ConnectedState) + applyConfig(key, value); +} + +bool PassThruCanBackend::writeFrame(const QCanBusFrame &frame) +{ + if (state() != ConnectedState) { + setError(tr("Device is not connected"), WriteError); + return false; + } + if (!frame.isValid()) { + setError(tr("Invalid CAN bus frame"), WriteError); + return false; + } + if (frame.frameType() != QCanBusFrame::DataFrame) { + setError(tr("Unsupported CAN frame type"), WriteError); + return false; + } + // Push the frame directly to the write queue of the worker thread, + // bypassing the QCanBusDevice output queue. Despite the duplicated + // queue, things are cleaner this way as it avoids a reverse dependency + // from the worker object on the QCanBusDevice object. + return m_canIO->enqueueMessage(frame); +} + +QString PassThruCanBackend::interpretErrorFrame(const QCanBusFrame &) +{ + // J2534 Pass-thru v04.04 does not seem to support error frames. + return {}; +} + +QList<QCanBusDeviceInfo> PassThruCanBackend::interfaces() +{ + QList<QCanBusDeviceInfo> list; +#ifdef Q_OS_WIN32 + QSettings entries (registryPath(), QSettings::NativeFormat); + const QStringList groups = entries.childGroups(); + + for (const auto &group : groups) { + entries.beginGroup(group); + + const QString name = canAdapterName(entries); + if (!name.isEmpty()) + list.append(createDeviceInfo(name)); + + entries.endGroup(); + } +#endif + return list; +} + +bool PassThruCanBackend::open() +{ + if (Q_UNLIKELY(state() != ConnectingState)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Unexpected state on open"); + return false; + } + // Support a special "adapter%subdevice" syntax to allow control of the + // device name passed to the J2534 library's PassThruOpen() function. + // If the "%subdevice" suffix is not used, the J2534 interface library + // will choose a default or ask the user. + const int splitPos = m_deviceName.indexOf(QChar::fromLatin1('%')); + const QString adapter = m_deviceName.left(splitPos); + QByteArray subDev; + + if (splitPos >= 0) + subDev = m_deviceName.midRef(splitPos + 1).toLatin1(); + + const QString library = libraryForAdapter(adapter); + if (library.isEmpty()) { + setError(tr("Adapter not found: %1").arg(adapter), ConnectionError); + return false; + } + bool ok = false; + uint bitRate = configurationParameter(BitRateKey).toUInt(&ok); + if (!ok) { + bitRate = 500*1000; // default initial bit rate + setConfigurationParameter(BitRateKey, bitRate); + } + m_ioThread.start(); + + return QMetaObject::invokeMethod(m_canIO, "open", Qt::QueuedConnection, + Q_ARG(QString, library), + Q_ARG(QByteArray, subDev), + Q_ARG(uint, bitRate)); +} + +void PassThruCanBackend::close() +{ + if (Q_UNLIKELY(state() != ClosingState)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Unexpected state on close"); + return; + } + QMetaObject::invokeMethod(m_canIO, "close", Qt::QueuedConnection); +} + +void PassThruCanBackend::ackOpenFinished(bool success) +{ + // Do not transition to connected state if close() has been called + // in the meantime. + if (state() != ConnectingState) + return; + + if (success) { + const QVariant loopback = configurationParameter(LoopbackKey); + if (loopback.toBool()) + applyConfig(LoopbackKey, loopback); + + QVariant filters = configurationParameter(RawFilterKey); + if (!filters.isValid()) { + // Configure default match-all filter. + filters = QVariant::fromValue(QList<Filter>{Filter{}}); + setConfigurationParameter(RawFilterKey, filters); + } + applyConfig(RawFilterKey, filters); + + QMetaObject::invokeMethod(m_canIO, "listen", Qt::QueuedConnection); + + setState(ConnectedState); + } else { + setState(UnconnectedState); + } +} + +void PassThruCanBackend::ackCloseFinished() +{ + m_ioThread.exit(0); + m_ioThread.wait(); + + setState(UnconnectedState); +} + +void PassThruCanBackend::applyConfig(int key, const QVariant &value) +{ + QMetaObject::invokeMethod(m_canIO, "applyConfig", Qt::QueuedConnection, + Q_ARG(int, key), Q_ARG(QVariant, value)); +} diff --git a/src/plugins/canbus/passthrucan/passthrucanbackend.h b/src/plugins/canbus/passthrucan/passthrucanbackend.h new file mode 100644 index 0000000..864d1b4 --- /dev/null +++ b/src/plugins/canbus/passthrucan/passthrucanbackend.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Ford Motor Company. +** 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 PASSTHRUCAN_PASSTHRUCANBACKEND_H +#define PASSTHRUCAN_PASSTHRUCANBACKEND_H + +#include <QtSerialBus/qcanbusdevice.h> +#include <QtSerialBus/qcanbusframe.h> + +#include <QString> +#include <QThread> +#include <QVector> + +QT_BEGIN_NAMESPACE + +class PassThruCanIO; + +class PassThruCanBackend : public QCanBusDevice +{ + Q_OBJECT + Q_DISABLE_COPY(PassThruCanBackend) +public: + explicit PassThruCanBackend(const QString &name, QObject *parent = nullptr); + virtual ~PassThruCanBackend(); + + void setConfigurationParameter(int key, const QVariant &value) override; + bool writeFrame(const QCanBusFrame &frame) override; + QString interpretErrorFrame(const QCanBusFrame &errorFrame) override; + + static QList<QCanBusDeviceInfo> interfaces(); + +protected: + bool open() override; + void close() override; + +private: + void ackOpenFinished(bool success); + void ackCloseFinished(); + void applyConfig(int key, const QVariant &value); + + QString m_deviceName; + QThread m_ioThread; + PassThruCanIO * m_canIO; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QCanBusDevice::CanBusError) +Q_DECLARE_METATYPE(QVector<QCanBusFrame>) + +#endif // PASSTHRUCAN_PASSTHRUCANBACKEND_H diff --git a/src/plugins/canbus/passthrucan/passthrucanio.cpp b/src/plugins/canbus/passthrucan/passthrucanio.cpp new file mode 100644 index 0000000..dc3d2c7 --- /dev/null +++ b/src/plugins/canbus/passthrucan/passthrucanio.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Ford Motor Company. +** 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 "passthrucanio.h" + +#include <QLoggingCategory> +#include <QtEndian> + +#include <cstring> + +PassThruCanIO::PassThruCanIO(QObject *parent) + : QObject(parent) + , m_ioBuffer (8, J2534::Message(J2534::Protocol::CAN)) +{ +} + +PassThruCanIO::~PassThruCanIO() +{ +} + +void PassThruCanIO::open(const QString &library, const QByteArray &subDev, uint bitRate) +{ + if (Q_UNLIKELY(m_passThru)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface already open"); + emit openFinished(false); + return; + } + qCDebug(QT_CANBUS_PLUGINS_PASSTHRU, "Loading interface library: %ls", + qUtf16Printable(library)); + + m_passThru = new J2534::PassThru(library, this); + J2534::PassThru::Status openStatus = m_passThru->lastError(); + + if (openStatus == J2534::PassThru::NoError) + openStatus = m_passThru->open(subDev, &m_deviceId); + + if (openStatus == J2534::PassThru::NoError + && m_passThru->connect(m_deviceId, J2534::Protocol::CAN, + J2534::PassThru::CAN29BitID | J2534::PassThru::CANIDBoth, + bitRate, &m_channelId) == J2534::PassThru::NoError) { + emit openFinished(true); + return; + } + emit errorOccurred(m_passThru->lastErrorString(), + QCanBusDevice::ConnectionError); + + if (openStatus == J2534::PassThru::NoError + && m_passThru->close(m_deviceId) != J2534::PassThru::NoError) + qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "Failed to close pass-thru device"); + + delete m_passThru; + m_passThru = nullptr; + + emit openFinished(false); +} + +void PassThruCanIO::close() +{ + if (Q_LIKELY(m_passThru)) { + delete m_idleNotifier; + m_idleNotifier = nullptr; + + if (m_passThru->disconnect(m_channelId) != J2534::PassThru::NoError + || m_passThru->close(m_deviceId) != J2534::PassThru::NoError) { + + qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, "Failed to close pass-thru device"); + emit errorOccurred(m_passThru->lastErrorString(), + QCanBusDevice::ConnectionError); + } + delete m_passThru; + m_passThru = nullptr; + } + emit closeFinished(); +} + +void PassThruCanIO::applyConfig(int key, const QVariant &value) +{ + if (Q_UNLIKELY(!m_passThru)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface not open"); + return; + } + bool success = true; + + switch (key) { + case QCanBusDevice::RawFilterKey: + success = setMessageFilters(qvariant_cast<QList<QCanBusDevice::Filter>>(value)); + break; + case QCanBusDevice::LoopbackKey: + success = setConfigValue(J2534::Config::Loopback, value.toBool()); + break; + case QCanBusDevice::BitRateKey: + success = setConfigValue(J2534::Config::DataRate, value.toUInt()); + break; + default: + emit errorOccurred(tr("Unsupported configuration key: %1").arg(key), + QCanBusDevice::ConfigurationError); + break; + } + if (!success) { + emit errorOccurred(tr("Configuration failed: %1").arg(m_passThru->lastErrorString()), + QCanBusDevice::ConfigurationError); + } +} + +void PassThruCanIO::listen() +{ + if (Q_UNLIKELY(!m_passThru)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface not open"); + return; + } + if (Q_UNLIKELY(m_idleNotifier)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Idle notifier already created"); + return; + } + m_idleNotifier = new QTimer(this); + connect(m_idleNotifier, &QTimer::timeout, this, &PassThruCanIO::pollForMessages); + + m_idleNotifier->start(0); +} + +bool PassThruCanIO::enqueueMessage(const QCanBusFrame &frame) +{ + const QMutexLocker lock (&m_writeGuard); + m_writeQueue.append(frame); + return true; +} + +bool PassThruCanIO::setMessageFilters(const QList<QCanBusDevice::Filter> &filters) +{ + if (m_passThru->clear(m_channelId, J2534::PassThru::MsgFilters) != J2534::PassThru::NoError) + return false; + + J2534::Message pattern {J2534::Protocol::CAN}; + pattern.setSize(4); + J2534::Message mask {J2534::Protocol::CAN}; + mask.setSize(4); + + for (const auto &filter : filters) { + if (filter.type != QCanBusFrame::DataFrame + && filter.type != QCanBusFrame::InvalidFrame) { + emit errorOccurred(tr("Configuration failed: unsupported filter type"), + QCanBusDevice::ConfigurationError); + break; + } + if (filter.format & QCanBusDevice::Filter::MatchExtendedFormat) + pattern.setRxStatus(J2534::Message::InCAN29BitID); + else + pattern.setRxStatus({}); + + if (filter.format != QCanBusDevice::Filter::MatchBaseAndExtendedFormat) + mask.setRxStatus(J2534::Message::InCAN29BitID); + else + mask.setRxStatus({}); + + qToBigEndian<quint32>(filter.frameId & filter.frameIdMask, pattern.data()); + qToBigEndian<quint32>(filter.frameIdMask, mask.data()); + + if (m_passThru->startMsgFilter(m_channelId, J2534::PassThru::PassFilter, + mask, pattern) != J2534::PassThru::NoError) + return false; + } + return true; +} + +bool PassThruCanIO::setConfigValue(J2534::Config::Parameter param, ulong value) +{ + const J2534::Config config {param, value}; + + return (m_passThru->setConfig(m_channelId, &config) == J2534::PassThru::NoError); +} + +void PassThruCanIO::pollForMessages() +{ + if (Q_UNLIKELY(!m_passThru)) { + qCCritical(QT_CANBUS_PLUGINS_PASSTHRU, "Pass-thru interface not open"); + return; + } + const bool writePending = writeMessages(); + readMessages(writePending); +} + +bool PassThruCanIO::writeMessages() +{ + ulong numMsgs = m_ioBuffer.size(); + { + const QMutexLocker lock (&m_writeGuard); + numMsgs = qMin<ulong>(m_writeQueue.size(), numMsgs); + + for (ulong i = 0; i < numMsgs; ++i) { + const QCanBusFrame &frame = m_writeQueue.at(i); + J2534::Message &msg = m_ioBuffer[i]; + + const QByteArray payload = frame.payload(); + const ulong payloadSize = qMin<ulong>(payload.size(), + J2534::Message::maxSize - 4); + msg.setRxStatus({}); + msg.setTimestamp(0); + msg.setSize(4 + payloadSize); + msg.setExtraDataIndex(0); + + if (frame.hasExtendedFrameFormat()) + msg.setTxFlags(J2534::Message::OutCAN29BitID); + else + msg.setTxFlags({}); + + qToBigEndian<quint32>(frame.frameId(), msg.data()); + std::memcpy(msg.data() + 4, payload.data(), payloadSize); + } + } + if (numMsgs == 0) + return false; + + const auto status = m_passThru->writeMsgs(m_channelId, m_ioBuffer.constData(), + &numMsgs, pollTimeout); + if (status == J2534::PassThru::BufferFull) + return false; + + if (status != J2534::PassThru::NoError && status != J2534::PassThru::Timeout) { + emit errorOccurred(tr("Message write failed: %1").arg(m_passThru->lastErrorString()), + QCanBusDevice::WriteError); + return false; + } + if (numMsgs == 0) + return false; + + bool morePending; + { + const QMutexLocker lock (&m_writeGuard); + // De-queue successfully written frames. + m_writeQueue.erase(m_writeQueue.begin(), m_writeQueue.begin() + numMsgs); + morePending = !m_writeQueue.isEmpty(); + } + emit messagesSent(numMsgs); + + return morePending; +} + +void PassThruCanIO::readMessages(bool writePending) +{ + // If there are outgoing messages waiting to be written, just check + // for already received messages but do not block waiting for more. + const uint timeout = (writePending) ? 0 : pollTimeout; + + ulong numMsgs = m_ioBuffer.size(); + const auto status = m_passThru->readMsgs(m_channelId, m_ioBuffer.data(), + &numMsgs, timeout); + if (status == J2534::PassThru::BufferEmpty) + return; + + if (status != J2534::PassThru::NoError && status != J2534::PassThru::Timeout) { + emit errorOccurred(tr("Message read failed: %1").arg(m_passThru->lastErrorString()), + QCanBusDevice::ReadError); + if (status != J2534::PassThru::BufferOverflow) + return; + } + const int numFrames = qMin<ulong>(m_ioBuffer.size(), numMsgs); + QVector<QCanBusFrame> frames; + frames.reserve(numFrames); + + for (int i = 0; i < numFrames; ++i) { + const J2534::Message &msg = m_ioBuffer.at(i); + if (Q_UNLIKELY(msg.size() < 4) + || Q_UNLIKELY(msg.size() > J2534::Message::maxSize)) { + // This normally shouldn't happen, so a log message is appropriate. + qCWarning(QT_CANBUS_PLUGINS_PASSTHRU, + "Message with invalid size %lu received", msg.size()); + continue; + } + const quint32 msgId = qFromBigEndian<quint32>(msg.data()); + const QByteArray payload (msg.data() + 4, msg.size() - 4); + + QCanBusFrame frame (msgId, payload); + frame.setExtendedFrameFormat((msg.rxStatus() & J2534::Message::InCAN29BitID) != 0); + frame.setLocalEcho((msg.rxStatus() & J2534::Message::InTxMsgType) != 0); + frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(msg.timestamp())); + + frames.append(std::move(frame)); + } + if (!frames.isEmpty()) + emit messagesReceived(std::move(frames)); +} diff --git a/src/plugins/canbus/passthrucan/passthrucanio.h b/src/plugins/canbus/passthrucan/passthrucanio.h new file mode 100644 index 0000000..071174c --- /dev/null +++ b/src/plugins/canbus/passthrucan/passthrucanio.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Ford Motor Company. +** 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 PASSTHRUCAN_PASSTHRUCANIO_H +#define PASSTHRUCAN_PASSTHRUCANIO_H + +#include "j2534passthru.h" + +#include <QtSerialBus/qcanbusdevice.h> +#include <QtSerialBus/qcanbusframe.h> +#include <QByteArray> +#include <QList> +#include <QMutex> +#include <QObject> +#include <QString> +#include <QTimer> +#include <QVariant> +#include <QVector> + +QT_BEGIN_NAMESPACE + +class PassThruCanIO : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(PassThruCanIO) +public: + static const uint pollTimeout = 100; // ms + + explicit PassThruCanIO(QObject *parent = nullptr); + virtual ~PassThruCanIO(); + + Q_INVOKABLE void open(const QString &library, const QByteArray &subDev, uint bitRate); + Q_INVOKABLE void close(); + Q_INVOKABLE void applyConfig(int key, const QVariant &value); + Q_INVOKABLE void listen(); + + // Internally locked; safe to call directly from any thread. + bool enqueueMessage(const QCanBusFrame &frame); + +Q_SIGNALS: + void errorOccurred(const QString &description, QCanBusDevice::CanBusError error); + void messagesReceived(QVector<QCanBusFrame> frames); + void messagesSent(qint64 count); + void openFinished(bool success); + void closeFinished(); + +private: + bool setMessageFilters(const QList<QCanBusDevice::Filter> &filters); + bool setConfigValue(J2534::Config::Parameter param, ulong value); + void pollForMessages(); + bool writeMessages(); + void readMessages(bool writePending); + + J2534::PassThru * m_passThru = nullptr; + J2534::PassThru::Handle m_deviceId = 0; + J2534::PassThru::Handle m_channelId = 0; + QTimer * m_idleNotifier = nullptr; + QVector<J2534::Message> m_ioBuffer; + QMutex m_writeGuard; + QList<QCanBusFrame> m_writeQueue; +}; + +QT_END_NAMESPACE + +#endif // PASSTHRUCAN_PASSTHRUCANIO_H diff --git a/src/plugins/canbus/passthrucan/plugin.json b/src/plugins/canbus/passthrucan/plugin.json new file mode 100644 index 0000000..2026547 --- /dev/null +++ b/src/plugins/canbus/passthrucan/plugin.json @@ -0,0 +1,3 @@ +{ + "Key": "passthrucan" +} diff --git a/src/plugins/canbus/peakcan/main.cpp b/src/plugins/canbus/peakcan/main.cpp index 7444f6d..8ab1b99 100644 --- a/src/plugins/canbus/peakcan/main.cpp +++ b/src/plugins/canbus/peakcan/main.cpp @@ -41,8 +41,12 @@ #include <QtSerialBus/qcanbusdevice.h> #include <QtSerialBus/qcanbusfactory.h> +#include <QtCore/qloggingcategory.h> + QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PEAKCAN, "qt.canbus.plugins.peakcan") + class PeakCanBusPlugin : public QObject, public QCanBusFactoryV2 { Q_OBJECT @@ -62,7 +66,7 @@ public: { QString errorReason; if (!PeakCanBackend::canCreate(&errorReason)) { - qWarning("%ls", qUtf16Printable(errorReason)); + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "%ls", qUtf16Printable(errorReason)); if (errorMessage) *errorMessage = errorReason; return nullptr; diff --git a/src/plugins/canbus/peakcan/peakcan_symbols_p.h b/src/plugins/canbus/peakcan/peakcan_symbols_p.h index 7c91750..d94a7ec 100644 --- a/src/plugins/canbus/peakcan/peakcan_symbols_p.h +++ b/src/plugins/canbus/peakcan/peakcan_symbols_p.h @@ -49,24 +49,15 @@ // We mean it. // -#ifdef LINK_LIBPCANBASIC - -extern "C" -{ -#include <pcanbasic.h> -} - -#else - #include <QtCore/qlibrary.h> #include <QtCore/qstring.h> #include <QtCore/qdebug.h> #ifdef Q_OS_WIN32 -#include <windows.h> -#define DRV_CALLBACK_TYPE WINAPI +# include <windows.h> +# define DRV_CALLBACK_TYPE WINAPI #else -#define DRV_CALLBACK_TYPE +# define DRV_CALLBACK_TYPE #endif // Currently defined and supported PCAN channels @@ -234,6 +225,7 @@ extern "C" #define PCAN_BAUD_20K 0x532F // 20 kBit/s #define PCAN_BAUD_10K 0x672F // 10 kBit/s #define PCAN_BAUD_5K 0x7F7F // 5 kBit/s +#define PCAN_BAUD_INVALID 0xFFFF // unknown or invalid baudrate #define PCAN_TYPE_ISA 0x01 // PCAN-ISA 82C200 #define PCAN_TYPE_ISA_SJA 0x09 // PCAN-ISA SJA1000 @@ -277,7 +269,7 @@ typedef struct tagTPCANTimestamp static fp_##symbolName symbolName; #define RESOLVE_SYMBOL(symbolName) \ - symbolName = (fp_##symbolName)pcanLibrary->resolve(#symbolName); \ + symbolName = reinterpret_cast<fp_##symbolName>(pcanLibrary->resolve(#symbolName)); \ if (!symbolName) \ return false; @@ -314,6 +306,4 @@ inline bool resolveSymbols(QLibrary *pcanLibrary) return true; } -#endif - #endif // PEAKCAN_SYMBOLS_P_H diff --git a/src/plugins/canbus/peakcan/peakcanbackend.cpp b/src/plugins/canbus/peakcan/peakcanbackend.cpp index 8f97f5d..7e6fe43 100644 --- a/src/plugins/canbus/peakcan/peakcanbackend.cpp +++ b/src/plugins/canbus/peakcan/peakcanbackend.cpp @@ -43,17 +43,20 @@ #include <QtCore/qtimer.h> #include <QtCore/qcoreevent.h> +#include <QtCore/qloggingcategory.h> #include <algorithm> #ifdef Q_OS_WIN32 -#include <QtCore/qwineventnotifier.h> +# include <QtCore/qwineventnotifier.h> #else -#include <QtCore/qsocketnotifier.h> +# include <QtCore/qsocketnotifier.h> #endif QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_PEAKCAN) + #ifndef LINK_LIBPCANBASIC Q_GLOBAL_STATIC(QLibrary, pcanLibrary) #endif @@ -76,7 +79,7 @@ struct PcanChannel{ char name[6]; TPCANHandle index; }; -PcanChannel pcanChannels[] = { +static const PcanChannel pcanChannels[] = { { "usb0", PCAN_USBBUS1 }, { "usb1", PCAN_USBBUS2 }, { "usb2", PCAN_USBBUS3 }, @@ -117,14 +120,30 @@ QList<QCanBusDeviceInfo> PeakCanBackend::interfaces() QList<QCanBusDeviceInfo> result; for (int i = 0; pcanChannels[i].index != PCAN_NONEBUS; ++i) { - int value; - const TPCANStatus stat = ::CAN_GetValue(pcanChannels[i].index, PCAN_CHANNEL_CONDITION, + int value = 0; + const TPCANHandle index = pcanChannels[i].index; + const TPCANStatus stat = ::CAN_GetValue(index, PCAN_CHANNEL_CONDITION, &value, sizeof(value)); if ((stat == PCAN_ERROR_OK) && (value & PCAN_CHANNEL_AVAILABLE)) { - const TPCANStatus fdStat = ::CAN_GetValue(pcanChannels[i].index, PCAN_CHANNEL_FEATURES, + const TPCANStatus fdStat = ::CAN_GetValue(index, PCAN_CHANNEL_FEATURES, &value, sizeof(value)); const bool isFd = (fdStat == PCAN_ERROR_OK) && (value & FEATURE_FD_CAPABLE); - result.append(createDeviceInfo(QLatin1String(pcanChannels[i].name), false, isFd)); + + char description[256] = {0}; + const TPCANStatus descStat = ::CAN_GetValue(index, PCAN_HARDWARE_NAME, + description, sizeof(description)); + if (descStat != PCAN_ERROR_OK) + description[0] = 0; + + int channel = 0; + const TPCANStatus chnStat = ::CAN_GetValue(index, PCAN_CONTROLLER_NUMBER, + &channel, sizeof(channel)); + if (chnStat != PCAN_ERROR_OK) + channel = 0; + + result.append(std::move(createDeviceInfo(QLatin1String(pcanChannels[i].name), + QString(), QLatin1String(description), + channel, false, isFd))); } } @@ -154,7 +173,7 @@ protected: } private: - PeakCanBackendPrivate *dptr; + PeakCanBackendPrivate * const dptr; }; #else class ReadNotifier : public QSocketNotifier @@ -178,7 +197,7 @@ protected: } private: - PeakCanBackendPrivate *dptr; + PeakCanBackendPrivate * const dptr; }; #endif @@ -203,7 +222,7 @@ protected: } private: - PeakCanBackendPrivate *dptr; + PeakCanBackendPrivate * const dptr; }; PeakCanBackendPrivate::PeakCanBackendPrivate(PeakCanBackend *q) @@ -214,7 +233,7 @@ PeakCanBackendPrivate::PeakCanBackendPrivate(PeakCanBackend *q) struct BitrateItem { int bitrate; - int code; + TPCANBaudrate code; }; struct BitrateLessFunctor @@ -225,7 +244,7 @@ struct BitrateLessFunctor } }; -static int bitrateCodeFromBitrate(int bitrate) +static TPCANBaudrate bitrateCodeFromBitrate(int bitrate) { static const BitrateItem bitratetable[] = { { 5000, PCAN_BAUD_5K }, @@ -248,7 +267,7 @@ static int bitrateCodeFromBitrate(int bitrate) const BitrateItem item = { bitrate , 0 }; const BitrateItem *where = std::lower_bound(bitratetable, endtable, item, BitrateLessFunctor()); - return where != endtable ? where->code : -1; + return where != endtable ? where->code : PCAN_BAUD_INVALID; } bool PeakCanBackendPrivate::open() @@ -256,7 +275,7 @@ bool PeakCanBackendPrivate::open() Q_Q(PeakCanBackend); const int bitrate = q->configurationParameter(QCanBusDevice::BitRateKey).toInt(); - const int bitrateCode = bitrateCodeFromBitrate(bitrate); + const TPCANBaudrate bitrateCode = bitrateCodeFromBitrate(bitrate); const TPCANStatus st = ::CAN_Initialize(channelIndex, bitrateCode, 0, 0, 0); if (Q_UNLIKELY(st != PCAN_ERROR_OK)) { @@ -359,7 +378,7 @@ void PeakCanBackendPrivate::setupDefaultConfigurations() q->setConfigurationParameter(QCanBusDevice::BitRateKey, 500000); } -QString PeakCanBackendPrivate::systemErrorString(int errorCode) +QString PeakCanBackendPrivate::systemErrorString(TPCANStatus errorCode) { QByteArray buffer(256, 0); if (Q_UNLIKELY(::CAN_GetErrorText(errorCode, 0, buffer.data()) != PCAN_ERROR_OK)) @@ -383,7 +402,7 @@ void PeakCanBackendPrivate::startWrite() ::memset(&message, 0, sizeof(message)); message.ID = frame.frameId(); - message.LEN = payload.size(); + message.LEN = static_cast<quint8>(payload.size()); message.MSGTYPE = frame.hasExtendedFrameFormat() ? PCAN_MESSAGE_EXTENDED : PCAN_MESSAGE_STANDARD; if (frame.frameType() == QCanBusFrame::RemoteRequestFrame) @@ -428,7 +447,7 @@ void PeakCanBackendPrivate::startRead() QCanBusFrame frame(message.ID, QByteArray(reinterpret_cast<const char *>(message.DATA), int(message.LEN))); const quint64 millis = timestamp.millis + Q_UINT64_C(0xFFFFFFFF) * timestamp.millis_overflow; const quint64 micros = Q_UINT64_C(1000) * millis + timestamp.micros; - frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(micros)); + frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(static_cast<qint64>(micros))); frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED); frame.setFrameType((message.MSGTYPE & PCAN_MESSAGE_RTR) ? QCanBusFrame::RemoteRequestFrame : QCanBusFrame::DataFrame); @@ -448,7 +467,7 @@ bool PeakCanBackendPrivate::verifyBitRate(int bitrate) return false; } - if (Q_UNLIKELY(bitrateCodeFromBitrate(bitrate) == -1)) { + if (Q_UNLIKELY(bitrateCodeFromBitrate(bitrate) == PCAN_BAUD_INVALID)) { q->setError(PeakCanBackend::tr("Unsupported bitrate value"), QCanBusDevice::ConfigurationError); return false; @@ -494,8 +513,8 @@ bool PeakCanBackend::open() const QVariant param = configurationParameter(key); const bool success = d->setConfigurationParameter(key, param); if (Q_UNLIKELY(!success)) { - qWarning("Cannot apply parameter: %d with value: %ls.", - key, qUtf16Printable(param.toString())); + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot apply parameter: %d with value: %ls.", + key, qUtf16Printable(param.toString())); } } } @@ -541,7 +560,7 @@ bool PeakCanBackend::writeFrame(const QCanBusFrame &newData) } // CAN FD frame format not implemented at this stage - if (Q_UNLIKELY(newData.payload().size() > 8)) { + if (Q_UNLIKELY(newData.hasFlexibleDataRateFormat())) { setError(tr("CAN FD frame format not supported."), QCanBusDevice::WriteError); return false; } diff --git a/src/plugins/canbus/peakcan/peakcanbackend_p.h b/src/plugins/canbus/peakcan/peakcanbackend_p.h index a6c6458..2dc8197 100644 --- a/src/plugins/canbus/peakcan/peakcanbackend_p.h +++ b/src/plugins/canbus/peakcan/peakcanbackend_p.h @@ -75,7 +75,7 @@ public: bool setConfigurationParameter(int key, const QVariant &value); void setupChannel(const QByteArray &interfaceName); void setupDefaultConfigurations(); - QString systemErrorString(int errorCode); + QString systemErrorString(TPCANStatus errorCode); void startWrite(); void startRead(); bool verifyBitRate(int bitrate); @@ -83,7 +83,7 @@ public: PeakCanBackend * const q_ptr; bool isOpen = false; - int channelIndex = PCAN_NONEBUS; + TPCANHandle channelIndex = PCAN_NONEBUS; QTimer *writeNotifier = nullptr; #if defined(Q_OS_WIN32) diff --git a/src/plugins/canbus/socketcan/main.cpp b/src/plugins/canbus/socketcan/main.cpp index 92e759b..328db97 100644 --- a/src/plugins/canbus/socketcan/main.cpp +++ b/src/plugins/canbus/socketcan/main.cpp @@ -40,10 +40,12 @@ #include <QtSerialBus/qcanbusdevice.h> #include <QtSerialBus/qcanbusfactory.h> -#include <QtCore/qfile.h> +#include <QtCore/qloggingcategory.h> QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_SOCKETCAN, "qt.canbus.plugins.socketcan") + //! [SocketCanFactory] class SocketCanBusPlugin : public QObject, public QCanBusFactoryV2 { diff --git a/src/plugins/canbus/socketcan/socketcanbackend.cpp b/src/plugins/canbus/socketcan/socketcanbackend.cpp index c890fff..74b0d1d 100644 --- a/src/plugins/canbus/socketcan/socketcanbackend.cpp +++ b/src/plugins/canbus/socketcan/socketcanbackend.cpp @@ -40,6 +40,7 @@ #include <QtCore/qdebug.h> #include <QtCore/qdiriterator.h> #include <QtCore/qfile.h> +#include <QtCore/qloggingcategory.h> #include <QtCore/qsocketnotifier.h> #include <linux/can/error.h> @@ -83,7 +84,11 @@ struct canfd_frame { QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_SOCKETCAN) + const char sysClassNetC[] = "/sys/class/net/"; +const char interfaceC[] = "/device/interface"; +const char devIdC[] = "/dev_id"; const char flagsC[] = "/flags"; const char mtuC[] = "/mtu"; const char typeC[] = "/type"; @@ -124,6 +129,23 @@ static quint32 flags(const QString &canDevice) return result; } +static QString deviceDescription(const QString &canDevice) +{ + const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(interfaceC); + const QByteArray content = fileContent(path); + if (content.isEmpty() && isVirtual(canDevice)) + return QStringLiteral("Virtual CAN"); + + return QString::fromUtf8(content); +} + +static int deviceChannel(const QString &canDevice) +{ + const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(devIdC); + const QByteArray content = fileContent(path); + return content.toInt(nullptr, 0); +} + QList<QCanBusDeviceInfo> SocketCanBackend::interfaces() { QList<QCanBusDeviceInfo> result; @@ -140,9 +162,12 @@ QList<QCanBusDeviceInfo> SocketCanBackend::interfaces() if (!(flags(deviceName) & DeviceIsActive)) continue; - auto info = createDeviceInfo(deviceName, isVirtual(deviceName), - isFlexibleDataRateCapable(deviceName)); - result.append(info); + const QString serial; + const QString description = deviceDescription(deviceName); + const int channel = deviceChannel(deviceName); + result.append(std::move(createDeviceInfo(deviceName, serial, description, + channel, isVirtual(deviceName), + isFlexibleDataRateCapable(deviceName)))); } std::sort(result.begin(), result.end(), @@ -249,7 +274,7 @@ bool SocketCanBackend::applyConfigurationParameter(int key, const QVariant &valu socklen_t s = sizeof(can_filter); if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FILTER, &filters, s) != 0)) { - qWarning("Cannot unset socket filters"); + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Cannot unset socket filters."); setError(qt_error_string(errno), QCanBusDevice::CanBusError::ConfigurationError); break; @@ -373,8 +398,8 @@ bool SocketCanBackend::connectSocket() const QVariant param = configurationParameter(key); bool success = applyConfigurationParameter(key, param); if (Q_UNLIKELY(!success)) { - qWarning("Cannot apply parameter: %d with value: %ls", - key, qUtf16Printable(param.toString())); + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Cannot apply parameter: %d with value: %ls.", + key, qUtf16Printable(param.toString())); } } @@ -443,7 +468,7 @@ bool SocketCanBackend::writeFrame(const QCanBusFrame &newData) if (Q_UNLIKELY(!canFdOptionEnabled && newData.hasFlexibleDataRateFormat())) { const QString error = tr("Cannot write CAN FD frame because CAN FD option is not enabled."); - qDebug("%ls", qUtf16Printable(error)); + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(error)); setError(error, QCanBusDevice::WriteError); return false; } diff --git a/src/plugins/canbus/systeccan/main.cpp b/src/plugins/canbus/systeccan/main.cpp index fcb76a3..89377a0 100644 --- a/src/plugins/canbus/systeccan/main.cpp +++ b/src/plugins/canbus/systeccan/main.cpp @@ -40,8 +40,12 @@ #include <QtSerialBus/qcanbusdevice.h> #include <QtSerialBus/qcanbusfactory.h> +#include <QtCore/qloggingcategory.h> + QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_SYSTECCAN, "qt.canbus.plugins.systeccan") + class SystecCanBusPlugin : public QObject, public QCanBusFactoryV2 { Q_OBJECT @@ -61,7 +65,7 @@ public: { QString errorReason; if (Q_UNLIKELY(!SystecCanBackend::canCreate(&errorReason))) { - qWarning("%ls", qUtf16Printable(errorReason)); + qCWarning(QT_CANBUS_PLUGINS_SYSTECCAN, "%ls", qUtf16Printable(errorReason)); if (errorMessage) *errorMessage = errorReason; return nullptr; diff --git a/src/plugins/canbus/systeccan/systeccan_symbols_p.h b/src/plugins/canbus/systeccan/systeccan_symbols_p.h index b69db83..102817c 100644 --- a/src/plugins/canbus/systeccan/systeccan_symbols_p.h +++ b/src/plugins/canbus/systeccan/systeccan_symbols_p.h @@ -62,7 +62,7 @@ typedef quint8 tUcanHandle; static fp_##symbolName symbolName; #define RESOLVE_SYMBOL(symbolName) \ - symbolName = (fp_##symbolName)systecLibrary->resolve(#symbolName); \ + symbolName = reinterpret_cast<fp_##symbolName>(systecLibrary->resolve(#symbolName)); \ if (!symbolName) \ return false; diff --git a/src/plugins/canbus/systeccan/systeccanbackend.cpp b/src/plugins/canbus/systeccan/systeccanbackend.cpp index 996122d..7041af8 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend.cpp +++ b/src/plugins/canbus/systeccan/systeccanbackend.cpp @@ -43,11 +43,14 @@ #include <QtCore/qcoreapplication.h> #include <QtCore/qcoreevent.h> #include <QtCore/qdebug.h> +#include <QtCore/qloggingcategory.h> #include <QtCore/qregularexpression.h> #include <QtCore/qtimer.h> QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_SYSTECCAN) + Q_GLOBAL_STATIC(QLibrary, systecLibrary) bool SystecCanBackend::canCreate(QString *errorReason) @@ -60,19 +63,49 @@ bool SystecCanBackend::canCreate(QString *errorReason) return true; } +QCanBusDeviceInfo SystecCanBackend::createDeviceInfo(const QString &serialNumber, + const QString &description, + uint deviceNumber, + int channelNumber) +{ + const QString name = QString::fromLatin1("can%1.%2").arg(deviceNumber).arg(channelNumber); + return QCanBusDevice::createDeviceInfo(name, serialNumber, description, channelNumber, false, false); +} + +static QString descriptionString(uint productCode) +{ + switch (productCode & USBCAN_PRODCODE_MASK_PID) { + case USBCAN_PRODCODE_PID_GW001: return QStringLiteral("USB-CANmodul (G1)"); + case USBCAN_PRODCODE_PID_GW002: return QStringLiteral("USB-CANmodul (G2)"); + case USBCAN_PRODCODE_PID_MULTIPORT: return QStringLiteral("Multiport CAN-to-USB (G3)"); + case USBCAN_PRODCODE_PID_BASIC: return QStringLiteral("USB-CANmodul1 (G3)"); + case USBCAN_PRODCODE_PID_ADVANCED: return QStringLiteral("USB-CANmodul2 (G3)"); + case USBCAN_PRODCODE_PID_USBCAN8: return QStringLiteral("USB-CANmodul8 (G3)"); + case USBCAN_PRODCODE_PID_USBCAN16: return QStringLiteral("USB-CANmodul16 (G3)"); + case USBCAN_PRODCODE_PID_ADVANCED_G4: return QStringLiteral("USB-CANmodul2 (G4)"); + case USBCAN_PRODCODE_PID_BASIC_G4: return QStringLiteral("USB-CANmodul1 (G4)"); + default: return QStringLiteral("Unknown"); + } +} + static void DRV_CALLBACK_TYPE ucanEnumCallback(DWORD index, BOOL isUsed, tUcanHardwareInfoEx *hardwareInfo, tUcanHardwareInitInfo *initInfo, void *args) { - auto result = static_cast<QStringList *>(args); + auto result = static_cast<QList<QCanBusDeviceInfo> *>(args); Q_UNUSED(index); Q_UNUSED(isUsed); - result->append(QString::fromLatin1("can%1.0").arg(hardwareInfo->m_bDeviceNr)); - if (USBCAN_CHECK_SUPPORT_TWO_CHANNEL(hardwareInfo)) - result->append(QString::fromLatin1("can%1.1").arg(hardwareInfo->m_bDeviceNr)); + const QString serialNumber = QString::number(hardwareInfo->m_dwSerialNr); + const QString description = descriptionString(hardwareInfo->m_dwProductCode); + result->append(std::move(SystecCanBackend::createDeviceInfo(serialNumber, description, + hardwareInfo->m_bDeviceNr, 0))); + if (USBCAN_CHECK_SUPPORT_TWO_CHANNEL(hardwareInfo)) { + result->append(std::move(SystecCanBackend::createDeviceInfo(serialNumber, description, + hardwareInfo->m_bDeviceNr, 1))); + } initInfo->m_fTryNext = true; // continue enumerating with next device } @@ -81,12 +114,8 @@ QList<QCanBusDeviceInfo> SystecCanBackend::interfaces() { QList<QCanBusDeviceInfo> result; - QStringList devices; - ::UcanEnumerateHardware(&ucanEnumCallback, &devices, false, - 0, ~0, 0, ~0, 0, ~0); + ::UcanEnumerateHardware(&ucanEnumCallback, &result, false, 0, ~0, 0, ~0, 0, ~0); - for (const QString &s : qAsConst(devices)) - result.append(createDeviceInfo(s, false, false)); return result; } @@ -110,7 +139,7 @@ protected: } private: - SystecCanBackendPrivate *dptr; + SystecCanBackendPrivate * const dptr; }; SystecCanBackendPrivate::SystecCanBackendPrivate(SystecCanBackend *q) : @@ -459,8 +488,8 @@ bool SystecCanBackend::open() const QVariant param = configurationParameter(key); const bool success = d->setConfigurationParameter(key, param); if (Q_UNLIKELY(!success)) { - qWarning("Cannot apply parameter %d with value %ls.", - key, qUtf16Printable(param.toString())); + qCWarning(QT_CANBUS_PLUGINS_SYSTECCAN, "Cannot apply parameter %d with value %ls.", + key, qUtf16Printable(param.toString())); } } @@ -505,7 +534,7 @@ bool SystecCanBackend::writeFrame(const QCanBusFrame &newData) } // CAN FD frame format is not supported by the hardware yet - if (Q_UNLIKELY(newData.payload().size() > 8)) { + if (Q_UNLIKELY(newData.hasFlexibleDataRateFormat())) { setError(tr("CAN FD frame format not supported"), QCanBusDevice::WriteError); return false; } diff --git a/src/plugins/canbus/systeccan/systeccanbackend.h b/src/plugins/canbus/systeccan/systeccanbackend.h index 7bf20f8..cb62808 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend.h +++ b/src/plugins/canbus/systeccan/systeccanbackend.h @@ -69,6 +69,12 @@ public: static QList<QCanBusDeviceInfo> interfaces(); static bool canCreate(QString *errorReason); + // This function needs to be public as it is accessed by a callback + static QCanBusDeviceInfo createDeviceInfo(const QString &serialNumber, + const QString &description, + uint deviceNumber, + int channelNumber); + private: SystecCanBackendPrivate * const d_ptr; }; diff --git a/src/plugins/canbus/systeccan/systeccanbackend_p.h b/src/plugins/canbus/systeccan/systeccanbackend_p.h index f180102..28eb7ff 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend_p.h +++ b/src/plugins/canbus/systeccan/systeccanbackend_p.h @@ -73,7 +73,7 @@ public: void customEvent(QEvent *event); private: - SystecCanBackendPrivate *dptr; + SystecCanBackendPrivate * const dptr; }; class SystecCanBackendPrivate diff --git a/src/plugins/canbus/tinycan/main.cpp b/src/plugins/canbus/tinycan/main.cpp index c73d81e..36fb05d 100644 --- a/src/plugins/canbus/tinycan/main.cpp +++ b/src/plugins/canbus/tinycan/main.cpp @@ -41,8 +41,12 @@ #include <QtSerialBus/qcanbusdevice.h> #include <QtSerialBus/qcanbusfactory.h> +#include <QtCore/qloggingcategory.h> + QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_TINYCAN, "qt.canbus.plugins.tinycan") + class TinyCanBusPlugin : public QObject, public QCanBusFactoryV2 { Q_OBJECT @@ -60,7 +64,7 @@ public: { QString errorReason; if (!TinyCanBackend::canCreate(&errorReason)) { - qWarning("%ls", qUtf16Printable(errorReason)); + qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "%ls", qUtf16Printable(errorReason)); if (errorMessage) *errorMessage = errorReason; return nullptr; diff --git a/src/plugins/canbus/tinycan/tinycan_symbols_p.h b/src/plugins/canbus/tinycan/tinycan_symbols_p.h index 36580ed..44dffd6 100644 --- a/src/plugins/canbus/tinycan/tinycan_symbols_p.h +++ b/src/plugins/canbus/tinycan/tinycan_symbols_p.h @@ -285,7 +285,7 @@ typedef void (DRV_CALLBACK_TYPE *CanRxEventCallback)( static fp_##symbolName symbolName; #define RESOLVE_SYMBOL(symbolName) \ - symbolName = (fp_##symbolName)mhstcanLibrary->resolve(#symbolName); \ + symbolName = reinterpret_cast<fp_##symbolName>(mhstcanLibrary->resolve(#symbolName)); \ if (!symbolName) \ return false; diff --git a/src/plugins/canbus/tinycan/tinycanbackend.cpp b/src/plugins/canbus/tinycan/tinycanbackend.cpp index 68e8bb2..3891186 100644 --- a/src/plugins/canbus/tinycan/tinycanbackend.cpp +++ b/src/plugins/canbus/tinycan/tinycanbackend.cpp @@ -45,11 +45,14 @@ #include <QtCore/qtimer.h> #include <QtCore/qmutex.h> #include <QtCore/qcoreevent.h> +#include <QtCore/qloggingcategory.h> #include <algorithm> QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_TINYCAN) + #ifndef LINK_LIBMHSTCAN Q_GLOBAL_STATIC(QLibrary, mhstcanLibrary) #endif @@ -107,7 +110,7 @@ protected: } private: - TinyCanBackendPrivate *dptr; + TinyCanBackendPrivate * const dptr; }; static int driverRefCount = 0; @@ -353,7 +356,7 @@ void TinyCanBackendPrivate::startWrite() ::memset(&message, 0, sizeof(message)); if (Q_UNLIKELY(payload.size() > int(sizeof(message.Data.Bytes)))) { - qWarning("Can not write frame with payload size %d, ignored", payload.size()); + qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "Cannot write frame with payload size %d.", payload.size()); } else { message.Id = frame.frameId(); message.Flags.Flag.Len = payload.size(); @@ -401,7 +404,7 @@ void TinyCanBackendPrivate::startRead() q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ReadError); } else { if (status.CanStatus == CAN_STATUS_BUS_OFF) { - qWarning("CAN bus is in off state, trying to reset the bus"); + 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); } @@ -517,8 +520,8 @@ bool TinyCanBackend::open() const QVariant param = configurationParameter(key); const bool success = d->setConfigurationParameter(key, param); if (Q_UNLIKELY(!success)) { - qWarning("Cannot apply parameter: %d with value: %ls", - key, qUtf16Printable(param.toString())); + qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "Cannot apply parameter: %d with value: %ls.", + key, qUtf16Printable(param.toString())); } } } @@ -565,7 +568,7 @@ bool TinyCanBackend::writeFrame(const QCanBusFrame &newData) } // CAN FD frame format not supported at this stage - if (Q_UNLIKELY(newData.payload().size() > 8)) { + if (Q_UNLIKELY(newData.hasFlexibleDataRateFormat())) { setError(tr("CAN FD frame format not supported."), QCanBusDevice::WriteError); return false; } diff --git a/src/plugins/canbus/vectorcan/main.cpp b/src/plugins/canbus/vectorcan/main.cpp index 07c30bc..bbfb851 100644 --- a/src/plugins/canbus/vectorcan/main.cpp +++ b/src/plugins/canbus/vectorcan/main.cpp @@ -40,8 +40,12 @@ #include <QtSerialBus/qcanbusdevice.h> #include <QtSerialBus/qcanbusfactory.h> +#include <QtCore/qloggingcategory.h> + QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_VECTORCAN, "qt.canbus.plugins.vectorcan") + class VectorCanBusPlugin : public QObject, public QCanBusFactoryV2 { Q_OBJECT @@ -61,7 +65,7 @@ public: { QString errorReason; if (Q_UNLIKELY(!VectorCanBackend::canCreate(&errorReason))) { - qWarning("%ls", qUtf16Printable(errorReason)); + qCWarning(QT_CANBUS_PLUGINS_VECTORCAN, "%ls", qUtf16Printable(errorReason)); if (errorMessage) *errorMessage = errorReason; return nullptr; diff --git a/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h index 7bfb3c6..3f2501f 100644 --- a/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h +++ b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h @@ -441,7 +441,7 @@ typedef struct _XLacceptance { static fp_##symbolName symbolName; #define RESOLVE_SYMBOL(symbolName) \ - symbolName = (fp_##symbolName)vectorcanLibrary->resolve(#symbolName); \ + symbolName = reinterpret_cast<fp_##symbolName>(vectorcanLibrary->resolve(#symbolName)); \ if (!symbolName) \ return false; diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend.cpp b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp index c84658b..dc28d7c 100644 --- a/src/plugins/canbus/vectorcan/vectorcanbackend.cpp +++ b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp @@ -42,6 +42,7 @@ #include <QtCore/qtimer.h> #include <QtCore/qcoreevent.h> +#include <QtCore/qloggingcategory.h> #include <QtCore/qwineventnotifier.h> #include <QtCore/qcoreapplication.h> @@ -49,6 +50,8 @@ QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_VECTORCAN) + #ifndef LINK_LIBVECTORCAN Q_GLOBAL_STATIC(QLibrary, vectorcanLibrary) #endif @@ -86,7 +89,12 @@ QList<QCanBusDeviceInfo> VectorCanBackend::interfaces() const bool isVirtual = config.channel[i].hwType == XL_HWTYPE_VIRTUAL; const bool isFd = config.channel[i].channelCapabilities & XL_CHANNEL_FLAG_CANFD_SUPPORT; - result.append(createDeviceInfo(QStringLiteral("can") + QString::number(i), isVirtual, isFd)); + const int channel = config.channel[i].hwChannel; + const QString name = QStringLiteral("can") + QString::number(i); + const QString serial = QString::number(config.channel[i].serialNumber); + const QString description = QLatin1String(config.channel[i].name); + result.append(std::move(createDeviceInfo(name, serial, description, channel, + isVirtual, isFd))); } VectorCanBackendPrivate::cleanupDriver(); @@ -117,7 +125,7 @@ protected: } private: - VectorCanBackendPrivate *dptr; + VectorCanBackendPrivate * const dptr; }; class WriteNotifier : public QTimer @@ -142,7 +150,7 @@ protected: } private: - VectorCanBackendPrivate *dptr; + VectorCanBackendPrivate * const dptr; }; @@ -459,8 +467,8 @@ bool VectorCanBackend::open() const QVariant param = configurationParameter(key); const bool success = d->setConfigurationParameter(key, param); if (!success) { - qWarning("Cannot apply parameter: %d with value: %ls", - key, qUtf16Printable(param.toString())); + qCWarning(QT_CANBUS_PLUGINS_VECTORCAN, "Cannot apply parameter: %d with value: %ls.", + key, qUtf16Printable(param.toString())); } } @@ -507,7 +515,7 @@ bool VectorCanBackend::writeFrame(const QCanBusFrame &newData) } // CAN FD frame format not implemented at this stage - if (Q_UNLIKELY(newData.payload().size() > MAX_MSG_LEN)) { + if (Q_UNLIKELY(newData.hasFlexibleDataRateFormat())) { setError(tr("CAN FD frame format not supported."), QCanBusDevice::WriteError); return false; diff --git a/src/serialbus/doc/src/passthrucan.qdoc b/src/serialbus/doc/src/passthrucan.qdoc new file mode 100644 index 0000000..740dbc4 --- /dev/null +++ b/src/serialbus/doc/src/passthrucan.qdoc @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Ford Motor Company. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ +/*! + \page qtserialbus-passthrucan-overview.html + \title Using PassThruCAN Plugin + + \brief Overview of how to use the J2534 Pass-Thru CAN plugin. + + The Pass-Thru CAN plugin accesses CAN adapters via the SAE J2534 Pass-Thru API. + SAE J2534 is a standard for accessing vehicle busses from an x86 Windows PC. + Although the API is specified only for 32-bit Windows, some vendors also provide + implementations for 64-bit Windows and other operating systems such as Linux. + + \section1 PassThruCAN usage + + To use PassThruCAN, the corresponding vendor drivers for the CAN adapter must + be installed. The vendor must also provide an implementation of the J2534 API + by way of a shared library. Currently, only version 04.04 of the Pass-Thru API + is supported. + + When using an x64 build of Qt, this plugin only works if the CAN device vendor + also provides a 64-bit version of the J2534 Pass-Thru interface library. If the + vendor provides only a 32-bit J2534 interface, a 32-bit build of Qt is required + to make use of it. + + For automatic device discovery, the vendor software must also list and describe + the available adapters in the Windows registry. On systems other than Windows, + automatic discovery is currently not supported. + + \section1 Creating CAN Bus Devices + + At first it is necessary to check that QCanBus provides the desired plugin: + + \code + if (QCanBus::instance()->plugins().contains(QStringLiteral("passthrucan"))) { + // plugin available + } + \endcode + + Where \e passthrucan is the plugin name. + + On Windows, automatic device discovery should be used to list the available + CAN adapters accessible via the Pass-Thru API: + + \code + const auto adapters = QCanBus::instance()-> + availableDevices(QStringLiteral("passthrucan")); + for (const QCanBusDeviceInfo &info : adapters) { + // List available adapter in the user interface. + uiListBox->addItem(info.name()); + } + \endcode + + On other operating systems, the list of discovered adapters will be empty. + Instead, the full path to the vendor-provided J2534 interface library + should be provided in lieu of the device name: + + \code + QCanBusDevice *device = QCanBus::instance()->createDevice( + QStringLiteral("passthrucan"), QStringLiteral("/path/to/libj2534-vendor.so")); + \endcode + + For special needs, it is also possible to pass a vendor-specific device + name argument when opening the Pass-Thru adapter: + + \code + QCanBusDevice *device = QCanBus::instance()->createDevice( + QStringLiteral("passthrucan"), info.name() + QChar::fromLatin1('%') + deviceName); + \endcode + + All operations on the Pass-Thru CAN bus device are executed asynchronously, + including connect and disconnect. In order to be notified when the device + is ready for reading and writing CAN frames, connect to the + \l {QCanBusDevice::}{stateChanged(QCanBusDevice::CanBusDeviceState state)} + signal: + + \code + connect(device, &QCanBusDevice::stateChanged, + this, &MyClass::canStateChanged); + device->connectDevice(); + \endcode + + \l {QCanBusDevice::}{state()} will return \l {QCanBusDevice::}{ConnectedState} + once the CAN adapter has been successfully connected to. The device is then + open for writing and reading CAN frames: + + \code + QCanBusFrame frame; + frame.setFrameId(8); + frame.setPayload(QByteArray("\xA3\x6E\x74\x9C", 4)); + device->writeFrame(frame); + \endcode + + The reading can be done using the \l {QCanBusDevice::}{readFrame()} method. The + \l {QCanBusDevice::}{framesReceived()} signal is emitted when at least one new frame + is available for reading: + + \code + QCanBusFrame frame = device->readFrame(); + \endcode + + The Pass-Thru CAN plugin supports the following configuration options + controllable via \l {QCanBusDevice::}{setConfigurationParameter()}: + + \table + \header + \li Configuration parameter key + \li Description + \row + \li QCanBusDevice::LoopbackKey + \li When enabled, if a CAN frame is transmitted on the CAN bus, a local + echo of this frame will be received by the CAN adapter. The echo + frames are marked with QCanBusFrame::hasLocalEcho(). By default, + loopback mode is disabled. + \row + \li QCanBusDevice::RawFilterKey + \li This option allows setting up filters for incoming CAN bus messages. + If provided, the value should be a \l {QList<QCanBusDevice::Filter>}. + Only data frame ID filters are supported. By default, data frames + with any ID are accepted. + \row + \li QCanBusDevice::BitRateKey + \li The bit rate of the CAN bus as an unsigned integer, in bit/s. The + default bit rate is 500000 (500 kbit/s). Setting the bit rate after + the device has already been connected may trigger an implicit + reinitialization of the CAN interface. + \endtable + + The Pass-Thru CAN plugin supports extended frame format (29-bit IDs), but not + flexible data-rate (CAN FD). + */ diff --git a/src/serialbus/doc/src/qtcanbus-backends.qdoc b/src/serialbus/doc/src/qtcanbus-backends.qdoc index e18adf6..1c4cd74 100644 --- a/src/serialbus/doc/src/qtcanbus-backends.qdoc +++ b/src/serialbus/doc/src/qtcanbus-backends.qdoc @@ -61,6 +61,10 @@ \li \l {Using SocketCAN Plugin}{SocketCAN} (\c socketcan) \li CAN bus plugin using Linux sockets and open source drivers. \row + \li CAN via SAE J2534 Pass-Thru + \li \l {Using PassThruCAN Plugin}{PassThruCAN} (\c passthrucan) + \li CAN bus plugin using the SAE J2534 Pass-Thru interface. + \row \li SYS TEC electronic \li \l {Using SystecCAN Backend}{SystecCAN} (\c systeccan) \li CAN bus backend using the SYS TEC CAN adapters. diff --git a/src/serialbus/doc/src/qtserialbus-index.qdoc b/src/serialbus/doc/src/qtserialbus-index.qdoc index 6c91fbf..db9f241 100644 --- a/src/serialbus/doc/src/qtserialbus-index.qdoc +++ b/src/serialbus/doc/src/qtserialbus-index.qdoc @@ -73,14 +73,33 @@ \li Logging Category \li Description \row + \li qt.canbus + \li Enables standard logging inside the \l {Qt CAN Bus} classes + \row + \li qt.canbus.plugins + \li Enables low level logging inside the \l {Qt CAN Bus} plugin classes. + To set logging for a specific plugin, use "qt.canbus.plugins.pluginname". + e.g. "qt.canbus.plugins.socketcan". "qt.canbus.plugins*" affects all plugins. + \row \li qt.modbus - \li Enables standard logging inside the Modbus classes + \li Enables standard logging inside the \l {Qt Modbus} classes \row \li qt.modbus.lowlevel \li Enables low level logging including individual packet content inside - the Modbus classes + the \l {Qt Modbus} classes \endtable + Logging categories can be used to enable additional warning and debug output + for \l QtSerialBus. More detailed information about logging can be found + in \l QLoggingCategory. + + A quick way to enable all \l {Qt Modbus} logging is to add the following line + to the main() function: + + \code + QLoggingCategory::setFilterRules(QStringLiteral("qt.modbus* = true")); + \endcode + \section1 Examples \list diff --git a/src/serialbus/qcanbusdevice.cpp b/src/serialbus/qcanbusdevice.cpp index 919c38a..b1c97a6 100644 --- a/src/serialbus/qcanbusdevice.cpp +++ b/src/serialbus/qcanbusdevice.cpp @@ -43,11 +43,14 @@ #include <QtCore/qdebug.h> #include <QtCore/qdatastream.h> #include <QtCore/qeventloop.h> +#include <QtCore/qloggingcategory.h> #include <QtCore/qscopedvaluerollback.h> #include <QtCore/qtimer.h> QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(QT_CANBUS, "qt.canbus") + /*! \class QCanBusDevice \inmodule QtSerialBus @@ -445,10 +448,10 @@ bool QCanBusDevice::waitForFramesWritten(int msecs) { // do not enter this function recursively if (Q_UNLIKELY(d_func()->waitForWrittenEntered)) { - qWarning("QCanBusDevice::waitForFramesWritten() must not be called " - "recursively. Check that no slot containing waitForFramesReceived() " - "is called in response to framesWritten(qint64) or errorOccurred(CanBusError)" - "signals\n"); + qCWarning(QT_CANBUS, "QCanBusDevice::waitForFramesWritten() must not be called " + "recursively. Check that no slot containing waitForFramesReceived() " + "is called in response to framesWritten(qint64) or " + "errorOccurred(CanBusError) signals."); return false; } @@ -498,10 +501,10 @@ bool QCanBusDevice::waitForFramesReceived(int msecs) { // do not enter this function recursively if (Q_UNLIKELY(d_func()->waitForReceivedEntered)) { - qWarning("QCanBusDevice::waitForFramesReceived() must not be called " - "recursively. Check that no slot containing waitForFramesReceived() " - "is called in response to framesReceived() or errorOccurred(CanBusError) " - "signals\n"); + qCWarning(QT_CANBUS, "QCanBusDevice::waitForFramesReceived() must not be called " + "recursively. Check that no slot containing waitForFramesReceived() " + "is called in response to framesReceived() or " + "errorOccurred(CanBusError) signals."); return false; } @@ -672,7 +675,7 @@ void QCanBusDevice::disconnectDevice() if (Q_UNLIKELY(d->state == QCanBusDevice::UnconnectedState || d->state == QCanBusDevice::ClosingState)) { - qWarning("Can not disconnect an unconnected device"); + qCWarning(QT_CANBUS, "Can not disconnect an unconnected device."); return; } @@ -724,10 +727,27 @@ void QCanBusDevice::setState(QCanBusDevice::CanBusDeviceState newState) QCanBusDeviceInfo QCanBusDevice::createDeviceInfo(const QString &name, bool isVirtual, bool isFlexibleDataRateCapable) { + return createDeviceInfo(name, QString(), QString(), 0, isVirtual, isFlexibleDataRateCapable); +} + +/*! + \since 5.11 + Returns a QCanBusDeviceInfo created from the given parameters \a name, + \a serialNumber, \a description, \a channel, \a isVirtual, and \a + isFlexibleDataRateCapable. + \internal + */ +QCanBusDeviceInfo QCanBusDevice::createDeviceInfo(const QString &name, const QString &serialNumber, + const QString &description, int channel, + bool isVirtual, bool isFlexibleDataRateCapable) +{ QScopedPointer<QCanBusDeviceInfoPrivate> info(new QCanBusDeviceInfoPrivate); info->name = name; - info->isVirtual = isVirtual; + info->serialNumber = serialNumber; + info->description = description; + info->channel = channel; info->hasFlexibleDataRate = isFlexibleDataRateCapable; + info->isVirtual = isVirtual; return QCanBusDeviceInfo(*info.take()); } diff --git a/src/serialbus/qcanbusdevice.h b/src/serialbus/qcanbusdevice.h index 3a644c6..482e9d0 100644 --- a/src/serialbus/qcanbusdevice.h +++ b/src/serialbus/qcanbusdevice.h @@ -146,6 +146,9 @@ protected: static QCanBusDeviceInfo createDeviceInfo(const QString &name, bool isVirtual = false, bool isFlexibleDataRateCapable = false); + static QCanBusDeviceInfo createDeviceInfo(const QString &name, const QString &serialNumber, + const QString &description, int channel, + bool isVirtual, bool isFlexibleDataRateCapable); }; Q_DECLARE_TYPEINFO(QCanBusDevice::CanBusError, Q_PRIMITIVE_TYPE); diff --git a/src/serialbus/qcanbusdeviceinfo.cpp b/src/serialbus/qcanbusdeviceinfo.cpp index 30b0e12..25c02e1 100644 --- a/src/serialbus/qcanbusdeviceinfo.cpp +++ b/src/serialbus/qcanbusdeviceinfo.cpp @@ -96,6 +96,38 @@ QString QCanBusDeviceInfo::name() const } /*! + \since 5.11 + Returns a textual description of the CAN bus interface, if available. + Example output: "PCAN USB Pro FD". If no description is available, + an empty string is returned. +*/ +QString QCanBusDeviceInfo::description() const +{ + return d_ptr->description; +} + +/*! + \since 5.11 + Returns the serial number of the CAN bus interface as string, if available. + Otherwise, an empty string is returned. +*/ +QString QCanBusDeviceInfo::serialNumber() const +{ + return d_ptr->serialNumber; +} + +/*! + \since 5.11 + Returns the sequential channel number of the CAN bus interface, starting + with zero. If the interface has only one channel or if no information about + the channel is available, zero is returned. +*/ +int QCanBusDeviceInfo::channel() const +{ + return d_ptr->channel; +} + +/*! Returns true, if the CAN bus interface is CAN FD (flexible data rate) capable. If this information is not available, false is returned. diff --git a/src/serialbus/qcanbusdeviceinfo.h b/src/serialbus/qcanbusdeviceinfo.h index 68b68a3..fbe2c71 100644 --- a/src/serialbus/qcanbusdeviceinfo.h +++ b/src/serialbus/qcanbusdeviceinfo.h @@ -65,6 +65,9 @@ public: } QString name() const; + QString description() const; + QString serialNumber() const; + int channel() const; bool hasFlexibleDataRate() const; bool isVirtual() const; diff --git a/src/serialbus/qcanbusdeviceinfo_p.h b/src/serialbus/qcanbusdeviceinfo_p.h index ae6d71d..78c9e1c 100644 --- a/src/serialbus/qcanbusdeviceinfo_p.h +++ b/src/serialbus/qcanbusdeviceinfo_p.h @@ -62,7 +62,9 @@ public: } QString name; + QString description; QString serialNumber; + int channel = 0; bool hasFlexibleDataRate = false; bool isVirtual = false; }; diff --git a/src/serialbus/qcanbusframe.h b/src/serialbus/qcanbusframe.h index 0300a9c..d1f8857 100644 --- a/src/serialbus/qcanbusframe.h +++ b/src/serialbus/qcanbusframe.h @@ -133,17 +133,16 @@ public: return false; // maximum permitted payload size in CAN or CAN FD + const int length = load.length(); if (isFlexibleDataRate) { - if (load.length() > 64) - return false; if (format == RemoteRequestFrame) return false; - } else { - if (load.length() > 8) - return false; + + return length <= 8 || length == 12 || length == 16 || length == 20 + || length == 24 || length == 32 || length == 48 || length == 64; } - return true; + return length <= 8; } FrameType frameType() const Q_DECL_NOTHROW diff --git a/src/tools/canbusutil/canbusutil.cpp b/src/tools/canbusutil/canbusutil.cpp index c553c52..38433bb 100644 --- a/src/tools/canbusutil/canbusutil.cpp +++ b/src/tools/canbusutil/canbusutil.cpp @@ -36,8 +36,11 @@ #include "canbusutil.h" -CanBusUtil::CanBusUtil(QTextStream &output, QCoreApplication &app, QObject *parent) - : QObject(parent), +#include <QCoreApplication> +#include <QTextStream> + +CanBusUtil::CanBusUtil(QTextStream &output, QCoreApplication &app, QObject *parent) : + QObject(parent), m_canBus(QCanBus::instance()), m_output(output), m_app(app), @@ -58,7 +61,7 @@ void CanBusUtil::setShowFdFlags(bool showFdFlags) bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, const QString &data) { if (!m_canBus) { - m_output << "Unable to create QCanBus" << endl; + m_output << tr("Error: Cannot create QCanBus.") << endl; return false; } @@ -73,29 +76,41 @@ bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, con if (m_listening) { if (m_readTask->isShowFdFlags()) m_canDevice->setConfigurationParameter(QCanBusDevice::CanFdKey, true); - connect(m_canDevice.data(), &QCanBusDevice::framesReceived, m_readTask, &ReadTask::checkMessages); + connect(m_canDevice.data(), &QCanBusDevice::framesReceived, + m_readTask, &ReadTask::handleFrames); } else { if (!sendData()) return false; - QTimer::singleShot(0, &m_app, SLOT(quit())); + QTimer::singleShot(0, &m_app, QCoreApplication::quit); } return true; } -void CanBusUtil::printPlugins() +int CanBusUtil::printPlugins() { + if (!m_canBus) { + m_output << tr("Error: Cannot create QCanBus.") << endl; + return 1; + } + const QStringList plugins = m_canBus->plugins(); - for (int i = 0; i < plugins.size(); i++) - m_output << plugins.at(i) << endl; + for (const QString &plugin : plugins) + m_output << plugin << endl; + return 0; } int CanBusUtil::printDevices(const QString &pluginName) { + if (!m_canBus) { + m_output << tr("Error: Cannot create QCanBus.") << endl; + return 1; + } + QString errorMessage; const QList<QCanBusDeviceInfo> devices = m_canBus->availableDevices(pluginName, &errorMessage); if (!errorMessage.isEmpty()) { - m_output << "Error: " << errorMessage << endl; + m_output << tr("Error gathering available devices: '%1'").arg(errorMessage) << endl; return 1; } @@ -108,12 +123,12 @@ bool CanBusUtil::parseDataField(quint32 &id, QString &payload) { int hashMarkPos = m_data.indexOf('#'); if (hashMarkPos < 0) { - m_output << "Data field invalid: No hash mark found!" << endl; + m_output << tr("Data field invalid: No hash mark found!") << endl; return false; } id = m_data.leftRef(hashMarkPos).toUInt(nullptr, 16); - payload = m_data.right(m_data.length() - hashMarkPos - 1); + payload = m_data.right(m_data.size() - hashMarkPos - 1); return true; } @@ -121,37 +136,30 @@ bool CanBusUtil::parseDataField(quint32 &id, QString &payload) bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) { if (!payload.isEmpty() && payload.at(0).toUpper() == 'R') { - bool validPayloadLength = false; - frame->setFrameType(QCanBusFrame::RemoteRequestFrame); - if (payload.size() == 1) { // payload = "R" - validPayloadLength = true; - } else if (payload.size() > 1) { // payload = "R8" - payload = payload.mid(1); - int rtrFrameLength = payload.toInt(&validPayloadLength); - - if (validPayloadLength && rtrFrameLength >= 0 && rtrFrameLength <= 8) { - frame->setPayload(QByteArray(rtrFrameLength, 0)); - } else if (validPayloadLength) { - m_output << "The length must be between 0 and 8 (including)." << endl; - validPayloadLength = false; - } - } + if (payload.size() == 1) // payload = "R" + return true; - if (!validPayloadLength) { - m_output << "Data field invalid: RTR data frame length not specified/valid." << endl; + bool ok = false; + int rtrFrameLength = payload.midRef(1).toInt(&ok); + if (ok && rtrFrameLength >= 0 && rtrFrameLength <= 8) { // payload = "R8" + frame->setPayload(QByteArray(rtrFrameLength, 0)); + return true; } - return validPayloadLength; - } else if (!payload.isEmpty() && payload.at(0) == '#') { + m_output << tr("Error: RTR frame length must be between 0 and 8 (including).") << endl; + return false; + } + + if (!payload.isEmpty() && payload.at(0) == '#') { frame->setFlexibleDataRateFormat(true); - payload = payload.mid(1); + payload.remove(0, 1); } const QRegularExpression re(QStringLiteral("^[0-9A-Fa-f]*$")); if (!re.match(payload).hasMatch()) { - m_output << "Data field invalid: Only hex numbers allowed." << endl; + m_output << tr("Data field invalid: Only hex numbers allowed.") << endl; return false; } @@ -163,7 +171,7 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) frame->setErrorStateIndicator(flags & ErrorStateIndicatorFlag); payload.remove(0, 1); } else { - m_output << "Data field invalid: Size is not multiple of two." << endl; + m_output << tr("Data field invalid: Size is not multiple of two.") << endl; return false; } } @@ -172,8 +180,8 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) const int maxSize = frame->hasFlexibleDataRateFormat() ? 64 : 8; if (bytes.size() > maxSize) { - m_output << "Warning: Truncating payload at max. size of " << maxSize << " bytes." << endl; - bytes.truncate(maxSize); + m_output << tr("Data field invalid: Size is longer than %1 bytes.").arg(maxSize) << endl; + return false; } frame->setPayload(bytes); @@ -184,20 +192,18 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) bool CanBusUtil::connectCanDevice() { if (!m_canBus->plugins().contains(m_pluginName)) { - m_output << "Could not find suitable plugin." << endl; - m_output << "Available plugins:" << endl; - printPlugins(); + m_output << tr("Cannot find CAN bus plugin '%1'.").arg(m_pluginName) << endl; return false; } m_canDevice.reset(m_canBus->createDevice(m_pluginName, m_deviceName)); if (!m_canDevice) { - m_output << "Unable to create QCanBusDevice with device name: " << m_deviceName << endl; + m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << endl; return false; } - connect(m_canDevice.data(), &QCanBusDevice::errorOccurred, m_readTask, &ReadTask::receiveError); + connect(m_canDevice.data(), &QCanBusDevice::errorOccurred, m_readTask, &ReadTask::handleError); if (!m_canDevice->connectDevice()) { - m_output << "Unable to connect QCanBusDevice with device name: " << m_deviceName << endl; + m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << endl; return false; } @@ -210,15 +216,15 @@ bool CanBusUtil::sendData() QString payload; QCanBusFrame frame; - if (parseDataField(id, payload) == false) + if (!parseDataField(id, payload)) return false; - if (setFrameFromPayload(payload, &frame) == false) + if (!setFrameFromPayload(payload, &frame)) return false; if (id > 0x1FFFFFFF) { // 29 bits - id = 0x1FFFFFFF; - m_output << "Warning! Id does not fit into Extended Frame Format, setting id to: " << id << endl; + m_output << tr("Cannot send invalid frame ID: '%1'").arg(id, 0, 16) << endl; + return false; } frame.setFrameId(id); diff --git a/src/tools/canbusutil/canbusutil.h b/src/tools/canbusutil/canbusutil.h index 356b3df..e6432a3 100644 --- a/src/tools/canbusutil/canbusutil.h +++ b/src/tools/canbusutil/canbusutil.h @@ -37,16 +37,16 @@ #ifndef CANBUSUTIL_H #define CANBUSUTIL_H +#include "readtask.h" + #include <QObject> -#include <QTextStream> -#include <QCoreApplication> #include <QScopedPointer> -#include "readtask.h" - QT_BEGIN_NAMESPACE class QCanBusFrame; +class QCoreApplication; +class QTextStream; QT_END_NAMESPACE @@ -59,7 +59,7 @@ public: void setShowTimeStamp(bool showTimeStamp); void setShowFdFlags(bool showFdFlags); bool start(const QString &pluginName, const QString &deviceName, const QString &data = QString()); - void printPlugins(); + int printPlugins(); int printDevices(const QString &pluginName); private: @@ -69,15 +69,15 @@ private: bool sendData(); private: - QCanBus *m_canBus; + QCanBus *m_canBus = nullptr; QTextStream &m_output; QCoreApplication &m_app; - bool m_listening; + bool m_listening = false; QString m_pluginName; QString m_deviceName; QString m_data; QScopedPointer<QCanBusDevice> m_canDevice; - ReadTask *m_readTask; + ReadTask *m_readTask = nullptr; }; #endif // CANBUSUTIL_H diff --git a/src/tools/canbusutil/main.cpp b/src/tools/canbusutil/main.cpp index 1304f22..6744f19 100644 --- a/src/tools/canbusutil/main.cpp +++ b/src/tools/canbusutil/main.cpp @@ -34,6 +34,9 @@ ** ****************************************************************************/ +#include "canbusutil.h" +#include "sigtermhandler.h" + #include <QCommandLineParser> #include <QCoreApplication> #include <QTextStream> @@ -41,15 +44,10 @@ #include <signal.h> -#include "canbusutil.h" -#include "sigtermhandler.h" - -#define PROGRAMNAME "canbusutil" - int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); - QCoreApplication::setApplicationName(QStringLiteral(PROGRAMNAME)); + QCoreApplication::setApplicationName(QStringLiteral("canbusutil")); QCoreApplication::setApplicationVersion(QStringLiteral(QT_VERSION_STR)); QScopedPointer<SigTermHandler> s(SigTermHandler::instance()); @@ -106,10 +104,8 @@ int main(int argc, char *argv[]) parser.process(app); - if (parser.isSet(listOption)) { - util.printPlugins(); - return 0; - } + if (parser.isSet(listOption)) + return util.printPlugins(); QString data; const QStringList args = parser.positionalArguments(); @@ -117,16 +113,16 @@ int main(int argc, char *argv[]) util.setShowTimeStamp(parser.isSet(showTimeStampOption)); util.setShowFdFlags(parser.isSet(showFdFlagsOption)); } else if (args.size() == 3) { - data = args[2]; + data = args.at(2); } else if (args.size() == 1 && parser.isSet(listDevicesOption)) { - return util.printDevices(args[0]); + return util.printDevices(args.at(0)); } else if (args.size() != 2) { - fprintf(stderr, "Invalid number of arguments (%d given).\n\n%s", - args.size(), qPrintable(parser.helpText())); + output << CanBusUtil::tr("Invalid number of arguments (%1 given).").arg(args.size()); + output << endl << endl << parser.helpText(); return 1; } - if (!util.start(args[0], args[1], data)) + if (!util.start(args.at(0), args.at(1), data)) return -1; return app.exec(); diff --git a/src/tools/canbusutil/readtask.cpp b/src/tools/canbusutil/readtask.cpp index fb159b7..2bde509 100644 --- a/src/tools/canbusutil/readtask.cpp +++ b/src/tools/canbusutil/readtask.cpp @@ -36,9 +36,9 @@ #include "readtask.h" -ReadTask::ReadTask(QTextStream &output, QObject *parent) - : QObject(parent), - output(output) { } +ReadTask::ReadTask(QTextStream &output, QObject *parent) : + QObject(parent), + m_output(output) { } void ReadTask::setShowTimeStamp(bool showTimeStamp) { @@ -55,10 +55,10 @@ void ReadTask::setShowFdFlags(bool showFlags) m_showFdFlags = showFlags; } -void ReadTask::checkMessages() { +void ReadTask::handleFrames() { auto canDevice = qobject_cast<QCanBusDevice *>(QObject::sender()); if (canDevice == nullptr) { - qWarning("ReadTask::checkMessages: Unknown sender"); + qWarning("ReadTask::handleFrames: Unknown sender."); return; } @@ -87,16 +87,17 @@ void ReadTask::checkMessages() { else view += frame.toString(); - output << view << endl; + m_output << view << endl; } } -void ReadTask::receiveError(QCanBusDevice::CanBusError /*error*/) { +void ReadTask::handleError(QCanBusDevice::CanBusError /*error*/) +{ auto canDevice = qobject_cast<QCanBusDevice *>(QObject::sender()); if (canDevice == nullptr) { - qWarning("ReadTask::receiveError: Unknown sender"); + qWarning("ReadTask::handleError: Unknown sender."); return; } - output << "Read error: " << canDevice->errorString() << endl; + m_output << tr("Read error: '%1'").arg(canDevice->errorString()) << endl; } diff --git a/src/tools/canbusutil/readtask.h b/src/tools/canbusutil/readtask.h index e00440a..a866d3d 100644 --- a/src/tools/canbusutil/readtask.h +++ b/src/tools/canbusutil/readtask.h @@ -45,17 +45,17 @@ class ReadTask : public QObject { Q_OBJECT public: - explicit ReadTask(QTextStream &output, QObject *parent = nullptr); + explicit ReadTask(QTextStream &m_output, QObject *parent = nullptr); void setShowTimeStamp(bool showStamp); bool isShowFdFlags() const; void setShowFdFlags(bool isShowFdFlags); public slots: - void checkMessages(); - void receiveError(QCanBusDevice::CanBusError /*error*/); + void handleFrames(); + void handleError(QCanBusDevice::CanBusError /*error*/); private: - QTextStream &output; + QTextStream &m_output; bool m_showTimeStamp = false; bool m_showFdFlags = false; }; diff --git a/tests/auto/plugins/genericcanbus/dummybackend.cpp b/tests/auto/plugins/genericcanbus/dummybackend.cpp index 4c3173a..83d2480 100644 --- a/tests/auto/plugins/genericcanbus/dummybackend.cpp +++ b/tests/auto/plugins/genericcanbus/dummybackend.cpp @@ -43,12 +43,16 @@ QT_BEGIN_NAMESPACE DummyBackend::DummyBackend() : - sendTimer(new QTimer(this)) + simulateReceivingTimer(new QTimer(this)) { - sendTimer->setInterval(1000); - sendTimer->setSingleShot(false); - connect(sendTimer, &QTimer::timeout, this, &DummyBackend::sendMessage); - sendTimer->start(); + connect(simulateReceivingTimer, &QTimer::timeout, [this]() { + const quint64 timeStamp = QDateTime::currentDateTime().toMSecsSinceEpoch(); + QCanBusFrame dummyFrame(12, "def"); + dummyFrame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(timeStamp * 1000)); + + enqueueReceivedFrames({dummyFrame}); + }); + simulateReceivingTimer->start(1000); } bool DummyBackend::open() @@ -62,20 +66,9 @@ void DummyBackend::close() setState(QCanBusDevice::UnconnectedState); } -void DummyBackend::sendMessage() -{ - quint64 timeStamp = QDateTime::currentDateTime().toMSecsSinceEpoch(); - QCanBusFrame dummyFrame; - dummyFrame.setFrameId(12); - dummyFrame.setPayload(QByteArray("def")); - dummyFrame.setTimeStamp(QCanBusFrame::TimeStamp(timeStamp / 1000, (timeStamp % 1000) * 1000)); - - enqueueReceivedFrames(QVector<QCanBusFrame>() << dummyFrame); -} - bool DummyBackend::writeFrame(const QCanBusFrame &data) { - qDebug() << "DummyBackend::writeFrame: " << data.toString(); + qDebug("DummyBackend::writeFrame: %ls", qUtf16Printable(data.toString())); return true; } diff --git a/tests/auto/plugins/genericcanbus/dummybackend.h b/tests/auto/plugins/genericcanbus/dummybackend.h index 22bbc80..d9727be 100644 --- a/tests/auto/plugins/genericcanbus/dummybackend.h +++ b/tests/auto/plugins/genericcanbus/dummybackend.h @@ -58,11 +58,8 @@ public: static QList<QCanBusDeviceInfo> interfaces(); -public Q_SLOTS: - void sendMessage(); - private: - QTimer *sendTimer; + QTimer *simulateReceivingTimer = nullptr; }; QT_END_NAMESPACE diff --git a/tests/auto/plugins/genericcanbus/main.cpp b/tests/auto/plugins/genericcanbus/main.cpp index 8e14fee..89103a4 100644 --- a/tests/auto/plugins/genericcanbus/main.cpp +++ b/tests/auto/plugins/genericcanbus/main.cpp @@ -38,7 +38,6 @@ #include <QtSerialBus/qcanbus.h> #include <QtSerialBus/qcanbusfactory.h> -#include "../../../../src/serialbus/qcanbusdeviceinfo_p.h" QT_BEGIN_NAMESPACE diff --git a/tests/auto/plugins/genericcanbusv1/dummybackendv1.cpp b/tests/auto/plugins/genericcanbusv1/dummybackendv1.cpp index 117aeec..4d7d9c0 100644 --- a/tests/auto/plugins/genericcanbusv1/dummybackendv1.cpp +++ b/tests/auto/plugins/genericcanbusv1/dummybackendv1.cpp @@ -43,16 +43,16 @@ QT_BEGIN_NAMESPACE DummyBackendV1::DummyBackendV1() : - sendTimer(new QTimer(this)) + simulateReceivingTimer(new QTimer(this)) { - connect(sendTimer, &QTimer::timeout, [this]() { + connect(simulateReceivingTimer, &QTimer::timeout, [this]() { const quint64 timeStamp = QDateTime::currentDateTime().toMSecsSinceEpoch(); QCanBusFrame dummyFrame(11, "abc"); dummyFrame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(timeStamp * 1000)); enqueueReceivedFrames({dummyFrame}); }); - sendTimer->start(1000); + simulateReceivingTimer->start(1000); } bool DummyBackendV1::open() diff --git a/tests/auto/plugins/genericcanbusv1/dummybackendv1.h b/tests/auto/plugins/genericcanbusv1/dummybackendv1.h index 4c3fc64..6892b4f 100644 --- a/tests/auto/plugins/genericcanbusv1/dummybackendv1.h +++ b/tests/auto/plugins/genericcanbusv1/dummybackendv1.h @@ -57,7 +57,7 @@ public: QString interpretErrorFrame(const QCanBusFrame &) override; private: - QTimer *sendTimer; + QTimer *simulateReceivingTimer = nullptr; }; QT_END_NAMESPACE diff --git a/tests/auto/qcanbus/tst_qcanbus.cpp b/tests/auto/qcanbus/tst_qcanbus.cpp index 9e6a647..475adc4 100644 --- a/tests/auto/qcanbus/tst_qcanbus.cpp +++ b/tests/auto/qcanbus/tst_qcanbus.cpp @@ -37,7 +37,7 @@ #include <QtSerialBus/qcanbus.h> #include <QtSerialBus/qcanbusfactory.h> -#include <QtTest/QtTest> +#include <QtTest/qtest.h> class tst_QCanBus : public QObject { @@ -52,7 +52,7 @@ private slots: void createDevice(); private: - QCanBus *bus; + QCanBus *bus = nullptr; }; tst_QCanBus::tst_QCanBus() @@ -89,12 +89,12 @@ void tst_QCanBus::plugins() void tst_QCanBus::interfaces() { // Plugins derived from QCanBusFactory(V1) don't have availableDevices() - const QList<QCanBusDeviceInfo> pluginListV1 = bus->availableDevices("genericV1"); - QVERIFY(pluginListV1.isEmpty()); + const QList<QCanBusDeviceInfo> deviceListV1 = bus->availableDevices("genericV1"); + QVERIFY(deviceListV1.isEmpty()); const QList<QCanBusDeviceInfo> pluginList = bus->availableDevices("generic"); - QCOMPARE(1, pluginList.size()); - QCOMPARE(QString("can0"), pluginList.at(0).name()); + QCOMPARE(pluginList.size(), 1); + QCOMPARE(pluginList.at(0).name(), QStringLiteral("can0")); QVERIFY(pluginList.at(0).isVirtual()); QVERIFY(pluginList.at(0).hasFlexibleDataRate()); } diff --git a/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp b/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp index 3fce8d5..cf18c4a 100644 --- a/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp +++ b/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp @@ -34,12 +34,13 @@ ** ****************************************************************************/ -#include <QtSerialBus/QCanBusDevice> -#include <QtSerialBus/QCanBusFrame> +#include <QtSerialBus/qcanbusdevice.h> +#include <QtSerialBus/qcanbusframe.h> -#include <QtTest/QtTest> -#include <QSignalSpy> -#include <QScopedPointer> +#include <QtCore/qscopedpointer.h> +#include <QtCore/qtimer.h> +#include <QtTest/qsignalspy.h> +#include <QtTest/qtest.h> #include <memory> @@ -47,8 +48,7 @@ class tst_Backend : public QCanBusDevice { Q_OBJECT public: - tst_Backend() : - firstOpen(true), writeBufferUsed(true) + tst_Backend() { referenceFrame.setFrameId(5); referenceFrame.setPayload(QByteArray("FOOBAR")); @@ -61,8 +61,8 @@ public: if (state() != QCanBusDevice::ConnectedState) return false; - // line below triggers framesReceived() signal - enqueueReceivedFrames(QVector<QCanBusFrame>() << referenceFrame); + // the line below triggers the framesReceived() signal + enqueueReceivedFrames({referenceFrame}); return true; } @@ -109,7 +109,7 @@ public: bool isWriteBuffered() const { return writeBufferUsed; } void setWriteBuffered(bool isBuffered) { - // permits switching between buffered and unbuffered write mode + // allows switching between buffered and unbuffered write mode writeBufferUsed = isBuffered; } @@ -128,8 +128,8 @@ public slots: private: QCanBusFrame referenceFrame; - bool firstOpen; - bool writeBufferUsed; + bool firstOpen = true; + bool writeBufferUsed = true; }; class tst_QCanBusDevice : public QObject @@ -165,21 +165,19 @@ void tst_QCanBusDevice::initTestCase() device.reset(new tst_Backend()); QVERIFY(device); - QSignalSpy stateSpy(device.data(), - SIGNAL(stateChanged(QCanBusDevice::CanBusDeviceState))); + QSignalSpy stateSpy(device.data(), &QCanBusDevice::stateChanged); - QVERIFY(!device->connectDevice()); //first connect triggered to fail + QVERIFY(!device->connectDevice()); // first connect triggered to fail QVERIFY(device->connectDevice()); - QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, - 5000); + QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); QCOMPARE(stateSpy.count(), 4); - QCOMPARE(stateSpy[0].at(0).value<QCanBusDevice::CanBusDeviceState>(), + QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(), QCanBusDevice::ConnectingState); - QCOMPARE(stateSpy[1].at(0).value<QCanBusDevice::CanBusDeviceState>(), + QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(), QCanBusDevice::UnconnectedState); - QCOMPARE(stateSpy[2].at(0).value<QCanBusDevice::CanBusDeviceState>(), + QCOMPARE(stateSpy.at(2).at(0).value<QCanBusDevice::CanBusDeviceState>(), QCanBusDevice::ConnectingState); - QCOMPARE(stateSpy[3].at(0).value<QCanBusDevice::CanBusDeviceState>(), + QCOMPARE(stateSpy.at(3).at(0).value<QCanBusDevice::CanBusDeviceState>(), QCanBusDevice::ConnectedState); } @@ -214,22 +212,20 @@ void tst_QCanBusDevice::write() { // we assume unbuffered writing in this function device->setWriteBuffered(false); - QCOMPARE(device->isWriteBuffered(), false); + QVERIFY(!device->isWriteBuffered()); - QSignalSpy spy(device.data(), SIGNAL(framesWritten(qint64))); - QSignalSpy stateSpy(device.data(), - SIGNAL(stateChanged(QCanBusDevice::CanBusDeviceState))); + QSignalSpy spy(device.data(), &QCanBusDevice::framesWritten); + QSignalSpy stateSpy(device.data(), &QCanBusDevice::stateChanged); QCanBusFrame frame; frame.setPayload(QByteArray("testData")); device->disconnectDevice(); - QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, - 5000); + QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000); QCOMPARE(stateSpy.count(), 2); - QCOMPARE(stateSpy[0].at(0).value<QCanBusDevice::CanBusDeviceState>(), + QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(), QCanBusDevice::ClosingState); - QCOMPARE(stateSpy[1].at(0).value<QCanBusDevice::CanBusDeviceState>(), + QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(), QCanBusDevice::UnconnectedState); stateSpy.clear(); QVERIFY(stateSpy.isEmpty()); @@ -237,14 +233,12 @@ void tst_QCanBusDevice::write() device->writeFrame(frame); QCOMPARE(spy.count(), 0); - device->connectDevice(); - QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, - 5000); + QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); QCOMPARE(stateSpy.count(), 2); - QCOMPARE(stateSpy[0].at(0).value<QCanBusDevice::CanBusDeviceState>(), + QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(), QCanBusDevice::ConnectingState); - QCOMPARE(stateSpy[1].at(0).value<QCanBusDevice::CanBusDeviceState>(), + QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(), QCanBusDevice::ConnectedState); device->writeFrame(frame); @@ -253,8 +247,7 @@ void tst_QCanBusDevice::write() void tst_QCanBusDevice::read() { - QSignalSpy stateSpy(device.data(), - SIGNAL(stateChanged(QCanBusDevice::CanBusDeviceState))); + QSignalSpy stateSpy(device.data(), &QCanBusDevice::stateChanged); device->disconnectDevice(); QCOMPARE(device->state(), QCanBusDevice::UnconnectedState); @@ -263,8 +256,7 @@ void tst_QCanBusDevice::read() const QCanBusFrame frame1 = device->readFrame(); QVERIFY(device->connectDevice()); - QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, - 5000); + QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); QCOMPARE(stateSpy.count(), 2); QCOMPARE(stateSpy[0].at(0).value<QCanBusDevice::CanBusDeviceState>(), QCanBusDevice::ConnectingState); @@ -281,48 +273,43 @@ void tst_QCanBusDevice::read() void tst_QCanBusDevice::error() { - QSignalSpy spy(device.data(), SIGNAL(errorOccurred(QCanBusDevice::CanBusError))); + QSignalSpy spy(device.data(), &QCanBusDevice::errorOccurred); QString testString(QStringLiteral("testString")); auto backend = qobject_cast<tst_Backend *>(device.data()); QVERIFY(backend); - //NoError + // NoError QVERIFY(device->errorString().isEmpty()); - //ReadError - backend->emulateError(testString+QString::fromLatin1("a"), - QCanBusDevice::ReadError); - QCOMPARE(testString+QString::fromLatin1("a"), device->errorString()); - QVERIFY(device->error() == 1); + // ReadError + backend->emulateError(testString + QStringLiteral("a"), QCanBusDevice::ReadError); + QCOMPARE(testString + QStringLiteral("a"), device->errorString()); + QCOMPARE(device->error(), 1); QCOMPARE(spy.count(), 1); - //WriteError - backend->emulateError(testString+QString::fromLatin1("b"), - QCanBusDevice::WriteError); - QCOMPARE(testString+QString::fromLatin1("b"), device->errorString()); - QVERIFY(device->error() == 2); + // WriteError + backend->emulateError(testString + QStringLiteral("b"), QCanBusDevice::WriteError); + QCOMPARE(testString + QStringLiteral("b"), device->errorString()); + QCOMPARE(device->error(), 2); QCOMPARE(spy.count(), 2); - //ConnectionError - backend->emulateError(testString+QString::fromLatin1("c"), - QCanBusDevice::ConnectionError); - QCOMPARE(testString+QString::fromLatin1("c"), device->errorString()); - QVERIFY(device->error() == 3); + // ConnectionError + backend->emulateError(testString + QStringLiteral("c"), QCanBusDevice::ConnectionError); + QCOMPARE(testString + QStringLiteral("c"), device->errorString()); + QCOMPARE(device->error(), 3); QCOMPARE(spy.count(), 3); - //ConfigurationError - backend->emulateError(testString+QString::fromLatin1("d"), - QCanBusDevice::ConfigurationError); - QCOMPARE(testString+QString::fromLatin1("d"), device->errorString()); - QVERIFY(device->error() == 4); + // ConfigurationError + backend->emulateError(testString + QStringLiteral("d"), QCanBusDevice::ConfigurationError); + QCOMPARE(testString + QStringLiteral("d"), device->errorString()); + QCOMPARE(device->error(), 4); QCOMPARE(spy.count(), 4); - //UnknownError - backend->emulateError(testString+QString::fromLatin1("e"), - QCanBusDevice::UnknownError); - QCOMPARE(testString+QString::fromLatin1("e"), device->errorString()); - QVERIFY(device->error() == 5); + // UnknownError + backend->emulateError(testString + QStringLiteral("e"), QCanBusDevice::UnknownError); + QCOMPARE(testString + QStringLiteral("e"), device->errorString()); + QCOMPARE(device->error(), 5); QCOMPARE(spy.count(), 5); } @@ -356,9 +343,8 @@ void tst_QCanBusDevice::tst_filtering() f.format = QCanBusDevice::Filter::MatchBaseFormat; filters.append(f); - QVariant wrapper = QVariant::fromValue(filters); - QList<QCanBusDevice::Filter> newFilter - = wrapper.value<QList<QCanBusDevice::Filter> >(); + const QVariant wrapper = QVariant::fromValue(filters); + const auto newFilter = wrapper.value<QList<QCanBusDevice::Filter> >(); QCOMPARE(newFilter.count(), 2); QCOMPARE(newFilter.at(0).type, QCanBusFrame::DataFrame); @@ -380,22 +366,21 @@ void tst_QCanBusDevice::tst_filtering() void tst_QCanBusDevice::tst_bufferingAttribute() { std::unique_ptr<tst_Backend> canDevice(new tst_Backend); - QVERIFY((bool)canDevice); + QVERIFY(canDevice != nullptr); // by default buffered set to true - QCOMPARE(canDevice->isWriteBuffered(), true); + QVERIFY(canDevice->isWriteBuffered()); canDevice->setWriteBuffered(false); - QCOMPARE(canDevice->isWriteBuffered(), false); + QVERIFY(!canDevice->isWriteBuffered()); canDevice->setWriteBuffered(true); - QCOMPARE(canDevice->isWriteBuffered(), true); + QVERIFY(canDevice->isWriteBuffered()); } void tst_QCanBusDevice::tst_waitForFramesReceived() { if (device->state() != QCanBusDevice::ConnectedState) { QVERIFY(device->connectDevice()); - QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, - 5000); + QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); } QVERIFY(!device->framesAvailable()); @@ -431,14 +416,14 @@ void tst_QCanBusDevice::tst_waitForFramesReceived() QVERIFY(!device->framesAvailable()); QTimer::singleShot(2000, [&]() { - device->emulateError(QString("TriggerError"), QCanBusDevice::ReadError); + device->emulateError(QStringLiteral("TriggerError"), QCanBusDevice::ReadError); }); elapsed.restart(); // error will be inserted after 2s result = device->waitForFramesReceived(8000); QVERIFY(!elapsed.hasExpired(8000)); QVERIFY(!result); - QCOMPARE(device->errorString(), QString("TriggerError")); + QCOMPARE(device->errorString(), QStringLiteral("TriggerError")); QCOMPARE(device->error(), QCanBusDevice::ReadError); // test recursive calling of waitForFramesReceived() behavior @@ -450,7 +435,7 @@ void tst_QCanBusDevice::tst_waitForFramesReceived() QTimer::singleShot(2000, [&]() { device->triggerNewFrame(); }); QObject::connect(device.data(), &QCanBusDevice::framesReceived, [this, &handleCounter]() { handleCounter++; - //this should trigger a recursion which we want to catch + // this should trigger a recursion which we want to catch device->waitForFramesReceived(5000); }); result = device->waitForFramesReceived(8000); @@ -462,26 +447,25 @@ void tst_QCanBusDevice::tst_waitForFramesWritten() { if (device->state() != QCanBusDevice::ConnectedState) { QVERIFY(device->connectDevice()); - QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, - 5000); + QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); } device->setWriteBuffered(false); bool result = device->waitForFramesWritten(1000); - QVERIFY(!result); //no buffer, wait not possible + QVERIFY(!result); // no buffer, waiting not possible device->setWriteBuffered(true); QVERIFY(device->framesToWrite() == 0); result = device->waitForFramesWritten(1000); - QVERIFY(!result); //nothing in buffer, nothing to wait for + QVERIFY(!result); // nothing in buffer, nothing to wait for QCanBusFrame frame; frame.setPayload(QByteArray("testData")); // test error case QTimer::singleShot(500, [&]() { - device->emulateError(QString("TriggerWriteError"), QCanBusDevice::ReadError); + device->emulateError(QStringLiteral("TriggerWriteError"), QCanBusDevice::ReadError); }); device->writeFrame(frame); QElapsedTimer elapsed; @@ -491,7 +475,7 @@ void tst_QCanBusDevice::tst_waitForFramesWritten() result = device->waitForFramesWritten(8000); QVERIFY(!elapsed.hasExpired(8000)); QVERIFY(!result); - QCOMPARE(device->errorString(), QString("TriggerWriteError")); + QCOMPARE(device->errorString(), QStringLiteral("TriggerWriteError")); QCOMPARE(device->error(), QCanBusDevice::ReadError); // flush remaining frames out to reset the test @@ -523,7 +507,7 @@ void tst_QCanBusDevice::tst_waitForFramesWritten() QTimer::singleShot(2000, [&]() { device->writeFrame(frame); }); QObject::connect(device.data(), &QCanBusDevice::framesWritten, [this, &handleCounter]() { handleCounter++; - //this should trigger a recursion which we want to catch + // this should trigger a recursion which we want to catch device->waitForFramesWritten(5000); }); result = device->waitForFramesWritten(8000); diff --git a/tests/auto/qcanbusframe/tst_qcanbusframe.cpp b/tests/auto/qcanbusframe/tst_qcanbusframe.cpp index 38c3784..96ebee1 100644 --- a/tests/auto/qcanbusframe/tst_qcanbusframe.cpp +++ b/tests/auto/qcanbusframe/tst_qcanbusframe.cpp @@ -34,8 +34,10 @@ ** ****************************************************************************/ -#include <QtTest/QtTest> -#include <QtSerialBus/QCanBusFrame> +#include <QtSerialBus/qcanbusframe.h> + +#include <QtCore/qdatastream.h> +#include <QtTest/qtest.h> class tst_QCanBusFrame : public QObject { @@ -54,6 +56,8 @@ private slots: void tst_isValid_data(); void tst_isValid(); + void tst_isValidSize_data(); + void tst_isValidSize(); void tst_toString_data(); void tst_toString(); @@ -330,7 +334,7 @@ void tst_QCanBusFrame::tst_isValid_data() << QByteArray(9, 0) << 512u << false << false; QTest::newRow("data frame CAN FD long payload") << QCanBusFrame::DataFrame << true - << QByteArray(9, 0) << 512u << false << true; + << QByteArray(12, 0) << 512u << false << true; QTest::newRow("data frame CAN FD too long payload") << QCanBusFrame::DataFrame << false << QByteArray(65, 0) << 512u << false << true; @@ -362,6 +366,46 @@ void tst_QCanBusFrame::tst_isValid() QCOMPARE(QCanBusFrame::InvalidFrame, frame.frameType()); } +void tst_QCanBusFrame::tst_isValidSize_data() +{ + QTest::addColumn<int>("payloadLength"); + QTest::addColumn<bool>("isFlexibleDataRate"); + QTest::addColumn<bool>("isValid"); + + QTest::newRow("2.0-0") << 0 << false << true; + QTest::newRow("2.0-8") << 8 << false << true; + QTest::newRow("2.0-9") << 9 << false << false; + + QTest::newRow("FD-0") << 0 << true << true; + QTest::newRow("FD-8") << 8 << true << true; + QTest::newRow("FD-9") << 9 << true << false; + QTest::newRow("FD-11") << 11 << true << false; + QTest::newRow("FD-12") << 12 << true << true; + QTest::newRow("FD-13") << 13 << true << false; + QTest::newRow("FD-16") << 16 << true << true; + + QTest::newRow("FD-20") << 20 << true << true; + QTest::newRow("FD-24") << 24 << true << true; + QTest::newRow("FD-32") << 32 << true << true; + QTest::newRow("FD-48") << 48 << true << true; + + QTest::newRow("FD-63") << 63 << true << false; + QTest::newRow("FD-64") << 64 << true << true; + QTest::newRow("FD-65") << 65 << true << false; +} + +void tst_QCanBusFrame::tst_isValidSize() +{ + QFETCH(int, payloadLength); + QFETCH(bool, isFlexibleDataRate); + QFETCH(bool, isValid); + + QCanBusFrame frame(0, QByteArray(payloadLength, ' ')); + frame.setFlexibleDataRateFormat(isFlexibleDataRate); + + QCOMPARE(frame.isValid(), isValid); +} + void tst_QCanBusFrame::tst_toString_data() { QTest::addColumn<QCanBusFrame::FrameType>("frameType"); @@ -568,7 +612,7 @@ void tst_QCanBusFrame::tst_error() QCOMPARE(frame.frameId(), 0u); //id of Error frame always 0 QCOMPARE(frame.error(), QCanBusFrame::TransmissionTimeoutError); - frame.setError(QCanBusFrame::FrameErrors(QCanBusFrame::ControllerError|QCanBusFrame::ProtocolViolationError)); + frame.setError(QCanBusFrame::FrameErrors(QCanBusFrame::ControllerError | QCanBusFrame::ProtocolViolationError)); QCOMPARE(frame.frameType(), QCanBusFrame::ErrorFrame); QCOMPARE(frame.frameId(), 0u); //id of Error frame always 0 QCOMPARE(frame.error(), @@ -576,7 +620,7 @@ void tst_QCanBusFrame::tst_error() frame.setFrameType(QCanBusFrame::RemoteRequestFrame); QCOMPARE(frame.frameType(), QCanBusFrame::RemoteRequestFrame); - QCOMPARE(frame.frameId(), (uint)(QCanBusFrame::ControllerError|QCanBusFrame::ProtocolViolationError)); + QCOMPARE(frame.frameId(), uint(QCanBusFrame::ControllerError | QCanBusFrame::ProtocolViolationError)); QCOMPARE(frame.error(), QCanBusFrame::NoError); } |