summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.qmake.conf2
-rw-r--r--config.tests/language/language.pro9
-rw-r--r--config.tests/language/main.cpp122
-rw-r--r--config.tests/socketcan/socketcan.pro2
-rw-r--r--config.tests/socketcan_fd/socketcan_fd.pro2
-rw-r--r--configure.json5
-rw-r--r--examples/serialbus/can/connectdialog.cpp14
-rw-r--r--examples/serialbus/can/connectdialog.h1
-rw-r--r--examples/serialbus/can/mainwindow.cpp69
-rw-r--r--examples/serialbus/can/mainwindow.ui98
-rw-r--r--examples/serialbus/modbus/adueditor/modbustcpclient_p.h4
-rw-r--r--qtserialbus.pro12
-rw-r--r--src/plugins/canbus/canbus.pro5
-rw-r--r--src/plugins/canbus/peakcan/main.cpp4
-rw-r--r--src/plugins/canbus/peakcan/peakcan_symbols_p.h15
-rw-r--r--src/plugins/canbus/peakcan/peakcanbackend.cpp168
-rw-r--r--src/plugins/canbus/peakcan/peakcanbackend_p.h3
-rw-r--r--src/plugins/canbus/socketcan/main.cpp3
-rw-r--r--src/plugins/canbus/socketcan/socketcanbackend.cpp17
-rw-r--r--src/plugins/canbus/tinycan/main.cpp4
-rw-r--r--src/plugins/canbus/tinycan/tinycan_symbols_p.h15
-rw-r--r--src/plugins/canbus/tinycan/tinycanbackend.cpp51
-rw-r--r--src/plugins/canbus/tinycan/tinycanbackend_p.h2
-rw-r--r--src/plugins/canbus/vectorcan/main.cpp69
-rw-r--r--src/plugins/canbus/vectorcan/plugin.json3
-rw-r--r--src/plugins/canbus/vectorcan/vectorcan.pro19
-rw-r--r--src/plugins/canbus/vectorcan/vectorcan_symbols_p.h484
-rw-r--r--src/plugins/canbus/vectorcan/vectorcanbackend.cpp490
-rw-r--r--src/plugins/canbus/vectorcan/vectorcanbackend.h77
-rw-r--r--src/plugins/canbus/vectorcan/vectorcanbackend_p.h94
-rw-r--r--src/serialbus/configure.json53
-rw-r--r--src/serialbus/doc/images/can-example.pngbin14937 -> 16453 bytes
-rw-r--r--src/serialbus/doc/qtserialbus.qdocconf1
-rw-r--r--src/serialbus/doc/src/peakcan.qdoc34
-rw-r--r--src/serialbus/doc/src/qtcanbus-backends.qdoc25
-rw-r--r--src/serialbus/doc/src/qtserialbus-index.qdoc1
-rw-r--r--src/serialbus/doc/src/socketcan.qdoc81
-rw-r--r--src/serialbus/doc/src/tinycan.qdoc33
-rw-r--r--src/serialbus/doc/src/vectorcan.qdoc92
-rw-r--r--src/serialbus/qcanbus.cpp56
-rw-r--r--src/serialbus/qcanbus.h7
-rw-r--r--src/serialbus/qcanbusdevice.cpp155
-rw-r--r--src/serialbus/qcanbusdevice.h3
-rw-r--r--src/serialbus/qcanbusdevice_p.h3
-rw-r--r--src/serialbus/qcanbusfactory.cpp14
-rw-r--r--src/serialbus/qcanbusfactory.h3
-rw-r--r--src/serialbus/qcanbusframe.cpp122
-rw-r--r--src/serialbus/qcanbusframe.h89
-rw-r--r--src/serialbus/qmodbusclient.cpp2
-rw-r--r--src/serialbus/qmodbusdataunit.cpp4
-rw-r--r--src/serialbus/qmodbusdevice.cpp2
-rw-r--r--src/serialbus/qmodbusdeviceidentification.cpp2
-rw-r--r--src/serialbus/qmodbuspdu.cpp8
-rw-r--r--src/serialbus/qmodbusreply.cpp2
-rw-r--r--src/serialbus/qmodbusrtuserialslave.cpp2
-rw-r--r--src/serialbus/qmodbusserver.cpp2
-rw-r--r--src/serialbus/qmodbustcpclient.cpp2
-rw-r--r--src/serialbus/qmodbustcpclient_p.h4
-rw-r--r--src/serialbus/qmodbustcpserver.cpp2
-rw-r--r--src/tools/canbusutil/canbusutil.cpp230
-rw-r--r--src/tools/canbusutil/canbusutil.h34
-rw-r--r--src/tools/canbusutil/main.cpp68
-rw-r--r--src/tools/canbusutil/readtask.cpp45
-rw-r--r--src/tools/canbusutil/readtask.h2
-rw-r--r--src/tools/tools.pro3
-rw-r--r--tests/auto/plugins/genericcanbus/dummybackend.cpp12
-rw-r--r--tests/auto/plugins/genericcanbus/dummybackend.h3
-rw-r--r--tests/auto/plugins/genericcanbus/main.cpp9
-rw-r--r--tests/auto/qcanbus/tst_qcanbus.cpp11
-rw-r--r--tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp222
-rw-r--r--tests/auto/qcanbusframe/tst_qcanbusframe.cpp211
72 files changed, 2641 insertions, 877 deletions
diff --git a/.gitignore b/.gitignore
index 9bc1090..98ece5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@ tests/auto/qmodbusserver/tst_qmodbusserver
*.ipch
*_wrapper.bat
*_wrapper.sh
+qtserialbus-config.pri
# Visual Studio generated files
*.ib_pdb_index
diff --git a/.qmake.conf b/.qmake.conf
index 45d16f2..556f554 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,4 +1,4 @@
load(qt_build_config)
CONFIG += warning_clean
-MODULE_VERSION = 5.7.1
+MODULE_VERSION = 5.8.0
diff --git a/config.tests/language/language.pro b/config.tests/language/language.pro
deleted file mode 100644
index 908d781..0000000
--- a/config.tests/language/language.pro
+++ /dev/null
@@ -1,9 +0,0 @@
-TEMPLATE = app
-
-CONFIG += qt
-CONFIG += c++11
-CONFIG += console
-
-QT = core
-
-SOURCES += main.cpp
diff --git a/config.tests/language/main.cpp b/config.tests/language/main.cpp
deleted file mode 100644
index 1d6732b..0000000
--- a/config.tests/language/main.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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 <QtCore/qglobal.h>
-
-#include <memory>
-#include <type_traits>
-
-struct Base
-{
- virtual void overrideMe() = 0;
-};
-
-struct Test : public Base
-{
- // strongly typed enums
- enum struct Enum : char {
- EnumA, EnumB
- };
-
- Test() = default; // defaulted and
- Test(const Test&) = delete; // deleted ctors
-
- explicit Test(int i)
- : Test(0, i) { }
- Test(int a, int i)
- : m_intA(a), m_intI(i) {}
-
- // std::is_same
- template <typename T, typename ... Ts> struct IsType { enum { value = false }; };
- template <typename T, typename T1, typename ... Ts> struct IsType<T, T1, Ts...> {
- enum { value = std::is_same<T, T1>::value || IsType<T, Ts...>::value };
- };
-
- // std::is_pod
- template <typename T> void test(T) const {
- static_assert(std::is_pod<T>::value, "Only POD supported.");
- static_assert(IsType<T, int>::value, "Only int supported.");
- }
-
- // variadics
- template <typename ... Args>
- void setValues(Args ... values)
- {
- const int size = sizeof...(Args);
- int tmp[size] = { (test(values), void(), '0')... };
- (void) (tmp);
- }
-
- // override keyword
- void overrideMe() override {}
-
- // const and constexpr
- Q_DECL_CONSTEXPR qint64 first() const Q_DECL_NOTHROW { return m_intA; }
- Q_DECL_RELAXED_CONSTEXPR void setFirst(int a) Q_DECL_NOTHROW { m_intA = a; }
-
- // non-static data member initializers
- int m_intA = 0;
- int m_intI = 0;
-};
-
-using func = int (*) (int, int);
-int total(int a, int i) { return a + i; }
-
-int main(int /*argc*/, char** /*argv*/)
-{
- // nullptr
- Test *t = nullptr;
- Test stackT(155);
- t = &stackT;
-
- // variadics
- t->setValues(1, 2, 3, 4, 5);
-
- // lambda and auto
- auto m = [](int a, int b) -> int {
- return a * b;
- };
-
- // decltype
- decltype(m) multiply = m;
- multiply(t->m_intA, t->m_intI);
-
- // alias templates
- func f = total;
- f(t->m_intA, t->m_intI);
-
- return 0;
-}
diff --git a/config.tests/socketcan/socketcan.pro b/config.tests/socketcan/socketcan.pro
index 104ea1e..3f6c643 100644
--- a/config.tests/socketcan/socketcan.pro
+++ b/config.tests/socketcan/socketcan.pro
@@ -1,4 +1,2 @@
-TEMPLATE = app
-
SOURCES += main.cpp
diff --git a/config.tests/socketcan_fd/socketcan_fd.pro b/config.tests/socketcan_fd/socketcan_fd.pro
index 104ea1e..3f6c643 100644
--- a/config.tests/socketcan_fd/socketcan_fd.pro
+++ b/config.tests/socketcan_fd/socketcan_fd.pro
@@ -1,4 +1,2 @@
-TEMPLATE = app
-
SOURCES += main.cpp
diff --git a/configure.json b/configure.json
new file mode 100644
index 0000000..c43610f
--- /dev/null
+++ b/configure.json
@@ -0,0 +1,5 @@
+{
+ "subconfigs": [
+ "src/serialbus"
+ ]
+}
diff --git a/examples/serialbus/can/connectdialog.cpp b/examples/serialbus/can/connectdialog.cpp
index 1c3ba43..43ded1d 100644
--- a/examples/serialbus/can/connectdialog.cpp
+++ b/examples/serialbus/can/connectdialog.cpp
@@ -76,7 +76,7 @@ ConnectDialog::ConnectDialog(QWidget *parent) :
m_ui->rawFilterEdit->hide();
m_ui->rawFilterLabel->hide();
- fillBackends();
+ m_ui->backendListBox->addItems(QCanBus::instance()->plugins());
fillSpeeds();
updateSettings();
@@ -108,11 +108,13 @@ void ConnectDialog::backendChanged(const QString &backend)
if (backend == QStringLiteral("generic"))
m_ui->interfaceNameEdit->setPlaceholderText(QStringLiteral("can0"));
else if (backend == QStringLiteral("peakcan"))
- m_ui->interfaceNameEdit->setPlaceholderText(QStringLiteral("usbbus1"));
+ m_ui->interfaceNameEdit->setPlaceholderText(QStringLiteral("usb0"));
else if (backend == QStringLiteral("socketcan"))
m_ui->interfaceNameEdit->setPlaceholderText(QStringLiteral("can0"));
else if (backend == QStringLiteral("tinycan"))
- m_ui->interfaceNameEdit->setPlaceholderText(QStringLiteral("channela"));
+ m_ui->interfaceNameEdit->setPlaceholderText(QStringLiteral("can0.0"));
+ else if (backend == QStringLiteral("vectorcan"))
+ m_ui->interfaceNameEdit->setPlaceholderText(QStringLiteral("can0"));
}
void ConnectDialog::ok()
@@ -234,12 +236,6 @@ void ConnectDialog::updateSettings()
}
}
-void ConnectDialog::fillBackends()
-{
- foreach (const QByteArray &backend, QCanBus::instance()->plugins())
- m_ui->backendListBox->addItem(backend);
-}
-
void ConnectDialog::fillSpeeds()
{
m_ui->speedBox->addItem(QStringLiteral("10000"), 10000);
diff --git a/examples/serialbus/can/connectdialog.h b/examples/serialbus/can/connectdialog.h
index f04f554..b7c4917 100644
--- a/examples/serialbus/can/connectdialog.h
+++ b/examples/serialbus/can/connectdialog.h
@@ -84,7 +84,6 @@ private:
QString configurationValue(QCanBusDevice::ConfigurationKey key);
void revertSettings();
void updateSettings();
- void fillBackends();
void fillSpeeds();
private:
diff --git a/examples/serialbus/can/mainwindow.cpp b/examples/serialbus/can/mainwindow.cpp
index 01101c9..b3b3a87 100644
--- a/examples/serialbus/can/mainwindow.cpp
+++ b/examples/serialbus/can/mainwindow.cpp
@@ -42,15 +42,12 @@
#include "ui_mainwindow.h"
#include "connectdialog.h"
-#include <QCanBusFrame>
#include <QCanBus>
+#include <QCanBusFrame>
#include <QCloseEvent>
+#include <QtDebug>
#include <QTimer>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/qdebug.h>
-
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
m_ui(new Ui::MainWindow),
@@ -95,6 +92,7 @@ void MainWindow::initActionsConnections()
connect(m_ui->actionDisconnect, &QAction::triggered, this, &MainWindow::disconnectDevice);
connect(m_ui->actionQuit, &QAction::triggered, this, &QWidget::close);
connect(m_ui->actionAboutQt, &QAction::triggered, qApp, &QApplication::aboutQt);
+ connect(m_ui->actionClearLog, &QAction::triggered, m_ui->receivedMessagesEdit, &QTextEdit::clear);
}
void MainWindow::receiveError(QCanBusDevice::CanBusError error) const
@@ -115,9 +113,12 @@ void MainWindow::connectDevice()
{
const ConnectDialog::Settings p = m_connectDialog->settings();
- m_canDevice = QCanBus::instance()->createDevice(p.backendName.toLocal8Bit(), p.deviceInterfaceName);
+ QString errorString;
+ m_canDevice = QCanBus::instance()->createDevice(p.backendName, p.deviceInterfaceName,
+ &errorString);
if (!m_canDevice) {
- showStatusMessage(tr("Error creating device: %1").arg(p.backendName));
+ showStatusMessage(tr("Error creating device '%1', reason: '%2'")
+ .arg(p.backendName).arg(errorString));
return;
}
@@ -184,51 +185,25 @@ void MainWindow::closeEvent(QCloseEvent *event)
event->accept();
}
-static QByteArray dataToHex(const QByteArray &data)
-{
- QByteArray result = data.toHex().toUpper();
-
- for (int i = 0; i < result.size(); i += 3)
- result.insert(i, ' ');
-
- return result;
-}
-
void MainWindow::checkMessages()
{
if (!m_canDevice)
return;
- const QCanBusFrame frame = m_canDevice->readFrame();
-
- const qint8 dataLength = frame.payload().size();
+ while (m_canDevice->framesAvailable()) {
+ const QCanBusFrame frame = m_canDevice->readFrame();
- const qint32 id = frame.frameId();
+ QString view;
+ if (frame.frameType() == QCanBusFrame::ErrorFrame)
+ interpretError(view, frame);
+ else
+ view = frame.toString();
- QString view;
- if (frame.frameType() == QCanBusFrame::ErrorFrame) {
- interpretError(view, frame);
- } else {
- const char *format =
- frame.hasExtendedFrameFormat() ? "Id: %08X" : "Id: %03X";
- view += QString::asprintf(format, static_cast<uint>(id));
- view += QLatin1String(" bytes: ");
- view += QString::number(dataLength, 10);
- if (frame.frameType() != QCanBusFrame::RemoteRequestFrame) {
- view += QLatin1String(" data: ");
- view += dataToHex(frame.payload());
- }
- }
+ const QString time = QString::fromLatin1("%1.%2 ")
+ .arg(frame.timeStamp().seconds(), 10, 10, QLatin1Char(' '))
+ .arg(frame.timeStamp().microSeconds() / 100, 4, 10, QLatin1Char('0'));
- if (frame.frameType() == QCanBusFrame::RemoteRequestFrame) {
- m_ui->requestList->addItem(view);
- m_ui->requestList->scrollToBottom();
- } else if (frame.frameType() == QCanBusFrame::ErrorFrame) {
- m_ui->errorList->addItem(view);
- m_ui->errorList->scrollToBottom();
- } else {
- m_ui->receiveList->addItem(view);
- m_ui->receiveList->scrollToBottom();
+ m_ui->receivedMessagesEdit->append(time + view);
}
}
@@ -248,10 +223,7 @@ void MainWindow::sendMessage() const
QCanBusFrame frame;
const int maxPayload = m_ui->fdBox->checkState() ? 64 : 8;
- int size = writings.size();
- if (size > maxPayload)
- size = maxPayload;
- writings = writings.left(size);
+ writings.truncate(maxPayload);
frame.setPayload(writings);
qint32 id = m_ui->idEdit->displayText().toInt(nullptr, 16);
@@ -260,6 +232,7 @@ void MainWindow::sendMessage() const
frame.setFrameId(id);
frame.setExtendedFrameFormat(m_ui->effBox->checkState());
+ frame.setFlexibleDataRateFormat(m_ui->fdBox->checkState());
if (m_ui->remoteFrame->isChecked())
frame.setFrameType(QCanBusFrame::RemoteRequestFrame);
diff --git a/examples/serialbus/can/mainwindow.ui b/examples/serialbus/can/mainwindow.ui
index 3db4218..38e1157 100644
--- a/examples/serialbus/can/mainwindow.ui
+++ b/examples/serialbus/can/mainwindow.ui
@@ -14,13 +14,13 @@
<string>CAN Example</string>
</property>
<widget class="QWidget" name="centralWidget">
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="0" column="0">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
<widget class="QGroupBox" name="sendMessagesBox">
<property name="title">
<string>Send CAN message</string>
</property>
- <layout class="QHBoxLayout" name="horizontalLayout">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
@@ -125,39 +125,52 @@ Otherwise, the standard format with 11 bit idenfier is used.</string>
</layout>
</widget>
</item>
- <item row="1" column="0">
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="0">
- <widget class="QLabel" name="label_4">
- <property name="text">
- <string>Messages</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string>Requests</string>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QLabel" name="label_6">
- <property name="text">
- <string>Errors</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QListWidget" name="receiveList"/>
- </item>
- <item row="1" column="1">
- <widget class="QListWidget" name="requestList"/>
- </item>
- <item row="1" column="2">
- <widget class="QListWidget" name="errorList"/>
- </item>
- </layout>
+ <item>
+ <widget class="QGroupBox" name="receivedMessagesBox">
+ <property name="title">
+ <string>Received CAN messages</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="font">
+ <font>
+ <family>Courier</family>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="text">
+ <string>Timestamp CAN-ID DLC Data</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTextEdit" name="receivedMessagesEdit">
+ <property name="font">
+ <font>
+ <family>Courier</family>
+ <kerning>false</kerning>
+ </font>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="lineWrapMode">
+ <enum>QTextEdit::NoWrap</enum>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</item>
</layout>
</widget>
@@ -177,6 +190,8 @@ Otherwise, the standard format with 11 bit idenfier is used.</string>
<addaction name="actionConnect"/>
<addaction name="actionDisconnect"/>
<addaction name="separator"/>
+ <addaction name="actionClearLog"/>
+ <addaction name="separator"/>
<addaction name="actionQuit"/>
</widget>
<widget class="QMenu" name="menuHelp">
@@ -197,6 +212,8 @@ Otherwise, the standard format with 11 bit idenfier is used.</string>
</attribute>
<addaction name="actionConnect"/>
<addaction name="actionDisconnect"/>
+ <addaction name="separator"/>
+ <addaction name="actionClearLog"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionConnect">
@@ -231,6 +248,15 @@ Otherwise, the standard format with 11 bit idenfier is used.</string>
<string>&amp;About Qt</string>
</property>
</action>
+ <action name="actionClearLog">
+ <property name="icon">
+ <iconset resource="can.qrc">
+ <normaloff>:/images/clear.png</normaloff>:/images/clear.png</iconset>
+ </property>
+ <property name="text">
+ <string>Clear &amp;Log</string>
+ </property>
+ </action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>
diff --git a/examples/serialbus/modbus/adueditor/modbustcpclient_p.h b/examples/serialbus/modbus/adueditor/modbustcpclient_p.h
index 50bcc04..5161487 100644
--- a/examples/serialbus/modbus/adueditor/modbustcpclient_p.h
+++ b/examples/serialbus/modbus/adueditor/modbustcpclient_p.h
@@ -85,7 +85,9 @@ public:
m_responseTimeoutDuration };
m_transactionStore.insert(m_tId, element);
- q->connect(q, &QModbusClient::timeoutChanged, element.timer.data(), &QTimer::setInterval);
+ using TypeId = void (QTimer::*)(int);
+ q->connect(q, &QModbusClient::timeoutChanged,
+ element.timer.data(), static_cast<TypeId>(&QTimer::setInterval));
QObject::connect(element.timer.data(), &QTimer::timeout, [this, writeToSocket]() {
if (!m_transactionStore.contains(m_tId))
return;
diff --git a/qtserialbus.pro b/qtserialbus.pro
index d0e94f7..469da33 100644
--- a/qtserialbus.pro
+++ b/qtserialbus.pro
@@ -4,16 +4,4 @@ lessThan(QT_MAJOR_VERSION, 5) {
message("Cannot build current QtSerialBus sources with Qt version $${QT_VERSION}.")
}
-load(configure)
-qtCompileTest(language)
-qtCompileTest(socketcan)
-qtCompileTest(socketcan_fd)
load(qt_parts)
-
-requires(config_language) # enforce defined set of C++11
-!config_language:warning("Cannot compile the module with your compiler, missing C++11 language features.")
-
-linux {
- !config_socketcan:warning("You need linux/can.h and linux/can/raw.h linux headers for socketCAN support, disabling it")
- !config_socketcan_fd:warning("Newer kernel needed for flexible data-rate frame support 'canfd_frame'")
-}
diff --git a/src/plugins/canbus/canbus.pro b/src/plugins/canbus/canbus.pro
index 342a8b7..0304dcc 100644
--- a/src/plugins/canbus/canbus.pro
+++ b/src/plugins/canbus/canbus.pro
@@ -1,7 +1,10 @@
TEMPLATE = subdirs
-config_socketcan {
+include($$OUT_PWD/../../serialbus/qtserialbus-config.pri)
+QT_FOR_CONFIG += serialbus-private
+qtConfig(socketcan) {
SUBDIRS += socketcan
}
SUBDIRS += peakcan tinycan
+win32:SUBDIRS += vectorcan
diff --git a/src/plugins/canbus/peakcan/main.cpp b/src/plugins/canbus/peakcan/main.cpp
index f76127d..00b4d36 100644
--- a/src/plugins/canbus/peakcan/main.cpp
+++ b/src/plugins/canbus/peakcan/main.cpp
@@ -51,11 +51,13 @@ class PeakCanBusPlugin : public QObject, public QCanBusFactory
public:
- QCanBusDevice *createDevice(const QString &interfaceName) const
+ QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
{
QString errorReason;
if (!PeakCanBackend::canCreate(&errorReason)) {
qWarning("%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 cb90599..456be17 100644
--- a/src/plugins/canbus/peakcan/peakcan_symbols_p.h
+++ b/src/plugins/canbus/peakcan/peakcan_symbols_p.h
@@ -258,7 +258,7 @@ typedef struct tagTPCANTimestamp
static fp_##symbolName symbolName;
#define RESOLVE_SYMBOL(symbolName) \
- symbolName = (fp_##symbolName)resolveSymbol(pcanLibrary, #symbolName); \
+ symbolName = (fp_##symbolName)pcanLibrary->resolve(#symbolName); \
if (!symbolName) \
return false;
@@ -273,23 +273,12 @@ GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetValue, TPCANHandle, TPCANParameter,
GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_SetValue, TPCANHandle, TPCANParameter, void *, quint32)
GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetErrorText, TPCANStatus, quint16, char *)
-inline QFunctionPointer resolveSymbol(QLibrary *pcanLibrary, const char *symbolName)
-{
- QFunctionPointer symbolFunctionPointer = pcanLibrary->resolve(symbolName);
- if (!symbolFunctionPointer)
- qWarning("Failed to resolve the pcanbasic symbol: %s", symbolName);
-
- return symbolFunctionPointer;
-}
-
inline bool resolveSymbols(QLibrary *pcanLibrary)
{
if (!pcanLibrary->isLoaded()) {
pcanLibrary->setFileName(QStringLiteral("pcanbasic"));
- if (!pcanLibrary->load()) {
- qWarning("Failed to load the library: %s", qPrintable(pcanLibrary->fileName()));
+ if (!pcanLibrary->load())
return false;
- }
}
RESOLVE_SYMBOL(CAN_Initialize)
diff --git a/src/plugins/canbus/peakcan/peakcanbackend.cpp b/src/plugins/canbus/peakcan/peakcanbackend.cpp
index 3f5d2c0..d8ddf76 100644
--- a/src/plugins/canbus/peakcan/peakcanbackend.cpp
+++ b/src/plugins/canbus/peakcan/peakcanbackend.cpp
@@ -214,13 +214,28 @@ bool PeakCanBackendPrivate::open()
return false;
}
- if (!acquireReadNotification()) {
- const TPCANStatus st = ::CAN_Uninitialize(channelIndex);
- if (st != PCAN_ERROR_OK)
- q->setError(systemErrorString(st), QCanBusDevice::ConnectionError);
+#if defined(Q_OS_WIN32)
+ if (readHandle == INVALID_HANDLE_VALUE) {
+ readHandle = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
+ if (!readHandle) {
+ q->setError(qt_error_string(::GetLastError()), QCanBusDevice::ConnectionError);
+ return false;
+ }
+ }
+#endif
+
+ const TPCANStatus err = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &readHandle, sizeof(readHandle));
+ if (err != PCAN_ERROR_OK) {
+ q->setError(systemErrorString(err), QCanBusDevice::ConnectionError);
return false;
}
+ writeNotifier = new WriteNotifier(this, q);
+ writeNotifier->setInterval(0);
+
+ readNotifier = new ReadNotifier(this, q);
+ readNotifier->setEnabled(true);
+
isOpen = true;
return true;
}
@@ -229,18 +244,31 @@ void PeakCanBackendPrivate::close()
{
Q_Q(PeakCanBackend);
- enableWriteNotification(false);
- releaseReadNotification();
+ delete readNotifier;
+ readNotifier = nullptr;
- if (writeNotifier) {
- delete writeNotifier;
- writeNotifier = nullptr;
- }
+ delete writeNotifier;
+ writeNotifier = nullptr;
+
+ quint32 value = 0;
+ const TPCANStatus err = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &value, sizeof(value));
+ if (err != PCAN_ERROR_OK)
+ emit q->setError(systemErrorString(err), QCanBusDevice::ConnectionError);
const TPCANStatus st = ::CAN_Uninitialize(channelIndex);
if (st != PCAN_ERROR_OK)
q->setError(systemErrorString(st), QCanBusDevice::ConnectionError);
+#if defined(Q_OS_WIN32)
+ if (readHandle && (readHandle != INVALID_HANDLE_VALUE)) {
+ if (!::CloseHandle(readHandle))
+ q->setError(qt_error_string(::GetLastError()), QCanBusDevice::ConnectionError);
+ readHandle = INVALID_HANDLE_VALUE;
+ }
+#else
+ readHandle = -1;
+#endif
+
isOpen = false;
}
@@ -260,37 +288,37 @@ bool PeakCanBackendPrivate::setConfigurationParameter(int key, const QVariant &v
static int channelIndexFromName(const QString &interfaceName)
{
- if (interfaceName == QStringLiteral("usbbus1"))
+ if (interfaceName == QStringLiteral("usb0"))
return PCAN_USBBUS1;
- else if (interfaceName == QStringLiteral("usbbus2"))
+ else if (interfaceName == QStringLiteral("usb1"))
return PCAN_USBBUS2;
- else if (interfaceName == QStringLiteral("usbbus3"))
+ else if (interfaceName == QStringLiteral("usb2"))
return PCAN_USBBUS3;
- else if (interfaceName == QStringLiteral("usbbus4"))
+ else if (interfaceName == QStringLiteral("usb3"))
return PCAN_USBBUS4;
- else if (interfaceName == QStringLiteral("usbbus5"))
+ else if (interfaceName == QStringLiteral("usb4"))
return PCAN_USBBUS5;
- else if (interfaceName == QStringLiteral("usbbus6"))
+ else if (interfaceName == QStringLiteral("usb5"))
return PCAN_USBBUS6;
- else if (interfaceName == QStringLiteral("usbbus7"))
+ else if (interfaceName == QStringLiteral("usb6"))
return PCAN_USBBUS7;
- else if (interfaceName == QStringLiteral("usbbus8"))
+ else if (interfaceName == QStringLiteral("usb7"))
return PCAN_USBBUS8;
- else if (interfaceName == QStringLiteral("pcibus1"))
+ else if (interfaceName == QStringLiteral("pci0"))
return PCAN_PCIBUS1;
- else if (interfaceName == QStringLiteral("pcibus2"))
+ else if (interfaceName == QStringLiteral("pci1"))
return PCAN_PCIBUS2;
- else if (interfaceName == QStringLiteral("pcibus3"))
+ else if (interfaceName == QStringLiteral("pci2"))
return PCAN_PCIBUS3;
- else if (interfaceName == QStringLiteral("pcibus4"))
+ else if (interfaceName == QStringLiteral("pci3"))
return PCAN_PCIBUS4;
- else if (interfaceName == QStringLiteral("pcibus5"))
+ else if (interfaceName == QStringLiteral("pci4"))
return PCAN_PCIBUS5;
- else if (interfaceName == QStringLiteral("pcibus6"))
+ else if (interfaceName == QStringLiteral("pci5"))
return PCAN_PCIBUS6;
- else if (interfaceName == QStringLiteral("pcibus7"))
+ else if (interfaceName == QStringLiteral("pci6"))
return PCAN_PCIBUS7;
- else if (interfaceName == QStringLiteral("pcibus8"))
+ else if (interfaceName == QStringLiteral("pci7"))
return PCAN_PCIBUS8;
else // TODO: Add other indexes here
return PCAN_NONEBUS;
@@ -317,30 +345,12 @@ QString PeakCanBackendPrivate::systemErrorString(int errorCode)
return QString::fromLatin1(buffer);
}
-void PeakCanBackendPrivate::enableWriteNotification(bool enable)
-{
- Q_Q(PeakCanBackend);
-
- if (writeNotifier) {
- if (enable) {
- if (!writeNotifier->isActive())
- writeNotifier->start();
- } else {
- writeNotifier->stop();
- }
- } else if (enable) {
- writeNotifier = new WriteNotifier(this, q);
- writeNotifier->setInterval(0);
- writeNotifier->start();
- }
-}
-
void PeakCanBackendPrivate::startWrite()
{
Q_Q(PeakCanBackend);
if (!q->hasOutgoingFrames()) {
- enableWriteNotification(false);
+ writeNotifier->stop();
return;
}
@@ -365,62 +375,8 @@ void PeakCanBackendPrivate::startWrite()
else
emit q->framesWritten(qint64(1));
- if (q->hasOutgoingFrames())
- enableWriteNotification(true);
-}
-
-bool PeakCanBackendPrivate::acquireReadNotification()
-{
- Q_Q(PeakCanBackend);
-
-#if defined(Q_OS_WIN32)
- if (readHandle == INVALID_HANDLE_VALUE) {
- readHandle = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
- if (!readHandle) {
- q->setError(qt_error_string(::GetLastError()), QCanBusDevice::ReadError);
- return false;
- }
- }
-#endif
-
- const TPCANStatus st = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT,
- &readHandle, sizeof(readHandle));
- if (st != PCAN_ERROR_OK) {
- q->setError(systemErrorString(st), QCanBusDevice::ReadError);
- return false;
- }
-
- if (!readNotifier) {
- readNotifier = new ReadNotifier(this, q);
- readNotifier->setEnabled(true);
- }
-
- return true;
-}
-
-void PeakCanBackendPrivate::releaseReadNotification()
-{
- Q_Q(PeakCanBackend);
-
- quint32 value = 0;
- const TPCANStatus st = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &value, sizeof(value));
- if (st != PCAN_ERROR_OK)
- q->setError(systemErrorString(st), QCanBusDevice::ConnectionError);
-
- if (readNotifier) {
- delete readNotifier;
- readNotifier = nullptr;
- }
-
-#if defined(Q_OS_WIN32)
- if (readHandle && (readHandle != INVALID_HANDLE_VALUE)) {
- if (!::CloseHandle(readHandle))
- q->setError(qt_error_string(::GetLastError()), QCanBusDevice::ConnectionError);
- readHandle = INVALID_HANDLE_VALUE;
- }
-#else
- readHandle = -1;
-#endif
+ if (q->hasOutgoingFrames() && !writeNotifier->isActive())
+ writeNotifier->start();
}
void PeakCanBackendPrivate::startRead()
@@ -444,12 +400,12 @@ 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 % 1000) + timestamp.micros;
- frame.setTimeStamp(QCanBusFrame::TimeStamp(millis / 1000, micros));
+ const quint64 micros = Q_UINT64_C(1000) * millis + timestamp.micros;
+ frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(micros));
frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED);
frame.setFrameType((message.MSGTYPE & PCAN_MESSAGE_RTR) ? QCanBusFrame::RemoteRequestFrame : QCanBusFrame::DataFrame);
- newFrames.append(frame);
+ newFrames.append(std::move(frame));
}
q->enqueueReceivedFrames(newFrames);
@@ -563,7 +519,9 @@ bool PeakCanBackend::writeFrame(const QCanBusFrame &newData)
}
enqueueOutgoingFrame(newData);
- d->enableWriteNotification(true);
+
+ if (!d->writeNotifier->isActive())
+ d->writeNotifier->start();
return true;
}
diff --git a/src/plugins/canbus/peakcan/peakcanbackend_p.h b/src/plugins/canbus/peakcan/peakcanbackend_p.h
index 88f39f5..9222b83 100644
--- a/src/plugins/canbus/peakcan/peakcanbackend_p.h
+++ b/src/plugins/canbus/peakcan/peakcanbackend_p.h
@@ -75,10 +75,7 @@ public:
void setupChannel(const QString &interfaceName);
void setupDefaultConfigurations();
QString systemErrorString(int errorCode);
- void enableWriteNotification(bool enable);
void startWrite();
- bool acquireReadNotification();
- void releaseReadNotification();
void startRead();
bool verifyBitRate(int bitrate);
diff --git a/src/plugins/canbus/socketcan/main.cpp b/src/plugins/canbus/socketcan/main.cpp
index e83b569..eb13cea 100644
--- a/src/plugins/canbus/socketcan/main.cpp
+++ b/src/plugins/canbus/socketcan/main.cpp
@@ -53,8 +53,9 @@ class SocketCanBusPlugin : public QObject, public QCanBusFactory
public:
- QCanBusDevice *createDevice(const QString &interfaceName) const
+ QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
{
+ Q_UNUSED(errorMessage);
auto device = new SocketCanBackend(interfaceName);
return device;
}
diff --git a/src/plugins/canbus/socketcan/socketcanbackend.cpp b/src/plugins/canbus/socketcan/socketcanbackend.cpp
index 60f36f9..8974279 100644
--- a/src/plugins/canbus/socketcan/socketcanbackend.cpp
+++ b/src/plugins/canbus/socketcan/socketcanbackend.cpp
@@ -368,7 +368,7 @@ bool SocketCanBackend::writeFrame(const QCanBusFrame &newData)
qWarning() << QString("payload (%1 bytes) is too large for chosen frame size of "
"maximal %2 bytes. Frame is discarded.").
arg(payloadSize).arg(canFdOptionEnabled ? CANFD_MAX_DLEN : CAN_MAX_DLEN);
- if (!canFdOptionEnabled && payloadSize <= CANFD_MAX_DLEN)
+ if (!canFdOptionEnabled && newData.hasFlexibleDataRateFormat())
setError(tr("Sending CAN FD frame although CAN FD option not enabled."),
QCanBusDevice::WriteError);
else
@@ -378,7 +378,7 @@ bool SocketCanBackend::writeFrame(const QCanBusFrame &newData)
}
qint64 bytesWritten = 0;
- if (canFdOptionEnabled) {
+ if (newData.hasFlexibleDataRateFormat()) {
canfd_frame frame;
frame.len = newData.payload().size();
frame.can_id = canId;
@@ -594,16 +594,13 @@ void SocketCanBackend::readSocket()
if (ioctl(canSocket, SIOCGSTAMP, &timeStamp) < 0) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ReadError);
- timeStamp.tv_sec = 0;
- timeStamp.tv_usec = 0;
+ memset(&timeStamp, 0, sizeof(timeStamp));
}
- QCanBusFrame::TimeStamp stamp;
- stamp.setSeconds(timeStamp.tv_sec);
- stamp.setMicroSeconds(timeStamp.tv_usec);
-
+ const QCanBusFrame::TimeStamp stamp(timeStamp.tv_sec, timeStamp.tv_usec);
QCanBusFrame bufferedFrame;
bufferedFrame.setTimeStamp(stamp);
+ bufferedFrame.setFlexibleDataRateFormat(bytesReceived == CANFD_MTU);
bufferedFrame.setExtendedFrameFormat(frame.can_id & CAN_EFF_FLAG);
Q_ASSERT(frame.len <= CANFD_MAX_DLEN);
@@ -615,10 +612,10 @@ void SocketCanBackend::readSocket()
bufferedFrame.setFrameId(frame.can_id & CAN_EFF_MASK);
- QByteArray load(reinterpret_cast<char *>(frame.data), frame.len);
+ const QByteArray load(reinterpret_cast<char *>(frame.data), frame.len);
bufferedFrame.setPayload(load);
- newFrames.append(bufferedFrame);
+ newFrames.append(std::move(bufferedFrame));
}
enqueueReceivedFrames(newFrames);
diff --git a/src/plugins/canbus/tinycan/main.cpp b/src/plugins/canbus/tinycan/main.cpp
index e8a084c..fe988ba 100644
--- a/src/plugins/canbus/tinycan/main.cpp
+++ b/src/plugins/canbus/tinycan/main.cpp
@@ -51,11 +51,13 @@ class TinyCanBusPlugin : public QObject, public QCanBusFactory
public:
- QCanBusDevice *createDevice(const QString &interfaceName) const
+ QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
{
QString errorReason;
if (!TinyCanBackend::canCreate(&errorReason)) {
qWarning("%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 5ad149c..6e05b92 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)resolveSymbol(mhstcanLibrary, #symbolName); \
+ symbolName = (fp_##symbolName)mhstcanLibrary->resolve(#symbolName); \
if (!symbolName) \
return false;
@@ -317,23 +317,12 @@ GENERATE_SYMBOL_VARIABLE(void, CanSetRxEventCallback, CanRxEventCallback)
GENERATE_SYMBOL_VARIABLE(void, CanSetEvents, quint16)
GENERATE_SYMBOL_VARIABLE(quint32, CanEventStatus, void)
-inline QFunctionPointer resolveSymbol(QLibrary *mhstcanLibrary, const char *symbolName)
-{
- QFunctionPointer symbolFunctionPointer = mhstcanLibrary->resolve(symbolName);
- if (!symbolFunctionPointer)
- qWarning("Failed to resolve the mhstcan symbol: %s", symbolName);
-
- return symbolFunctionPointer;
-}
-
inline bool resolveSymbols(QLibrary *mhstcanLibrary)
{
if (!mhstcanLibrary->isLoaded()) {
mhstcanLibrary->setFileName(QStringLiteral("mhstcan"));
- if (!mhstcanLibrary->load()) {
- qWarning("Failed to load the library: %s", qPrintable(mhstcanLibrary->fileName()));
+ if (!mhstcanLibrary->load())
return false;
- }
}
RESOLVE_SYMBOL(CanInitDriver)
diff --git a/src/plugins/canbus/tinycan/tinycanbackend.cpp b/src/plugins/canbus/tinycan/tinycanbackend.cpp
index 2cee2d0..0961813 100644
--- a/src/plugins/canbus/tinycan/tinycanbackend.cpp
+++ b/src/plugins/canbus/tinycan/tinycanbackend.cpp
@@ -197,6 +197,9 @@ bool TinyCanBackendPrivate::open()
}
}
+ writeNotifier = new WriteNotifier(this, q);
+ writeNotifier->setInterval(0);
+
isOpen = true;
return true;
}
@@ -205,6 +208,9 @@ void TinyCanBackendPrivate::close()
{
Q_Q(TinyCanBackend);
+ delete writeNotifier;
+ writeNotifier = nullptr;
+
const int ret = ::CanDeviceClose(channelIndex);
if (ret < 0)
q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ConnectionError);
@@ -299,9 +305,9 @@ QString TinyCanBackendPrivate::systemErrorString(int errorCode)
static int channelIndexFromName(const QString &interfaceName)
{
- if (interfaceName == QStringLiteral("channela"))
+ if (interfaceName == QStringLiteral("can0.0"))
return INDEX_CAN_KANAL_A;
- else if (interfaceName == QStringLiteral("channelb"))
+ else if (interfaceName == QStringLiteral("can0.1"))
return INDEX_CAN_KANAL_B;
else
return INDEX_INVALID;
@@ -320,30 +326,12 @@ void TinyCanBackendPrivate::setupDefaultConfigurations()
q->setConfigurationParameter(QCanBusDevice::BitRateKey, 500000);
}
-void TinyCanBackendPrivate::enableWriteNotification(bool enable)
-{
- Q_Q(TinyCanBackend);
-
- if (writeNotifier) {
- if (enable) {
- if (!writeNotifier->isActive())
- writeNotifier->start();
- } else {
- writeNotifier->stop();
- }
- } else if (enable) {
- writeNotifier = new WriteNotifier(this, q);
- writeNotifier->setInterval(0);
- writeNotifier->start();
- }
-}
-
void TinyCanBackendPrivate::startWrite()
{
Q_Q(TinyCanBackend);
if (!q->hasOutgoingFrames()) {
- enableWriteNotification(false);
+ writeNotifier->stop();
return;
}
@@ -372,16 +360,8 @@ void TinyCanBackendPrivate::startWrite()
emit q->framesWritten(messagesToWrite);
}
- if (q->hasOutgoingFrames())
- enableWriteNotification(true);
-}
-
-bool TinyCanBackendPrivate::enableReadNotification()
-{
- ::CanSetRxEventCallback(&canRxEventCallback);
- ::CanSetEvents(EVENT_ENABLE_RX_MESSAGES);
-
- return true;
+ if (q->hasOutgoingFrames() && !writeNotifier->isActive())
+ writeNotifier->start();
}
// this method is called from the different thread!
@@ -431,7 +411,7 @@ void TinyCanBackendPrivate::startRead()
else
frame.setFrameType(QCanBusFrame::DataFrame);
- newFrames.append(frame);
+ newFrames.append(std::move(frame));
}
q->enqueueReceivedFrames(newFrames);
@@ -448,7 +428,8 @@ void TinyCanBackendPrivate::startupDriver()
return;
}
- enableReadNotification();
+ ::CanSetRxEventCallback(&canRxEventCallback);
+ ::CanSetEvents(EVENT_ENABLE_RX_MESSAGES);
} else if (driverRefCount < 0) {
qCritical("Wrong reference counter: %d", driverRefCount);
@@ -578,7 +559,9 @@ bool TinyCanBackend::writeFrame(const QCanBusFrame &newData)
}
enqueueOutgoingFrame(newData);
- d->enableWriteNotification(true);
+
+ if (!d->writeNotifier->isActive())
+ d->writeNotifier->start();
return true;
}
diff --git a/src/plugins/canbus/tinycan/tinycanbackend_p.h b/src/plugins/canbus/tinycan/tinycanbackend_p.h
index 14630dd..18c82ef 100644
--- a/src/plugins/canbus/tinycan/tinycanbackend_p.h
+++ b/src/plugins/canbus/tinycan/tinycanbackend_p.h
@@ -70,9 +70,7 @@ public:
QString systemErrorString(int errorCode);
void setupChannel(const QString &interfaceName);
void setupDefaultConfigurations();
- void enableWriteNotification(bool enable);
void startWrite();
- bool enableReadNotification();
void startRead();
void startupDriver();
void cleanupDriver();
diff --git a/src/plugins/canbus/vectorcan/main.cpp b/src/plugins/canbus/vectorcan/main.cpp
new file mode 100644
index 0000000..7be88b9
--- /dev/null
+++ b/src/plugins/canbus/vectorcan/main.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 "vectorcanbackend.h"
+
+#include <QtSerialBus/qcanbus.h>
+#include <QtSerialBus/qcanbusdevice.h>
+#include <QtSerialBus/qcanbusfactory.h>
+
+QT_BEGIN_NAMESPACE
+
+class VectorCanBusPlugin : public QObject, public QCanBusFactory
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QCanBusFactory" FILE "plugin.json")
+ Q_INTERFACES(QCanBusFactory)
+
+public:
+ QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
+ {
+ QString errorReason;
+ if (!VectorCanBackend::canCreate(&errorReason)) {
+ qWarning("%s", qPrintable(errorReason));
+ if (errorMessage)
+ *errorMessage = errorReason;
+ return nullptr;
+ }
+
+ auto device = new VectorCanBackend(interfaceName);
+ return device;
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/canbus/vectorcan/plugin.json b/src/plugins/canbus/vectorcan/plugin.json
new file mode 100644
index 0000000..de0002c
--- /dev/null
+++ b/src/plugins/canbus/vectorcan/plugin.json
@@ -0,0 +1,3 @@
+{
+ "Key": "vectorcan"
+}
diff --git a/src/plugins/canbus/vectorcan/vectorcan.pro b/src/plugins/canbus/vectorcan/vectorcan.pro
new file mode 100644
index 0000000..a43c061
--- /dev/null
+++ b/src/plugins/canbus/vectorcan/vectorcan.pro
@@ -0,0 +1,19 @@
+TARGET = qtvectorcanbus
+
+QT = core-private serialbus
+
+HEADERS += \
+ vectorcanbackend.h \
+ vectorcanbackend_p.h \
+ vectorcan_symbols_p.h
+
+SOURCES += \
+ main.cpp \
+ vectorcanbackend.cpp
+
+DISTFILES = plugin.json
+
+PLUGIN_TYPE = canbus
+PLUGIN_EXTENDS = serialbus
+PLUGIN_CLASS_NAME = VectorCanBusPlugin
+load(qt_plugin)
diff --git a/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h
new file mode 100644
index 0000000..429f50e
--- /dev/null
+++ b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h
@@ -0,0 +1,484 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 VECTORCAN_SYMBOLS_P_H
+#define VECTORCAN_SYMBOLS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifdef LINK_LIBVECTORCAN
+
+extern "C"
+{
+#include <vxlapi.h>
+}
+
+#else
+
+#include <QtCore/qlibrary.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qdebug.h>
+
+#ifdef Q_OS_WIN32
+# include <windows.h>
+#else
+# error "Unsupported platform"
+#endif
+
+// transceiver types: CAN Cab
+#define XL_TRANSCEIVER_TYPE_NONE 0x0000
+#define XL_TRANSCEIVER_TYPE_CAN_251 0x0001
+#define XL_TRANSCEIVER_TYPE_CAN_252 0x0002
+#define XL_TRANSCEIVER_TYPE_CAN_DNOPTO 0x0003
+#define XL_TRANSCEIVER_TYPE_CAN_SWC_PROTO 0x0005 // Prototype. Driver may latch-up.
+#define XL_TRANSCEIVER_TYPE_CAN_SWC 0x0006
+#define XL_TRANSCEIVER_TYPE_CAN_EVA 0x0007
+#define XL_TRANSCEIVER_TYPE_CAN_FIBER 0x0008
+#define XL_TRANSCEIVER_TYPE_CAN_1054_OPTO 0x000B // 1054 with optical isolation
+#define XL_TRANSCEIVER_TYPE_CAN_SWC_OPTO 0x000C // SWC with optical isolation
+#define XL_TRANSCEIVER_TYPE_CAN_B10011S 0x000D // B10011S truck-and-trailer
+#define XL_TRANSCEIVER_TYPE_CAN_1050 0x000E // 1050
+#define XL_TRANSCEIVER_TYPE_CAN_1050_OPTO 0x000F // 1050 with optical isolation
+#define XL_TRANSCEIVER_TYPE_CAN_1041 0x0010 // 1041
+#define XL_TRANSCEIVER_TYPE_CAN_1041_OPTO 0x0011 // 1041 with optical isolation
+#define XL_TRANSCEIVER_TYPE_LIN_6258_OPTO 0x0017 // Vector LINcab 6258opto with transceiver Infineon TLE6258
+#define XL_TRANSCEIVER_TYPE_LIN_6259_OPTO 0x0019 // Vector LINcab 6259opto with transceiver Infineon TLE6259
+#define XL_TRANSCEIVER_TYPE_DAIO_8444_OPTO 0x001D // Vector IOcab 8444 (8 dig.Inp.; 4 dig.Outp.; 4 ana.Inp.; 4 ana.Outp.)
+#define XL_TRANSCEIVER_TYPE_CAN_1041A_OPTO 0x0021 // 1041A with optical isolation
+#define XL_TRANSCEIVER_TYPE_LIN_6259_MAG 0x0023 // LIN transceiver 6259, with transceiver Infineon TLE6259, magnetically isolated, stress functionality
+#define XL_TRANSCEIVER_TYPE_LIN_7259_MAG 0x0025 // LIN transceiver 7259, with transceiver Infineon TLE7259, magnetically isolated, stress functionality
+#define XL_TRANSCEIVER_TYPE_LIN_7269_MAG 0x0027 // LIN transceiver 7269, with transceiver Infineon TLE7269, magnetically isolated, stress functionality
+#define XL_TRANSCEIVER_TYPE_CAN_1054_MAG 0x0033 // TJA1054, magnetically isolated, with selectable termination resistor (via 4th IO line)
+#define XL_TRANSCEIVER_TYPE_CAN_251_MAG 0x0035 // 82C250/251 or equivalent, magnetically isolated
+#define XL_TRANSCEIVER_TYPE_CAN_1050_MAG 0x0037 // TJA1050, magnetically isolated
+#define XL_TRANSCEIVER_TYPE_CAN_1040_MAG 0x0039 // TJA1040, magnetically isolated
+#define XL_TRANSCEIVER_TYPE_CAN_1041A_MAG 0x003B // TJA1041A, magnetically isolated
+#define XL_TRANSCEIVER_TYPE_TWIN_CAN_1041A_MAG 0x0080 // TWINcab with two TJA1041, magnetically isolated
+#define XL_TRANSCEIVER_TYPE_TWIN_LIN_7269_MAG 0x0081 // TWINcab with two 7259, Infineon TLE7259, magnetically isolated, stress functionality
+#define XL_TRANSCEIVER_TYPE_TWIN_CAN_1041AV2_MAG 0x0082 // TWINcab with two TJA1041, magnetically isolated
+#define XL_TRANSCEIVER_TYPE_TWIN_CAN_1054_1041A_MAG 0x0083 // TWINcab with TJA1054A and TJA1041A with magnetic isolation
+// transceiver types: CAN PiggyBack
+#define XL_TRANSCEIVER_TYPE_PB_CAN_251 0x0101
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1054 0x0103
+#define XL_TRANSCEIVER_TYPE_PB_CAN_251_OPTO 0x0105
+#define XL_TRANSCEIVER_TYPE_PB_CAN_SWC 0x010B
+// 0x010D not supported, 0x010F, 0x0111, 0x0113 reserved for future use!!
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1054_OPTO 0x0115
+#define XL_TRANSCEIVER_TYPE_PB_CAN_SWC_OPTO 0x0117
+#define XL_TRANSCEIVER_TYPE_PB_CAN_TT_OPTO 0x0119
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1050 0x011B
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1050_OPTO 0x011D
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1041 0x011F
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1041_OPTO 0x0121
+#define XL_TRANSCEIVER_TYPE_PB_LIN_6258_OPTO 0x0129 // LIN piggy back with transceiver Infineon TLE6258
+#define XL_TRANSCEIVER_TYPE_PB_LIN_6259_OPTO 0x012B // LIN piggy back with transceiver Infineon TLE6259
+#define XL_TRANSCEIVER_TYPE_PB_LIN_6259_MAG 0x012D // LIN piggy back with transceiver Infineon TLE6259, magnetically isolated, stress functionality
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1041A_OPTO 0x012F // CAN transceiver 1041A
+#define XL_TRANSCEIVER_TYPE_PB_LIN_7259_MAG 0x0131 // LIN piggy back with transceiver Infineon TLE7259, magnetically isolated, stress functionality
+#define XL_TRANSCEIVER_TYPE_PB_LIN_7269_MAG 0x0133 // LIN piggy back with transceiver Infineon TLE7269, magnetically isolated, stress functionality
+#define XL_TRANSCEIVER_TYPE_PB_CAN_251_MAG 0x0135 // 82C250/251 or compatible, magnetically isolated
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1050_MAG 0x0136 // TJA 1050, magnetically isolated
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1040_MAG 0x0137 // TJA 1040, magnetically isolated
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1041A_MAG 0x0138 // TJA 1041A, magnetically isolated
+#define XL_TRANSCEIVER_TYPE_PB_DAIO_8444_OPTO 0x0139 // optically isolated IO piggy
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1054_MAG 0x013B // TJA1054, magnetically isolated, with selectable termination resistor (via 4th IO line)
+#define XL_TRANSCEIVER_TYPE_CAN_1051_CAP_FIX 0x013C // TJA1051 - fixed transceiver on e.g. 16xx/8970
+#define XL_TRANSCEIVER_TYPE_DAIO_1021_FIX 0x013D // Onboard IO of VN1630/VN1640
+#define XL_TRANSCEIVER_TYPE_LIN_7269_CAP_FIX 0x013E // TLE7269 - fixed transceiver on 1611
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1051_CAP 0x013F // TJA 1051, capacitive isolated
+#define XL_TRANSCEIVER_TYPE_PB_CAN_SWC_7356_CAP 0x0140 // Single Wire NCV7356, capacitive isolated
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1055_CAP 0x0141 // TJA1055, capacitive isolated, with selectable termination resistor (via 4th IO line)
+#define XL_TRANSCEIVER_TYPE_PB_CAN_1057_CAP 0x0142 // TJA 1057, capacitive isolated
+// transceiver types: FlexRay PiggyBacks
+#define XL_TRANSCEIVER_TYPE_PB_FR_1080 0x0201 // TJA 1080
+#define XL_TRANSCEIVER_TYPE_PB_FR_1080_MAG 0x0202 // TJA 1080 magnetically isolated piggy
+#define XL_TRANSCEIVER_TYPE_PB_FR_1080A_MAG 0x0203 // TJA 1080A magnetically isolated piggy
+#define XL_TRANSCEIVER_TYPE_PB_FR_1082_CAP 0x0204 // TJA 1082 capacitive isolated piggy
+#define XL_TRANSCEIVER_TYPE_PB_FRC_1082_CAP 0x0205 // TJA 1082 capacitive isolated piggy with CANpiggy form factor
+
+#define XL_TRANSCEIVER_TYPE_ETH_BCM54810_FIX 0x0230 // Onboard Broadcom PHY on VN5610
+
+// IOpiggy 8642
+#define XL_TRANSCEIVER_TYPE_PB_DAIO_8642 0x0280 // Iopiggy for VN8900
+
+// transceiver Operation Modes
+#define XL_TRANSCEIVER_LINEMODE_NA ((quint32)0x0000)
+#define XL_TRANSCEIVER_LINEMODE_TWO_LINE ((quint32)0x0001)
+#define XL_TRANSCEIVER_LINEMODE_CAN_H ((quint32)0x0002)
+#define XL_TRANSCEIVER_LINEMODE_CAN_L ((quint32)0x0003)
+#define XL_TRANSCEIVER_LINEMODE_SWC_SLEEP ((quint32)0x0004) // SWC Sleep Mode.
+#define XL_TRANSCEIVER_LINEMODE_SWC_NORMAL ((quint32)0x0005) // SWC Normal Mode.
+#define XL_TRANSCEIVER_LINEMODE_SWC_FAST ((quint32)0x0006) // SWC High-Speed Mode.
+#define XL_TRANSCEIVER_LINEMODE_SWC_WAKEUP ((quint32)0x0007) // SWC Wakeup Mode.
+#define XL_TRANSCEIVER_LINEMODE_SLEEP ((quint32)0x0008)
+#define XL_TRANSCEIVER_LINEMODE_NORMAL ((quint32)0x0009)
+#define XL_TRANSCEIVER_LINEMODE_STDBY ((quint32)0x000a) // Standby for those who support it
+#define XL_TRANSCEIVER_LINEMODE_TT_CAN_H ((quint32)0x000b) // truck & trailer: operating mode single wire using CAN high
+#define XL_TRANSCEIVER_LINEMODE_TT_CAN_L ((quint32)0x000c) // truck & trailer: operating mode single wire using CAN low
+#define XL_TRANSCEIVER_LINEMODE_EVA_00 ((quint32)0x000d) // CANcab Eva
+#define XL_TRANSCEIVER_LINEMODE_EVA_01 ((quint32)0x000e) // CANcab Eva
+#define XL_TRANSCEIVER_LINEMODE_EVA_10 ((quint32)0x000f) // CANcab Eva
+#define XL_TRANSCEIVER_LINEMODE_EVA_11 ((quint32)0x0010) // CANcab Eva
+
+// transceiver Status Flags (not all used, but for compatibility reasons)
+#define XL_TRANSCEIVER_STATUS_PRESENT ((quint32)0x0001)
+#define XL_TRANSCEIVER_STATUS_POWER ((quint32)0x0002)
+#define XL_TRANSCEIVER_STATUS_MEMBLANK ((quint32)0x0004)
+#define XL_TRANSCEIVER_STATUS_MEMCORRUPT ((quint32)0x0008)
+#define XL_TRANSCEIVER_STATUS_POWER_GOOD ((quint32)0x0010)
+#define XL_TRANSCEIVER_STATUS_EXT_POWER_GOOD ((quint32)0x0020)
+#define XL_TRANSCEIVER_STATUS_NOT_SUPPORTED ((quint32)0x0040)
+
+// common event tags
+#define XL_RECEIVE_MSG ((quint16)0x0001)
+#define XL_CHIP_STATE ((quint16)0x0004)
+#define XL_TRANSCEIVER_INFO ((quint16)0x0006)
+#define XL_TRANSCEIVER (XL_TRANSCEIVER_INFO)
+#define XL_TIMER_EVENT ((quint16)0x0008)
+#define XL_TIMER (XL_TIMER_EVENT)
+#define XL_TRANSMIT_MSG ((quint16)0x000A)
+#define XL_SYNC_PULSE ((quint16)0x000B)
+#define XL_APPLICATION_NOTIFICATION ((quint16)0x000F)
+
+// CAN/CAN-FD event tags Rx
+#define XL_CAN_EV_TAG_RX_OK ((quint16)0x0400)
+#define XL_CAN_EV_TAG_RX_ERROR ((quint16)0x0401)
+#define XL_CAN_EV_TAG_TX_ERROR ((quint16)0x0402)
+#define XL_CAN_EV_TAG_TX_REQUEST ((quint16)0x0403)
+#define XL_CAN_EV_TAG_TX_OK ((quint16)0x0404)
+#define XL_CAN_EV_TAG_CHIP_STATE ((quint16)0x0409)
+
+// CAN/CAN-FD event tags Tx
+#define XL_CAN_EV_TAG_TX_MSG ((quint16)0x0440)
+#define XL_CAN_EV_TAG_TX_ERRFR ((quint16)0x0441)
+
+// Bus types
+#define XL_BUS_TYPE_NONE 0x00000000
+#define XL_BUS_TYPE_CAN 0x00000001
+
+#include <pshpack1.h>
+typedef quint64 XLaccess;
+typedef HANDLE XLhandle;
+
+// message flags
+#ifndef MAX_MSG_LEN
+#define MAX_MSG_LEN 8
+#endif
+
+// interface version for our events
+#define XL_INTERFACE_VERSION_V2 2
+#define XL_INTERFACE_VERSION_V3 3
+#define XL_INTERFACE_VERSION_V4 4
+//current version
+#define XL_INTERFACE_VERSION XL_INTERFACE_VERSION_V3
+
+#define XL_CAN_EXT_MSG_ID 0x80000000
+
+#define XL_CAN_MSG_FLAG_ERROR_FRAME 0x01
+#define XL_CAN_MSG_FLAG_OVERRUN 0x02 // Overrun in Driver or CAN Controller, previous msgs have been lost.
+#define XL_CAN_MSG_FLAG_NERR 0x04 // Line Error on Lowspeed
+#define XL_CAN_MSG_FLAG_WAKEUP 0x08 // High Voltage Message on Single Wire CAN
+#define XL_CAN_MSG_FLAG_REMOTE_FRAME 0x10
+#define XL_CAN_MSG_FLAG_RESERVED_1 0x20
+#define XL_CAN_MSG_FLAG_TX_COMPLETED 0x40 // Message Transmitted
+#define XL_CAN_MSG_FLAG_TX_REQUEST 0x80 // Transmit Message stored into Controller
+#define XL_CAN_MSG_FLAG_SRR_BIT_DOM 0x0200 // SRR bit in CAN message is dominant
+
+#define XL_EVENT_FLAG_OVERRUN 0x01 // Used in XLevent.flags
+
+// structure for XL_RECEIVE_MSG, XL_TRANSMIT_MSG (32 bytes)
+struct s_xl_can_msg {
+ unsigned long id;
+ quint16 flags;
+ quint16 dlc;
+ quint64 res1;
+ quint8 data[MAX_MSG_LEN];
+ quint64 res2;
+};
+static_assert(sizeof(s_xl_can_msg) == 32, "Invalid size of s_xl_can_msg structure");
+
+// defines for SET_OUTPUT_MODE
+#define XL_OUTPUT_MODE_SILENT 0 // switch CAN trx into default silent mode
+#define XL_OUTPUT_MODE_NORMAL 1 // switch CAN trx into normal mode
+#define XL_OUTPUT_MODE_TX_OFF 2 // switch CAN trx into silent mode with tx pin off
+#define XL_OUTPUT_MODE_SJA_1000_SILENT 3 // switch CAN trx into SJA1000 silent mode
+
+// Transceiver modes
+#define XL_TRANSCEIVER_EVENT_ERROR 1
+#define XL_TRANSCEIVER_EVENT_CHANGED 2
+
+// basic bus message structure
+union s_xl_tag_data {
+ struct s_xl_can_msg msg;
+};
+
+// event type definition (48 bytes)
+typedef struct s_xl_event {
+ quint8 tag;
+ quint8 chanIndex;
+ quint16 transId;
+ quint16 portHandle;
+ quint8 flags; // e.g. XL_EVENT_FLAG_OVERRUN
+ quint8 reserved;
+ quint64 timeStamp;
+ union s_xl_tag_data tagData; // 32 bytes
+} XLevent;
+static_assert(sizeof(s_xl_event) == 48, "Invalid size of s_xl_event structure");
+
+// build a channels mask from the channels index
+#define XL_CHANNEL_MASK(x) (quint64(1) << (x))
+
+#define XL_MAX_APPNAME 32
+
+// driver status
+typedef qint16 XLstatus;
+
+#define XL_SUCCESS 0
+#define XL_PENDING 1
+
+#define XL_ERR_QUEUE_IS_EMPTY 10
+#define XL_ERR_QUEUE_IS_FULL 11
+#define XL_ERR_TX_NOT_POSSIBLE 12
+#define XL_ERR_NO_LICENSE 14
+#define XL_ERR_WRONG_PARAMETER 101
+#define XL_ERR_TWICE_REGISTER 110
+#define XL_ERR_INVALID_CHAN_INDEX 111
+#define XL_ERR_INVALID_ACCESS 112
+#define XL_ERR_PORT_IS_OFFLINE 113
+#define XL_ERR_CHAN_IS_ONLINE 116
+#define XL_ERR_NOT_IMPLEMENTED 117
+#define XL_ERR_INVALID_PORT 118
+#define XL_ERR_HW_NOT_READY 120
+#define XL_ERR_CMD_TIMEOUT 121
+#define XL_ERR_HW_NOT_PRESENT 129
+#define XL_ERR_NOTIFY_ALREADY_ACTIVE 131
+#define XL_ERR_NO_RESOURCES 152
+#define XL_ERR_WRONG_CHIP_TYPE 153
+#define XL_ERR_WRONG_COMMAND 154
+#define XL_ERR_INVALID_HANDLE 155
+#define XL_ERR_RESERVED_NOT_ZERO 157
+#define XL_ERR_INIT_ACCESS_MISSING 158
+#define XL_ERR_CANNOT_OPEN_DRIVER 201
+#define XL_ERR_WRONG_BUS_TYPE 202
+#define XL_ERR_DLL_NOT_FOUND 203
+#define XL_ERR_INVALID_CHANNEL_MASK 204
+#define XL_ERR_NOT_SUPPORTED 205
+// special stream defines
+#define XL_ERR_CONNECTION_BROKEN 210
+#define XL_ERR_CONNECTION_CLOSED 211
+#define XL_ERR_INVALID_STREAM_NAME 212
+#define XL_ERR_CONNECTION_FAILED 213
+#define XL_ERR_STREAM_NOT_FOUND 214
+#define XL_ERR_STREAM_NOT_CONNECTED 215
+#define XL_ERR_QUEUE_OVERRUN 216
+#define XL_ERROR 255
+
+// defines for xlGetDriverConfig structures
+#define XL_MAX_LENGTH 31
+#define XL_CONFIG_MAX_CHANNELS 64
+
+// flags for channelCapabilities
+#define XL_CHANNEL_FLAG_TIME_SYNC_RUNNING 0x00000001
+#define XL_CHANNEL_FLAG_CANFD_SUPPORT 0x20000000
+
+// activate - channel flags
+#define XL_ACTIVATE_NONE 0
+#define XL_ACTIVATE_RESET_CLOCK 8
+
+#define XL_BUS_COMPATIBLE_CAN XL_BUS_TYPE_CAN
+
+// the following bus types can be used with the current cab / piggy
+#define XL_BUS_ACTIVE_CAP_CAN (XL_BUS_COMPATIBLE_CAN << 16)
+
+// acceptance filter
+#define XL_CAN_STD 01 // flag for standard ID's
+#define XL_CAN_EXT 02 // flag for extended ID's
+
+typedef struct {
+ quint32 busType;
+ union {
+ struct {
+ quint32 bitRate;
+ quint8 sjw;
+ quint8 tseg1;
+ quint8 tseg2;
+ quint8 sam;
+ quint8 outputMode;
+ } can;
+ quint8 raw[32];
+ } data;
+} XLbusParams;
+
+// porthandle
+#define XL_INVALID_PORTHANDLE (-1)
+typedef long XLportHandle, *pXLportHandle;
+
+// defines for FPGA core types (fpgaCoreCapabilities)
+#define XL_FPGA_CORE_TYPE_NONE 0
+#define XL_FPGA_CORE_TYPE_CAN 1
+
+// defines for special DeviceStatus
+#define XL_SPECIAL_DEVICE_STAT_FPGA_UPDATE_DONE 0x01 // automatic driver FPGA flashing done
+
+typedef struct s_xl_channel_config {
+ char name[XL_MAX_LENGTH + 1];
+ quint8 hwType; // HWTYPE_xxxx (see above)
+ quint8 hwIndex; // Index of the hardware (same type) (0,1,...)
+ quint8 hwChannel; // Index of the channel (same hardware) (0,1,...)
+ quint16 transceiverType; // TRANSCEIVER_TYPE_xxxx (see above)
+ quint16 transceiverState; // transceiver state (XL_TRANSCEIVER_STATUS...)
+ quint16 configError; // XL_CHANNEL_CONFIG_ERROR_XXX (see above)
+ quint8 channelIndex; // Global channel index (0,1,...)
+ quint64 channelMask; // Global channel mask (=1<<channelIndex)
+ quint32 channelCapabilities; // capabilities which are supported (e.g CHANNEL_FLAG_XXX)
+ quint32 channelBusCapabilities; // what buses are supported and which are possible to be activated (e.g. XXX_BUS_ACTIVE_CAP_CAN)
+
+ // channel
+ quint8 isOnBus; // The channel is on bus
+ quint32 connectedBusType; // currently selected bus
+ XLbusParams busParams;
+
+ quint32 driverVersion;
+ quint32 interfaceVersion; // version of interface with driver
+ quint32 raw_data[10];
+
+ quint32 serialNumber;
+ quint32 articleNumber;
+
+ char transceiverName[XL_MAX_LENGTH + 1]; // name for CANcab or another transceiver
+
+ quint32 specialCabFlags; // XL_SPECIAL_CAB_LIN_RECESSIVE_STRESS, XL_SPECIAL_CAB_LIN_DOMINANT_TIMEOUT flags
+ quint32 dominantTimeout; // Dominant Timeout in us.
+ quint8 dominantRecessiveDelay; // Delay in us.
+ quint8 recessiveDominantDelay; // Delay in us.
+ quint8 connectionInfo; // XL_CONNECTION_INFO_XXX
+ quint8 currentlyAvailableTimestamps; // XL_CURRENTLY_AVAILABLE_TIMESTAMP...
+ quint16 minimalSupplyVoltage; // Minimal Supply Voltage of the Cab/Piggy in 1/100 V
+ quint16 maximalSupplyVoltage; // Maximal Supply Voltage of the Cab/Piggy in 1/100 V
+ quint32 maximalBaudrate; // Maximal supported LIN baudrate
+ quint8 fpgaCoreCapabilities; // e.g.: XL_FPGA_CORE_TYPE_XXX
+ quint8 specialDeviceStatus; // e.g.: XL_SPECIAL_DEVICE_STAT_XXX
+ quint16 channelBusActiveCapabilities; // like channelBusCapabilities (but without core dependencies)
+ quint16 breakOffset; // compensation for edge asymmetry in ns
+ quint16 delimiterOffset; // compensation for edgdfde asymmetry in ns
+ quint32 reserved[3];
+} XLchannelConfig, *pXLchannelConfig;
+
+typedef struct s_xl_driver_config {
+ quint32 dllVersion;
+ quint32 channelCount; // total number of channels
+ quint32 reserved[10];
+ XLchannelConfig channel[XL_CONFIG_MAX_CHANNELS]; // [channelCount]
+} XLdriverConfig, *pXLdriverConfig;
+
+// structure for the acceptance filter
+typedef struct _XLacc_filt {
+ quint8 isSet;
+ unsigned long code;
+ unsigned long mask; // relevant = 1
+} XLaccFilt;
+
+// structure for the acceptance filter of one CAN chip
+typedef struct _XLacceptance {
+ XLaccFilt std;
+ XLaccFilt xtd;
+} XLacceptance;
+
+// defines for xlSetGlobalTimeSync
+#define XL_SET_TIMESYNC_NO_CHANGE ((unsigned long)0)
+#define XL_SET_TIMESYNC_ON ((unsigned long)1)
+#define XL_SET_TIMESYNC_OFF ((unsigned long)2)
+
+#include <poppack.h>
+
+#define GENERATE_SYMBOL_VARIABLE(returnType, symbolName, ...) \
+ typedef returnType (WINAPI *fp_##symbolName)(__VA_ARGS__); \
+ static fp_##symbolName symbolName;
+
+#define RESOLVE_SYMBOL(symbolName) \
+ symbolName = (fp_##symbolName)vectorcanLibrary->resolve(#symbolName); \
+ if (!symbolName) \
+ return false;
+
+GENERATE_SYMBOL_VARIABLE(XLstatus, xlOpenDriver, void)
+GENERATE_SYMBOL_VARIABLE(XLstatus, xlCloseDriver, void)
+GENERATE_SYMBOL_VARIABLE(XLstatus, xlGetDriverConfig, XLdriverConfig *)
+GENERATE_SYMBOL_VARIABLE(XLstatus, xlOpenPort, XLportHandle *, char *, XLaccess, XLaccess *, quint32, quint32, quint32)
+GENERATE_SYMBOL_VARIABLE(XLstatus, xlClosePort, XLportHandle)
+GENERATE_SYMBOL_VARIABLE(XLstatus, xlActivateChannel, XLportHandle, XLaccess, quint32, quint32)
+GENERATE_SYMBOL_VARIABLE(XLstatus, xlDeactivateChannel, XLportHandle, XLaccess)
+GENERATE_SYMBOL_VARIABLE(XLstatus, xlCanSetChannelBitrate, XLportHandle, XLaccess, quint32)
+GENERATE_SYMBOL_VARIABLE(XLstatus, xlCanTransmit, XLportHandle, XLaccess, quint32 *, void *)
+GENERATE_SYMBOL_VARIABLE(XLstatus, xlReceive, XLportHandle, quint32 *, XLevent *)
+GENERATE_SYMBOL_VARIABLE(XLstatus, xlSetNotification, XLportHandle, XLhandle *, int)
+GENERATE_SYMBOL_VARIABLE(char *, xlGetErrorString, XLstatus)
+
+inline bool resolveSymbols(QLibrary *vectorcanLibrary)
+{
+ if (!vectorcanLibrary->isLoaded()) {
+ vectorcanLibrary->setFileName(QStringLiteral("vxlapi"));
+ if (!vectorcanLibrary->load())
+ return false;
+ }
+
+ RESOLVE_SYMBOL(xlOpenDriver)
+ RESOLVE_SYMBOL(xlCloseDriver)
+ RESOLVE_SYMBOL(xlGetDriverConfig)
+ RESOLVE_SYMBOL(xlOpenPort)
+ RESOLVE_SYMBOL(xlClosePort)
+ RESOLVE_SYMBOL(xlActivateChannel)
+ RESOLVE_SYMBOL(xlDeactivateChannel)
+ RESOLVE_SYMBOL(xlCanSetChannelBitrate)
+ RESOLVE_SYMBOL(xlCanTransmit)
+ RESOLVE_SYMBOL(xlReceive)
+ RESOLVE_SYMBOL(xlSetNotification)
+ RESOLVE_SYMBOL(xlGetErrorString)
+
+ return true;
+}
+
+#endif
+
+#endif // VECTORCAN_SYMBOLS_P_H
diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend.cpp b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp
new file mode 100644
index 0000000..005b726
--- /dev/null
+++ b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp
@@ -0,0 +1,490 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 "vectorcanbackend.h"
+#include "vectorcanbackend_p.h"
+#include "vectorcan_symbols_p.h"
+
+#include <QtSerialBus/qcanbusdevice.h>
+
+#include <QtCore/qtimer.h>
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qwineventnotifier.h>
+#include <QtCore/qcoreapplication.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef LINK_LIBVECTORCAN
+Q_GLOBAL_STATIC(QLibrary, vectorcanLibrary)
+#endif
+
+bool VectorCanBackend::canCreate(QString *errorReason)
+{
+#ifdef LINK_LIBVECTORCAN
+ return true;
+#else
+ static bool symbolsResolved = resolveSymbols(vectorcanLibrary());
+ if (!symbolsResolved) {
+ *errorReason = vectorcanLibrary()->errorString();
+ return false;
+ }
+ return true;
+#endif
+}
+
+static int driverRefCount = 0;
+
+class ReadNotifier : public QWinEventNotifier
+{
+ // no Q_OBJECT macro!
+public:
+ explicit ReadNotifier(VectorCanBackendPrivate *d, QObject *parent)
+ : QWinEventNotifier(parent)
+ , dptr(d)
+ {
+ setHandle(dptr->readHandle);
+ }
+
+protected:
+ bool event(QEvent *e) override
+ {
+ if (e->type() == QEvent::WinEventAct) {
+ dptr->startRead();
+ return true;
+ }
+ return QWinEventNotifier::event(e);
+ }
+
+private:
+ VectorCanBackendPrivate *dptr;
+};
+
+class WriteNotifier : public QTimer
+{
+ // no Q_OBJECT macro!
+public:
+ WriteNotifier(VectorCanBackendPrivate *d, QObject *parent)
+ : QTimer(parent)
+ , dptr(d)
+ {
+ setInterval(0);
+ }
+
+protected:
+ void timerEvent(QTimerEvent *e) override
+ {
+ if (e->timerId() == timerId()) {
+ dptr->startWrite();
+ return;
+ }
+ QTimer::timerEvent(e);
+ }
+
+private:
+ VectorCanBackendPrivate *dptr;
+};
+
+
+VectorCanBackendPrivate::VectorCanBackendPrivate(VectorCanBackend *q)
+ : q_ptr(q)
+ , portHandle(XL_INVALID_PORTHANDLE)
+{
+ startupDriver();
+}
+
+VectorCanBackendPrivate::~VectorCanBackendPrivate()
+{
+ cleanupDriver();
+}
+
+bool VectorCanBackendPrivate::open()
+{
+ Q_Q(VectorCanBackend);
+
+ {
+ XLaccess permissionMask = channelMask;
+ const quint32 queueSize = 256;
+ const XLstatus status = ::xlOpenPort(&portHandle,
+ const_cast<char *>(qPrintable(qApp->applicationName())),
+ channelMask, &permissionMask, queueSize,
+ XL_INTERFACE_VERSION, XL_BUS_TYPE_CAN);
+
+ if (status != XL_SUCCESS || portHandle == XL_INVALID_PORTHANDLE) {
+ q->setError(systemErrorString(status),
+ QCanBusDevice::ConnectionError);
+ return false;
+ }
+ }
+
+ {
+ const XLstatus status = ::xlActivateChannel(portHandle, channelMask,
+ XL_BUS_TYPE_CAN, XL_ACTIVATE_RESET_CLOCK);
+ if (status != XL_SUCCESS) {
+ q->setError(systemErrorString(status),
+ QCanBusDevice::CanBusError::ConnectionError);
+ return false;
+ }
+ }
+
+ {
+ const int queueLevel = 1;
+ const XLstatus status = ::xlSetNotification(portHandle, &readHandle, queueLevel);
+ if (status != XL_SUCCESS) {
+ q->setError(systemErrorString(status),
+ QCanBusDevice::ConnectionError);
+ return false;
+ }
+ }
+
+ readNotifier = new ReadNotifier(this, q);
+ readNotifier->setEnabled(true);
+
+ writeNotifier = new WriteNotifier(this, q);
+
+ return true;
+}
+
+void VectorCanBackendPrivate::close()
+{
+ Q_Q(VectorCanBackend);
+
+ delete readNotifier;
+ readNotifier = nullptr;
+
+ delete writeNotifier;
+ writeNotifier = nullptr;
+
+ {
+ const XLstatus status = ::xlDeactivateChannel(portHandle, channelMask);
+ if (status != XL_SUCCESS) {
+ q->setError(systemErrorString(status),
+ QCanBusDevice::CanBusError::ConfigurationError);
+ }
+ }
+
+ {
+ const XLstatus status = ::xlClosePort(portHandle);
+ if (status != XL_SUCCESS) {
+ q->setError(systemErrorString(status),
+ QCanBusDevice::ConnectionError);
+ }
+ }
+
+ portHandle = XL_INVALID_PORTHANDLE;
+}
+
+bool VectorCanBackendPrivate::setConfigurationParameter(int key, const QVariant &value)
+{
+ Q_Q(VectorCanBackend);
+
+ switch (key) {
+ case QCanBusDevice::BitRateKey:
+ return setBitRate(value.toUInt());
+ default:
+ q->setError(VectorCanBackend::tr("Unsupported configuration key"),
+ QCanBusDevice::ConfigurationError);
+ return false;
+ }
+}
+
+void VectorCanBackendPrivate::setupChannel(const QString &interfaceName)
+{
+ if (interfaceName.startsWith(QStringLiteral("can"))) {
+ const QStringRef ref = interfaceName.midRef(3);
+ bool ok = false;
+ const int channelIndex = ref.toInt(&ok);
+ if (ok && (channelIndex >= 0 && channelIndex < XL_CONFIG_MAX_CHANNELS)) {
+ channelMask = XL_CHANNEL_MASK((channelIndex));
+ return;
+ }
+ }
+
+ qCritical() << "Unable to parse the channel from an interface";
+}
+
+void VectorCanBackendPrivate::setupDefaultConfigurations()
+{
+ Q_Q(VectorCanBackend);
+
+ q->setConfigurationParameter(QCanBusDevice::BitRateKey, 500000);
+}
+
+QString VectorCanBackendPrivate::systemErrorString(int errorCode) const
+{
+ if (const char *string = ::xlGetErrorString(errorCode))
+ return QString::fromUtf8(string);
+ return VectorCanBackend::tr("Unable to retrieve an error string");
+}
+
+void VectorCanBackendPrivate::startWrite()
+{
+ Q_Q(VectorCanBackend);
+
+ if (!q->hasOutgoingFrames()) {
+ writeNotifier->stop();
+ return;
+ }
+
+ const QCanBusFrame frame = q->dequeueOutgoingFrame();
+ const QByteArray payload = frame.payload();
+
+ XLevent event;
+ ::memset(&event, 0, sizeof(event));
+
+ event.tag = XL_TRANSMIT_MSG;
+
+ s_xl_can_msg &msg = event.tagData.msg;
+
+ msg.id = frame.frameId();
+ if (frame.hasExtendedFrameFormat())
+ msg.id |= XL_CAN_EXT_MSG_ID;
+
+ msg.dlc = payload.size();
+
+ if (frame.frameType() == QCanBusFrame::RemoteRequestFrame)
+ msg.flags |= XL_CAN_MSG_FLAG_REMOTE_FRAME; // we do not care about the payload
+ else if (frame.frameType() == QCanBusFrame::ErrorFrame)
+ msg.flags |= XL_CAN_MSG_FLAG_ERROR_FRAME; // we do not care about the payload
+ else
+ ::memcpy(msg.data, payload.constData(), sizeof(msg.data));
+
+ quint32 eventCount = 1;
+ const XLstatus status = ::xlCanTransmit(portHandle, channelMask,
+ &eventCount, &event);
+ if (status != XL_SUCCESS) {
+ q->setError(systemErrorString(status),
+ QCanBusDevice::WriteError);
+ } else {
+ emit q->framesWritten(qint64(eventCount));
+ }
+
+ if (q->hasOutgoingFrames())
+ writeNotifier->start();
+}
+
+void VectorCanBackendPrivate::startRead()
+{
+ Q_Q(VectorCanBackend);
+
+ QVector<QCanBusFrame> newFrames;
+
+ for (;;) {
+ quint32 eventCount = 1;
+ XLevent event;
+ ::memset(&event, 0, sizeof(event));
+
+ const XLstatus status = ::xlReceive(portHandle, &eventCount, &event);
+ if (status != XL_SUCCESS) {
+ if (status != XL_ERR_QUEUE_IS_EMPTY) {
+ q->setError(systemErrorString(status),
+ QCanBusDevice::ReadError);
+ }
+ break;
+ }
+
+ if (event.tag != XL_RECEIVE_MSG || event.portHandle != portHandle)
+ continue;
+
+ const s_xl_can_msg &msg = event.tagData.msg;
+
+ QCanBusFrame frame(msg.id, QByteArray(reinterpret_cast<const char *>(msg.data),
+ int(msg.dlc)));
+ frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(event.timeStamp / 1000));
+ frame.setExtendedFrameFormat(msg.id & XL_CAN_EXT_MSG_ID);
+ frame.setFrameType((msg.flags & XL_CAN_MSG_FLAG_REMOTE_FRAME)
+ ? QCanBusFrame::RemoteRequestFrame
+ : (msg.flags & XL_CAN_MSG_FLAG_ERROR_FRAME)
+ ? QCanBusFrame::ErrorFrame
+ : QCanBusFrame::DataFrame);
+
+ newFrames.append(std::move(frame));
+ }
+
+ q->enqueueReceivedFrames(newFrames);
+}
+
+void VectorCanBackendPrivate::startupDriver()
+{
+ Q_Q(VectorCanBackend);
+
+ if (driverRefCount == 0) {
+ const XLstatus status = ::xlOpenDriver();
+ if (status != XL_SUCCESS) {
+ q->setError(systemErrorString(status),
+ QCanBusDevice::CanBusError::ConnectionError);
+ return;
+ }
+ } else if (driverRefCount < 0) {
+ qCritical("Wrong reference counter: %d", driverRefCount);
+ return;
+ }
+
+ ++driverRefCount;
+}
+
+void VectorCanBackendPrivate::cleanupDriver()
+{
+ --driverRefCount;
+
+ if (driverRefCount < 0) {
+ qCritical("Wrong reference counter: %d", driverRefCount);
+ driverRefCount = 0;
+ } else if (driverRefCount == 0) {
+ ::xlCloseDriver();
+ }
+}
+
+bool VectorCanBackendPrivate::setBitRate(quint32 bitrate)
+{
+ Q_Q(VectorCanBackend);
+
+ if (q->state() != QCanBusDevice::UnconnectedState) {
+ const XLstatus status = ::xlCanSetChannelBitrate(portHandle, channelMask, bitrate);
+ if (status != XL_SUCCESS) {
+ q->setError(systemErrorString(status),
+ QCanBusDevice::CanBusError::ConfigurationError);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+VectorCanBackend::VectorCanBackend(const QString &name, QObject *parent)
+ : QCanBusDevice(parent)
+ , d_ptr(new VectorCanBackendPrivate(this))
+{
+ Q_D(VectorCanBackend);
+
+ d->setupChannel(name);
+ d->setupDefaultConfigurations();
+}
+
+VectorCanBackend::~VectorCanBackend()
+{
+ if (state() == ConnectedState)
+ close();
+
+ delete d_ptr;
+}
+
+bool VectorCanBackend::open()
+{
+ Q_D(VectorCanBackend);
+
+ if (!d->open()) {
+ close(); // sets UnconnectedState
+ return false;
+ }
+
+ const auto keys = configurationKeys();
+ for (int key : keys) {
+ const QVariant param = configurationParameter(key);
+ const bool success = d->setConfigurationParameter(key, param);
+ if (!success) {
+ qWarning() << "Cannot apply parameter:" << key
+ << "with value:" << param;
+ }
+ }
+
+ setState(QCanBusDevice::ConnectedState);
+ return true;
+}
+
+void VectorCanBackend::close()
+{
+ Q_D(VectorCanBackend);
+
+ d->close();
+
+ setState(QCanBusDevice::UnconnectedState);
+}
+
+void VectorCanBackend::setConfigurationParameter(int key, const QVariant &value)
+{
+ Q_D(VectorCanBackend);
+
+ if (d->setConfigurationParameter(key, value))
+ QCanBusDevice::setConfigurationParameter(key, value);
+}
+
+bool VectorCanBackend::writeFrame(const QCanBusFrame &newData)
+{
+ Q_D(VectorCanBackend);
+
+ if (state() != QCanBusDevice::ConnectedState)
+ return false;
+
+ if (!newData.isValid()) {
+ setError(tr("Cannot write invalid QCanBusFrame"),
+ QCanBusDevice::WriteError);
+ return false;
+ }
+
+ if (newData.frameType() != QCanBusFrame::DataFrame
+ && newData.frameType() != QCanBusFrame::RemoteRequestFrame
+ && newData.frameType() != QCanBusFrame::ErrorFrame) {
+ setError(tr("Unable to write a frame with unacceptable type"),
+ QCanBusDevice::WriteError);
+ return false;
+ }
+
+ // CAN FD frame format not implemented at this stage
+ if (newData.payload().size() > MAX_MSG_LEN) {
+ setError(tr("CAN FD frame format not supported."),
+ QCanBusDevice::WriteError);
+ return false;
+ }
+
+ enqueueOutgoingFrame(newData);
+
+ if (!d->writeNotifier->isActive())
+ d->writeNotifier->start();
+
+ return true;
+}
+
+// TODO: Implement me
+QString VectorCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame)
+{
+ Q_UNUSED(errorFrame);
+
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend.h b/src/plugins/canbus/vectorcan/vectorcanbackend.h
new file mode 100644
index 0000000..89966ed
--- /dev/null
+++ b/src/plugins/canbus/vectorcan/vectorcanbackend.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 VECTORCANBACKEND_H
+#define VECTORCANBACKEND_H
+
+#include <QtSerialBus/qcanbusframe.h>
+#include <QtSerialBus/qcanbusdevice.h>
+
+#include <QtCore/qvariant.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class VectorCanBackendPrivate;
+
+class VectorCanBackend : public QCanBusDevice
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(VectorCanBackend)
+ Q_DISABLE_COPY(VectorCanBackend)
+public:
+ explicit VectorCanBackend(const QString &name, QObject *parent = nullptr);
+ ~VectorCanBackend() override;
+
+ bool open() override;
+ void close() override;
+
+ void setConfigurationParameter(int key, const QVariant &value) override;
+
+ bool writeFrame(const QCanBusFrame &newData) override;
+
+ QString interpretErrorFrame(const QCanBusFrame &errorFrame) override;
+
+ static bool canCreate(QString *errorReason);
+
+private:
+ VectorCanBackendPrivate * const d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // VECTORCANBACKEND_H
diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend_p.h b/src/plugins/canbus/vectorcan/vectorcanbackend_p.h
new file mode 100644
index 0000000..dbb3903
--- /dev/null
+++ b/src/plugins/canbus/vectorcan/vectorcanbackend_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 VECTORCANBACKEND_P_H
+#define VECTORCANBACKEND_P_H
+
+#include "vectorcanbackend.h"
+
+#if defined(Q_OS_WIN32)
+# include <qt_windows.h>
+#else
+# error "Unsupported platform" // other stuff
+#endif
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+class QWinEventNotifier;
+class QTimer;
+
+class VectorCanBackendPrivate
+{
+ Q_DECLARE_PUBLIC(VectorCanBackend)
+public:
+ VectorCanBackendPrivate(VectorCanBackend *q);
+ ~VectorCanBackendPrivate();
+
+ bool open();
+ void close();
+ bool setConfigurationParameter(int key, const QVariant &value);
+ void setupChannel(const QString &interfaceName);
+ void setupDefaultConfigurations();
+ QString systemErrorString(int errorCode) const;
+ void startWrite();
+ void startRead();
+ void startupDriver();
+ void cleanupDriver();
+ bool setBitRate(quint32 bitrate);
+
+ VectorCanBackend * const q_ptr;
+
+ long portHandle;
+ quint64 channelMask = 0;
+ HANDLE readHandle = INVALID_HANDLE_VALUE;
+ QTimer *writeNotifier = nullptr;
+ QWinEventNotifier *readNotifier = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // VECTORCANBACKEND_P_H
diff --git a/src/serialbus/configure.json b/src/serialbus/configure.json
new file mode 100644
index 0000000..d640f57
--- /dev/null
+++ b/src/serialbus/configure.json
@@ -0,0 +1,53 @@
+{
+ "module": "serialbus",
+ "testDir": "../../config.tests",
+
+ "tests": {
+ "socketcan": {
+ "label": "Socket CAN",
+ "type": "compile",
+ "test": "socketcan"
+ },
+ "socketcan_fd": {
+ "label": "Socket CAN FD",
+ "type": "compile",
+ "test": "socketcan_fd"
+ }
+ },
+
+ "features": {
+ "socketcan": {
+ "label": "Socket CAN",
+ "condition": "config.linux && tests.socketcan",
+ "output": [ "privateFeature" ]
+ },
+ "socketcan_fd": {
+ "label": "Socket CAN FD",
+ "condition": "config.linux && features.socketcan && tests.socketcan_fd",
+ "output": [ "privateFeature"]
+ }
+ },
+
+ "report": [
+ {
+ "type": "note",
+ "condition": "config.linux && !features.socketcan",
+ "message": "QtSerialBus: Cannot find linux/can.h and linux/can/raw.h Linux headers for socketCAN support."
+ },
+ {
+ "type": "note",
+ "condition": "config.linux && features.socketcan && !features.socketcan_fd",
+ "message": "QtSerialBus: Newer kernel needed for flexible data-rate frame support (canfd_frame)."
+ }
+ ],
+
+ "summary": [
+ {
+ "section": "Qt SerialBus",
+ "entries": [
+ "socketcan",
+ "socketcan_fd"
+ ]
+ }
+ ]
+}
diff --git a/src/serialbus/doc/images/can-example.png b/src/serialbus/doc/images/can-example.png
index 861218f..4b44819 100644
--- a/src/serialbus/doc/images/can-example.png
+++ b/src/serialbus/doc/images/can-example.png
Binary files differ
diff --git a/src/serialbus/doc/qtserialbus.qdocconf b/src/serialbus/doc/qtserialbus.qdocconf
index d80d953..c84064c 100644
--- a/src/serialbus/doc/qtserialbus.qdocconf
+++ b/src/serialbus/doc/qtserialbus.qdocconf
@@ -43,4 +43,3 @@ imagedirs += images
navigation.landingpage = "Qt Serial Bus"
navigation.cppclassespage = "Qt Serial Bus C++ Classes"
-navigation.homepage = "Qt Documentation (Technology Preview)"
diff --git a/src/serialbus/doc/src/peakcan.qdoc b/src/serialbus/doc/src/peakcan.qdoc
index 3b18393..d47d776 100644
--- a/src/serialbus/doc/src/peakcan.qdoc
+++ b/src/serialbus/doc/src/peakcan.qdoc
@@ -26,40 +26,38 @@
****************************************************************************/
/*!
\page qtserialbus-peakcan-overview.html
- \title Using PeakCAN Backend
+ \title Using PeakCAN Plugin
- \brief Overview of how to use the PeakCAN backend.
+ \brief Overview of how to use the PeakCAN plugin.
- The PeakCAN backend encapsulates the low-level API to work with the
+ The PeakCAN plugin encapsulates the low-level API to work with the
\l{http://www.peak-system.com/}{PEAK-System} CAN adapters.
\section1 Creating CAN Bus Devices
- At first it is necessary to check that QCanBus provides the desired backend:
+ At first it is necessary to check that QCanBus provides the desired plugin:
\code
- foreach (const QByteArray &backend, QCanBus::instance()->plugins()) {
- if (backend == "peakcan") {
- // were found
- break;
- }
+ if (QCanBus::instance()->plugins().contains(QStringLiteral("peakcan"))) {
+ // plugin available
}
\endcode
- Where \e peakcan is the backend name.
+ Where \e peakcan is the plugin name.
Next, a connection to a specific interface can be established:
\code
- QCanBusDevice *device = QCanBus::instance()->createDevice("peakcan", QStringLiteral("usbbus1"));
+ QCanBusDevice *device = QCanBus::instance()->createDevice(
+ QStringLiteral("peakcan"), QStringLiteral("usb0"));
device->connectDevice();
\endcode
- Where \e usbbus1 is the active CAN interface name. The PeakCAN backend supports
- eight USB interfaces from \e usbbus1 to \e usbbus8 and eight PCI interfaces from
- \e pcibus1 to \e pcibus8.
+ Where \e usb0 is the active CAN interface name. The PeakCAN plugin supports
+ eight USB interfaces from \e usb0 to \e usb7 and eight PCI interfaces from
+ \e pci0 to \e pci7.
- \note Only the USB and PCI adapters are currently supported by this backend.
+ \note Only the USB and PCI adapters are currently supported by this plugin.
The device is now open for writing and reading CAN frames:
@@ -72,11 +70,11 @@
\endcode
The reading can be done using the \l {QCanBusDevice::}{readFrame()} method. The
- \l {QCanBusDevice::}{framesReceived()} signal is emitted when a new frame is available
- for reading:
+ \l {QCanBusDevice::}{framesReceived()} signal is emitted when at least one new frame
+ is available for reading:
\code
- QCanBusFrame frame = device->readFrame();
+ QCanBusFrame frame = device->readFrame();
\endcode
PeakCAN supports the following configurations that can be controlled through
diff --git a/src/serialbus/doc/src/qtcanbus-backends.qdoc b/src/serialbus/doc/src/qtcanbus-backends.qdoc
index a2f504e..239ccc3 100644
--- a/src/serialbus/doc/src/qtcanbus-backends.qdoc
+++ b/src/serialbus/doc/src/qtcanbus-backends.qdoc
@@ -41,30 +41,37 @@
The CAN Bus API provides some common API to access the CAN devices:
\list
+ \li QCanBus provides an API to create a QCanBusDevice from a chosen plugin.
\li QCanBusDevice provides an API for direct access to the CAN device.
\li QCanBusFrame defines a CAN frame that can be written and read from QCanBusDevice.
\endlist
+ \section1 CAN Bus Plugins
+
Multiple vendors provide CAN devices with varying APIs for access. The QtSerialBus module
- supports the following sets of CAN bus backends:
+ supports the following sets of CAN bus plugins:
\table
\header
\li Vendor
- \li Backend (key)
+ \li Plugin (key)
\li Brief description
\row
\li CAN over Linux sockets
- \li \l {Using SocketCAN Backend}{SocketCAN} (\c socketcan)
- \li CAN bus backend using Linux sockets and open source drivers.
+ \li \l {Using SocketCAN Plugin}{SocketCAN} (\c socketcan)
+ \li CAN bus plugin using Linux sockets and open source drivers.
\row
\li PEAK-System
- \li \l {Using PeakCAN Backend}{PeakCAN} (\c peakcan)
- \li CAN bus backend using the PEAK CAN adapters.
+ \li \l {Using PeakCAN Plugin}{PeakCAN} (\c peakcan)
+ \li CAN bus plugin using the PEAK CAN adapters.
\row
\li MHS Elektronik
- \li \l {Using TinyCAN Backend}{TinyCAN} (\c tinycan)
- \li CAN bus backend using the MHS CAN adapters.
+ \li \l {Using TinyCAN Plugin}{TinyCAN} (\c tinycan)
+ \li CAN bus plugin using the MHS CAN adapters.
+ \row
+ \li Vector Informatik
+ \li \l {Using VectorCAN Plugin}{VectorCAN} (\c vectorcan)
+ \li CAN bus plugin using the Vector CAN adapters.
\endtable
\section1 Implementing a Custom CAN Plugin
@@ -103,7 +110,7 @@
\endlist
The \l {QCanBusDevice::open()}{open()} and \l {QCanBusDevice::close()}{close()}
- methods are used in conjuntion with \l QCanBusDevice::connectDevice() and
+ methods are used in conjunction with \l QCanBusDevice::connectDevice() and
\l QCanBusDevice::disconnectDevice() respectively. Check the
function documentation for implementation details.
diff --git a/src/serialbus/doc/src/qtserialbus-index.qdoc b/src/serialbus/doc/src/qtserialbus-index.qdoc
index c5136d4..790341f 100644
--- a/src/serialbus/doc/src/qtserialbus-index.qdoc
+++ b/src/serialbus/doc/src/qtserialbus-index.qdoc
@@ -33,7 +33,6 @@
The Qt Serial Bus API provides classes and functions to access the various
industrial serial buses and protocols, such as CAN, ModBus, and others.
- This module is currently released as a \b {Technology Preview}.
\section1 Getting Started
diff --git a/src/serialbus/doc/src/socketcan.qdoc b/src/serialbus/doc/src/socketcan.qdoc
index a979103..87bbe79 100644
--- a/src/serialbus/doc/src/socketcan.qdoc
+++ b/src/serialbus/doc/src/socketcan.qdoc
@@ -26,33 +26,76 @@
****************************************************************************/
/*!
\page qtserialbus-socketcan-overview.html
- \title Using SocketCAN Backend
+ \title Using SocketCAN Plugin
- \brief Overview of how to use the SocketCAN backend.
+ \brief Overview of how to use the SocketCAN plugin.
- The SocketCAN backend encapsulates the Linux sockets API for accessing the CAN devices.
+ The SocketCAN plugin encapsulates the Linux sockets API for accessing the CAN devices.
This API is a set of open source CAN drivers and a networking stack contributed by
Volkswagen Research to the Linux kernel.
+ \section1 SocketCAN usage
+
+ To list all (including unconfigured) network interfaces, the command
+ \c{ifconfig -a} can be used.
+
+ To use SocketCAN, the corresponding Linux Kernel modules must be loaded
+ and the network interface must be configured.
+
+ \section2 Setting up real CAN hardware
+
+ This section assumes, that the device driver is already loaded
+ (most likely automatically when connecting the CAN hardware).
+
+ To set the device can0 to a bitrate of 250 kBit/s:
+ \code
+ sudo ip link set can0 type can bitrate 250000
+ sudo ip link set up can0
+ \endcode
+
+ \section2 Setting up a virtual CAN bus
+
+ \note For CAN FD usage, the MTU (Maximum Transmission Unit) has to be set
+ to 72 byte.
+
+ \code
+ sudo modprobe vcan
+ sudo ip link add dev vcan0 type vcan
+ sudo ip link set vcan0 mtu 72
+ sudo ip link set up vcan0
+ \endcode
+
+ The command line test programs used in the following are from
+ the \l{https://github.com/linux-can/can-utils}{can-utils} package:
+
+ \code
+ # Display received CAN messages
+ candump vcan0
+
+ # Send a CAN message
+ cansend vcan0 123##3112233445566778899aabbccddeeff
+
+ # Generate random CAN messages
+ cangen vcan0
+ \endcode
+
\section1 Creating CAN Bus Devices
- At first it is necessary to check that QCanBus provides the desired backend:
+ At first it is necessary to check that QCanBus provides the desired plugin:
\code
- foreach (const QByteArray &backend, QCanBus::instance()->plugins()) {
- if (backend == "socketcan") {
- // were found
- break;
- }
+ if (QCanBus::instance()->plugins().contains(QStringLiteral("socketcan"))) {
+ // plugin available
}
\endcode
- Where \e socketcan is the backend name.
+ Where \e socketcan is the plugin name.
Next, a connection to a specific interface can be established:
\code
- QCanBusDevice *device = QCanBus::instance()->createDevice("socketcan", QStringLiteral("can0"));
+ QCanBusDevice *device = QCanBus::instance()->createDevice(
+ QStringLiteral("socketcan"), QStringLiteral("can0"));
device->connectDevice();
\endcode
@@ -70,11 +113,11 @@
\endcode
The reading can be done using the \l {QCanBusDevice::}{readFrame()} method. The
- \l {QCanBusDevice::}{framesReceived()} signal is emitted when a new frame is available
- for reading:
+ \l {QCanBusDevice::}{framesReceived()} signal is emitted when at least one new frame
+ is available for reading:
\code
- QCanBusFrame frame = device->readFrame();
+ QCanBusFrame frame = device->readFrame();
\endcode
SocketCAN supports the following configurations that can be controlled through
@@ -87,10 +130,14 @@
\row
\li QCanBusDevice::LoopbackKey
\li To meet the multiple-user needs, the local loopback is enabled by default.
+ This means, whenever a CAN frame is transmitted on the CAN bus, a local
+ echo of this frame is sent to all applications connected to this CAN device.
\row
\li QCanBusDevice::ReceiveOwnKey
\li The reception of the CAN frames on the same socket that was sending the CAN frame
- is disabled by default.
+ is disabled by default. When enabling this option, all CAN frames sent to the CAN
+ bus immediately appear in the receive buffer. This can be used to check if sending
+ was successful.
\row
\li QCanBusDevice::ErrorFilterKey
\li A CAN interface driver can generate so called \e {Error Message Frames} that can
@@ -104,13 +151,13 @@
By default, the connection is configured to accept any CAN bus message.
\row
\li QCanBusDevice::BitRateKey
- \li This configuration is not supported by the socketcan backend. However
+ \li This configuration is not supported by the socketcan plugin. However
it is possible to set the rate when configuring the CAN network interface using
the \c {ip link} command.
\row
\li QCanBusDevice::CanFdKey
\li This configuration option determines whether CANFD frames may be sent or received.
- By default, this option is disabled. It controls controls the CAN_RAW_FD_FRAMES
+ By default, this option is disabled. It controls the CAN_RAW_FD_FRAMES
option of the CAN socket.
\endtable
diff --git a/src/serialbus/doc/src/tinycan.qdoc b/src/serialbus/doc/src/tinycan.qdoc
index da45775..a504198 100644
--- a/src/serialbus/doc/src/tinycan.qdoc
+++ b/src/serialbus/doc/src/tinycan.qdoc
@@ -26,39 +26,38 @@
****************************************************************************/
/*!
\page qtserialbus-tinycan-overview.html
- \title Using TinyCAN Backend
+ \title Using TinyCAN Plugin
- \brief Overview of how to use the TinyCAN backend.
+ \brief Overview of how to use the TinyCAN plugin.
- The TinyCAN backend encapsulates the low-level API to work with the
+ The TinyCAN plugin encapsulates the low-level API to work with the
\l{http://www.mhs-elektronik.de/}{MHS Elektronik} CAN adapters.
\section1 Creating CAN Bus Devices
- At first it is necessary to check that QCanBus provides the desired backend:
+ At first it is necessary to check that QCanBus provides the desired plugin:
\code
- foreach (const QByteArray &backend, QCanBus::instance()->plugins()) {
- if (backend == "tinycan") {
- // were found
- break;
- }
+ if (QCanBus::instance()->plugins().contains(QStringLiteral("tinycan"))) {
+ // plugin available
}
\endcode
- Where \e tinycan is the backend name.
+ Where \e tinycan is the plugin name.
Next, a connection to a specific interface can be established:
\code
- QCanBusDevice *device = QCanBus::instance()->createDevice("tinycan", QStringLiteral("channela"));
+ QCanBusDevice *device = QCanBus::instance()->createDevice(
+ QStringLiteral("tinycan"), QStringLiteral("channela"));
device->connectDevice();
\endcode
- Where \e channela is the active CAN interface name. The TinyCAN backend
- provides two interfaces, \e channela and \e channelb.
+ Where \e can0.0 is the active CAN interface name. TinyCAN provides the
+ interfaces \e can0.0 and \e can0.1, which maps to INDEX_CAN_KANAL_A resp.
+ INDEX_CAN_KANAL_B in the TinyCAN plugin.
- \note Only the USB adapters are currently supported by this backend.
+ \note Only the USB adapters are currently supported by this plugin.
The device is now open for writing and reading CAN frames:
@@ -71,11 +70,11 @@
\endcode
The reading can be done using the \l {QCanBusDevice::}{readFrame()} method. The
- \l {QCanBusDevice::}{framesReceived()} signal is emitted when new frames are available
- for reading:
+ \l {QCanBusDevice::}{framesReceived()} signal is emitted when at least one new frame
+ is available for reading:
\code
- QCanBusFrame frame = device->readFrame();
+ QCanBusFrame frame = device->readFrame();
\endcode
TinyCAN supports the following configurations that can be controlled through
diff --git a/src/serialbus/doc/src/vectorcan.qdoc b/src/serialbus/doc/src/vectorcan.qdoc
new file mode 100644
index 0000000..8163207
--- /dev/null
+++ b/src/serialbus/doc/src/vectorcan.qdoc
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: http://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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+/*!
+ \page qtserialbus-vectorcan-overview.html
+ \title Using VectorCAN Plugin
+
+ \brief Overview of how to use the VectorCAN plugin.
+
+ The VectorCAN plugin encapsulates the low-level API to work with the
+ \l{http://www.vector.com/}{Vector Informatik} CAN adapters.
+
+ \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("vectorcan"))) {
+ // plugin available
+ }
+ \endcode
+
+ Where \e vectorcan is the plugin name.
+
+ Next, a connection to a specific interface can be established:
+
+ \code
+ QCanBusDevice *device = QCanBus::instance()->createDevice(
+ QStringLiteral("vectorcan"), QStringLiteral("can0"));
+ device->connectDevice();
+ \endcode
+
+ Where \e can0 is the active CAN channel name. The VectorCAN plugin provides
+ 64 channels (defined by XL_CONFIG_MAX_CHANNELS in the Vector API) from \e can0
+ to \e can63. Some of these channels can be virtual, and therefore can be used
+ without actual CAN hardware. To find out the virtual channels, the program
+ "Vector Hardware Config" (vcanconf.exe) can be used, which is included in
+ Vector's driver package.
+
+ The device is now open for writing and reading CAN frames:
+
+ \code
+ QCanBusFrame frame;
+ frame.setFrameId(8);
+ QByteArray payload("A36E");
+ frame.setPayload(payload);
+ 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
+
+ VectorCAN supports the following configurations that can be controlled through
+ \l {QCanBusDevice::}{setConfigurationParameter()}:
+
+ \table
+ \header
+ \li Configuration parameter key
+ \li Description
+ \row
+ \li QCanBusDevice::BitRateKey
+ \li Determines the bit rate of the CAN bus connection.
+ \endtable
+*/
diff --git a/src/serialbus/qcanbus.cpp b/src/serialbus/qcanbus.cpp
index 85b824f..3e1704c 100644
--- a/src/serialbus/qcanbus.cpp
+++ b/src/serialbus/qcanbus.cpp
@@ -64,7 +64,7 @@ public:
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qFactoryLoader,
(QCanBusFactory_iid, QLatin1String("/canbus")))
-typedef QMap<QByteArray, QCanBusPrivate> QCanBusPluginStore;
+typedef QMap<QString, QCanBusPrivate> QCanBusPluginStore;
Q_GLOBAL_STATIC(QCanBusPluginStore, qCanBusPlugins)
static QCanBus *globalInstance = nullptr;
@@ -80,19 +80,18 @@ static void loadPlugins()
QCanBusPrivate d;
d.index = i;
d.meta = obj;
- qCanBusPlugins()->insert(
- obj.value(QStringLiteral("Key")).toString().toLatin1(), d);
+ qCanBusPlugins()->insert(obj.value(QStringLiteral("Key")).toString(), d);
}
}
/*!
\class QCanBus
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
- \brief The QCanBus class handles registration and creation of bus backends.
+ \brief The QCanBus class handles registration and creation of bus plugins.
- QCanBus loads Qt CAN Bus plugins at runtime. The ownership of serial bus backends is
+ QCanBus loads Qt CAN Bus plugins at runtime. The ownership of serial bus plugins is
transferred to the loader.
*/
@@ -110,37 +109,60 @@ QCanBus *QCanBus::instance()
/*!
Returns a list of identifiers for all loaded plugins.
*/
-QList<QByteArray> QCanBus::plugins() const
+QStringList QCanBus::plugins() const
{
return qCanBusPlugins()->keys();
}
+static void setErrorMessage(QString *result, const QString &message)
+{
+ if (!result)
+ return;
+
+ *result = message;
+}
+
/*!
Creates a CAN bus device. \a plugin is the name of the plugin as returned by the \l plugins()
- method. \a interfaceName is the CAN bus interface name.
+ method. \a interfaceName is the CAN bus interface name. In case of failure, the optional
+ parameter \a errorMessage returns a textual error description.
- Ownership of the returned backend is transferred to the caller.
+ Ownership of the returned plugin is transferred to the caller.
Returns \c nullptr if no suitable device can be found.
+
+ For example, the following call would connect to the SocketCAN interface vcan0:
+
+ \code
+ QCanBusDevice *device = QCanBus::instance()->createDevice(
+ QStringLiteral("socketcan"), QStringLiteral("vcan0"));
+ device->connectDevice();
+ \endcode
+
+ \note The \a interfaceName is plugin-dependent. See the corresponding plugin documentation
+ for more information: \l {CAN Bus Plugins}.
*/
-QCanBusDevice *QCanBus::createDevice(const QByteArray &plugin,
- const QString &interfaceName) const
+QCanBusDevice *QCanBus::createDevice(const QString &plugin, const QString &interfaceName,
+ QString *errorMessage) const
{
- if (!qCanBusPlugins()->contains(plugin))
+ if (!qCanBusPlugins()->contains(plugin)) {
+ setErrorMessage(errorMessage, tr("No such plugin: '%1'").arg(plugin));
return nullptr;
+ }
QCanBusPrivate d = qCanBusPlugins()->value(plugin);
if (!d.factory) {
d.factory
= qobject_cast<QCanBusFactory *>(qFactoryLoader->instance(d.index));
- if (!d.factory)
- return nullptr;
- qCanBusPlugins()->insert(plugin, d);
+ if (d.factory)
+ qCanBusPlugins()->insert(plugin, d);
}
- if (!d.factory)
+ if (!d.factory) {
+ setErrorMessage(errorMessage, tr("No factory for plugin: '%1'").arg(plugin));
return nullptr;
+ }
- return d.factory->createDevice(interfaceName);
+ return d.factory->createDevice(interfaceName, errorMessage);
}
QCanBus::QCanBus(QObject *parent) :
diff --git a/src/serialbus/qcanbus.h b/src/serialbus/qcanbus.h
index 97bf35e..3f03400 100644
--- a/src/serialbus/qcanbus.h
+++ b/src/serialbus/qcanbus.h
@@ -51,10 +51,11 @@ class Q_SERIALBUS_EXPORT QCanBus : public QObject
public:
static QCanBus *instance();
- QList<QByteArray> plugins() const;
+ QStringList plugins() const;
- QCanBusDevice *createDevice(const QByteArray &plugin,
- const QString &interfaceName) const;
+ QCanBusDevice *createDevice(const QString &plugin,
+ const QString &interfaceName,
+ QString *errorMessage = nullptr) const;
private:
QCanBus(QObject *parent = nullptr);
diff --git a/src/serialbus/qcanbusdevice.cpp b/src/serialbus/qcanbusdevice.cpp
index 00505bf..c80753a 100644
--- a/src/serialbus/qcanbusdevice.cpp
+++ b/src/serialbus/qcanbusdevice.cpp
@@ -41,18 +41,21 @@
#include <QtCore/qdebug.h>
#include <QtCore/qdatastream.h>
+#include <QtCore/qeventloop.h>
+#include <QtCore/qscopedvaluerollback.h>
+#include <QtCore/qtimer.h>
QT_BEGIN_NAMESPACE
/*!
\class QCanBusDevice
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief The QCanBusDevice class is the interface class for CAN bus.
- QCanBusDevice communicates with a CAN backend providing users with a convenient API.
- The CAN backend must be specified during the object creation.
+ QCanBusDevice communicates with a CAN plugin providing users with a convenient API.
+ The CAN plugin must be specified during the object creation.
*/
/*!
@@ -62,7 +65,7 @@ QT_BEGIN_NAMESPACE
\value NoError No errors have occurred.
\value ReadError An error occurred during a read operation.
\value WriteError An error occurred during a write operation.
- \value ConnectionError An error occurred when attempting to open the backend.
+ \value ConnectionError An error occurred when attempting to open the plugin.
\value ConfigurationError An error occurred when attempting to set a configuration
parameter.
\value UnknownError An unknown error occurred.
@@ -91,15 +94,17 @@ QT_BEGIN_NAMESPACE
\value ErrorFilterKey This key defines the type of error that should be
forwarded via the current connection. The associated
value should be of type \l QCanBusFrame::FrameErrors.
- \value LoopbackKey This key defines whether the CAN bus device should
- operate in loopback mode. The expected value for this
- key is \c bool.
- \value ReceiveOwnKey This key defines whether this CAN device can send messages.
+ \value LoopbackKey This key defines whether the CAN bus device should operate in loopback
+ mode. Loopback means, whenever a CAN frame is transmitted on the CAN
+ bus, a local echo of this frame is sent to all applications connected to
+ this CAN device. The expected value for this key is \c bool.
+ \value ReceiveOwnKey This key defines whether this CAN device receives its own send frames.
+ This can be used to check if the transmission was successful.
The expected value for this key is \c bool.
\value BitRateKey This key defines the bitrate in bits per second.
\value CanFdKey This key defines whether sending and receiving of CAN FD frames
should be enabled. The expected value for this key is \c bool.
- \value UserKey This key defines the range where custom keys start. It's most
+ \value UserKey This key defines the range where custom keys start. Its most
common purpose is to permit platform-specific configuration
options.
@@ -109,12 +114,12 @@ QT_BEGIN_NAMESPACE
/*!
\class QCanBusDevice::Filter
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
- \brief The QCanBusDevice::Filter struct defines a filter for CAN bus messages.
+ \brief The QCanBusDevice::Filter struct defines a filter for CAN bus frames.
A list of QCanBusDevice::Filter instances is passed to
- \l QCanBusDevice::setConfigurationParameter() to enable filtering. If a received CAN message
+ \l QCanBusDevice::setConfigurationParameter() to enable filtering. If a received CAN frame
matches at least one of the filters in the list, the QCanBusDevice will accept it.
The example below demonstrates how to use the struct:
@@ -125,20 +130,20 @@ QT_BEGIN_NAMESPACE
/*!
\enum QCanBusDevice::Filter::FormatFilter
This enum describes the format pattern, which is used to filter incoming
- CAN bus messages.
+ CAN bus frames.
- \value MatchBaseFormat The CAN bus message must use the base message format
+ \value MatchBaseFormat The CAN bus frame must use the base frame format
(11 bit identifier).
- \value MatchExtendedFormat The CAN bus message must use the extended message format
+ \value MatchExtendedFormat The CAN bus frame must use the extended frame format
(29 bit identifier).
- \value MatchBaseAndExtendedFormat The CAN bus message can have a base or an extended
- message format.
+ \value MatchBaseAndExtendedFormat The CAN bus frame can have a base or an extended
+ frame format.
*/
/*!
\variable QCanBusDevice::Filter::frameId
- \brief the frame id used to filter the incoming messages.
+ \brief The frame id used to filter the incoming frames.
The frameId is used in conjunction with \a frameIdMask.
The matching is successful if the following evaluates to \c true:
@@ -155,7 +160,7 @@ QT_BEGIN_NAMESPACE
/*!
\variable QCanBusDevice::Filter::frameIdMask
- \brief the bit mask that is applied to the frame id of the filter and the received message.
+ \brief The bit mask that is applied to the frame id of the filter and the received frame.
The two frame ids are matching if the following evaluates to \c true:
@@ -171,9 +176,9 @@ QT_BEGIN_NAMESPACE
/*!
\variable QCanBusDevice::Filter::type
- \brief the type of the message to be filtered.
+ \brief The type of the frame to be filtered.
- Any CAN bus message type can be matched by setting this variable
+ Any CAN bus frame type can be matched by setting this variable
to \l QCanBusFrame::InvalidFrame. The filter object is invalid if
type is equal to \l QCanBusFrame::UnknownFrame.
@@ -185,7 +190,7 @@ QT_BEGIN_NAMESPACE
/*!
\variable QCanBusDevice::Filter::format
- \brief the message format of the matching CAN bus message.
+ \brief The frame format of the matching CAN bus frame.
By default this field is set to \l QCanBusDevice::Filter::MatchBaseAndExtendedFormat.
*/
@@ -383,8 +388,6 @@ QString QCanBusDevice::errorString() const
}
/*!
- \fn qint64 QCanBusDevice::framesAvailable() const
-
Returns the number of available frames. If no frames are available,
this function returns 0.
@@ -396,8 +399,6 @@ qint64 QCanBusDevice::framesAvailable() const
}
/*!
- \fn qint64 QCanBusDevice::framesToWrite() const
-
Returns the number of frames waiting to be written.
\sa writeFrame()
@@ -408,6 +409,106 @@ qint64 QCanBusDevice::framesToWrite() const
}
/*!
+ For buffered devices, this function waits until all buffered frames
+ have been written to the device and the \l framesWritten() signal has been emitted,
+ or until \a msecs milliseconds have passed. If \a msecs is -1,
+ this function will not time out. For unbuffered devices, it returns immediately with \c false
+ as \l writeFrame() does not require a write buffer.
+
+ Returns \c true if the \l framesWritten() signal is emitted;
+ otherwise returns \c false (i.e. if the operation timed out, or if an error occurred).
+
+ \note This function will start a local event loop. This may lead to scenarios whereby
+ other application slots may be called while the execution of this function scope is blocking.
+ To avoid problems, the signals for this class should not be connected to slots.
+ Similarly this function must never be called in response to the \l framesWritten()
+ or \l errorOccurred() signals.
+
+ \sa waitForFramesReceived()
+ */
+bool QCanBusDevice::waitForFramesWritten(int msecs)
+{
+ // do not enter this function recursively
+ if (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");
+ return false;
+ }
+
+ QScopedValueRollback<bool> guard(d_func()->waitForWrittenEntered);
+ d_func()->waitForWrittenEntered = true;
+
+ if (d_func()->state != ConnectedState)
+ return false;
+
+ if (!framesToWrite())
+ return false; // nothing pending, nothing to wait upon
+
+ QEventLoop loop;
+ connect(this, &QCanBusDevice::framesWritten, &loop, [&]() { loop.exit(0); });
+ connect(this, &QCanBusDevice::errorOccurred, &loop, [&]() { loop.exit(1); });
+ if (msecs >= 0)
+ QTimer::singleShot(msecs, &loop, [&]() { loop.exit(2); });
+
+ int result = 0;
+ while (framesToWrite() > 0) {
+ // wait till all written or time out
+ result = loop.exec(QEventLoop::ExcludeUserInputEvents);
+ if (result > 0)
+ return false;
+ }
+ return true;
+}
+
+/*!
+ Blocks until new frames are available for reading and the \l framesReceived()
+ signal has been emitted, or until \a msecs milliseconds have passed. If
+ \a msecs is \c -1, this function will not time out.
+
+ Returns \c true if new frames are available for reading and the \l framesReceived()
+ signal is emitted; otherwise returns \c false (if the operation timed out
+ or if an error occurred).
+
+ \note This function will start a local event loop. This may lead to scenarios whereby
+ other application slots may be called while the execution of this function scope is blocking.
+ To avoid problems, the signals for this class should not be connected to slots.
+ Similarly this function must never be called in response to the \l framesReceived()
+ or \l errorOccurred() signals.
+
+ \sa waitForFramesWritten()
+ */
+bool QCanBusDevice::waitForFramesReceived(int msecs)
+{
+ // do not enter this function recursively
+ if (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");
+ return false;
+ }
+
+ QScopedValueRollback<bool> guard(d_func()->waitForReceivedEntered);
+ d_func()->waitForReceivedEntered = true;
+
+ if (d_func()->state != ConnectedState)
+ return false;
+
+ QEventLoop loop;
+
+ connect(this, &QCanBusDevice::framesReceived, &loop, [&]() { loop.exit(0); });
+ connect(this, &QCanBusDevice::errorOccurred, &loop, [&]() { loop.exit(1); });
+ if (msecs >= 0)
+ QTimer::singleShot(msecs, &loop, [&]() { loop.exit(2); });
+
+ int result = loop.exec(QEventLoop::ExcludeUserInputEvents);
+
+ return result == 0;
+}
+
+/*!
\fn bool QCanBusDevice::open()
This function is called by connectDevice(). Subclasses must provide
@@ -493,7 +594,7 @@ QCanBusFrame QCanBusDevice::readFrame()
As per CAN bus specification, frames of type
\l {QCanBusFrame::RemoteRequestFrame} {remote transfer request (RTR)}
- do not have a payload but require a size larger than zero. This size indicates
+ do not have a payload, but a length from 0 to 8 (including). This length indicates
the expected response payload length from the remote party. Therefore when sending a RTR frame using
this function it may still be required to set an arbitrary payload on \a frame. The length of
the arbitrary payload is what is set as size expectation for the RTR frame.
diff --git a/src/serialbus/qcanbusdevice.h b/src/serialbus/qcanbusdevice.h
index 5700aeb..6a015af 100644
--- a/src/serialbus/qcanbusdevice.h
+++ b/src/serialbus/qcanbusdevice.h
@@ -106,6 +106,9 @@ public:
qint64 framesAvailable() const;
qint64 framesToWrite() const;
+ virtual bool waitForFramesWritten(int msecs);
+ virtual bool waitForFramesReceived(int msecs);
+
// TODO rename these once QIODevice dependency has been removed
bool connectDevice();
void disconnectDevice();
diff --git a/src/serialbus/qcanbusdevice_p.h b/src/serialbus/qcanbusdevice_p.h
index 033a0f7..db067c5 100644
--- a/src/serialbus/qcanbusdevice_p.h
+++ b/src/serialbus/qcanbusdevice_p.h
@@ -75,6 +75,9 @@ public:
QMutex incomingFramesGuard;
QVector<QCanBusFrame> outgoingFrames;
QVector<ConfigEntry> configOptions;
+
+ bool waitForReceivedEntered = false;
+ bool waitForWrittenEntered = false;
};
QT_END_NAMESPACE
diff --git a/src/serialbus/qcanbusfactory.cpp b/src/serialbus/qcanbusfactory.cpp
index 968e16d..b3df416 100644
--- a/src/serialbus/qcanbusfactory.cpp
+++ b/src/serialbus/qcanbusfactory.cpp
@@ -41,7 +41,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QCanBusFactory
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief The QCanBusFactory class is a factory class used as the
plugin interface for CAN bus plugins.
@@ -50,13 +50,15 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn QCanBusDevice *QCanBusFactory::createDevice(const QString &interfaceName) const
+ \fn QCanBusDevice *QCanBusFactory::createDevice(const QString &interfaceName,
+ QString *errorMessage) const
- Creates a new QCanBusDevice.
- \a interfaceName is the network/CAN interface name. The caller must take
- ownership of the returned pointer.
+ Creates a new QCanBusDevice. The caller must take ownership of the returned pointer.
- If the factory cannot create a backend, it returns 0.
+ \a interfaceName is the network/CAN interface name.
+ \a errorMessage contains an error description in case of failure.
+
+ If the factory cannot create a plugin, it returns \c nullptr.
*/
/*!
diff --git a/src/serialbus/qcanbusfactory.h b/src/serialbus/qcanbusfactory.h
index b143058..4d391c1 100644
--- a/src/serialbus/qcanbusfactory.h
+++ b/src/serialbus/qcanbusfactory.h
@@ -46,7 +46,8 @@ QT_BEGIN_NAMESPACE
class Q_SERIALBUS_EXPORT QCanBusFactory
{
public:
- virtual QCanBusDevice *createDevice(const QString &interfaceName) const = 0;
+ virtual QCanBusDevice *createDevice(const QString &interfaceName,
+ QString *errorMessage) const = 0;
protected:
virtual ~QCanBusFactory() {}
};
diff --git a/src/serialbus/qcanbusframe.cpp b/src/serialbus/qcanbusframe.cpp
index 105bf8b..db17e1d 100644
--- a/src/serialbus/qcanbusframe.cpp
+++ b/src/serialbus/qcanbusframe.cpp
@@ -43,7 +43,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QCanBusFrame
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief QCanBusFrame is a container class representing a single CAN frame.
@@ -54,7 +54,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn QCanBusFrame::QCanBusFrame(QCanBusFrame::FrameType type)
+ \fn QCanBusFrame::QCanBusFrame(QCanBusFrame::FrameType type = DataFrame)
Constructs a CAN frame of the specified \a type.
*/
@@ -70,7 +70,9 @@ QT_BEGIN_NAMESPACE
Returns \c false if the \l frameType() is \l InvalidFrame,
the \l hasExtendedFrameFormat() is not set although \l frameId() is longer than 11 bit or
- the payload is longer than the maximal permitted payload length of 64 byte.
+ the payload is longer than the maximal permitted payload length of 64 byte if \e {Flexible
+ Data-Rate} mode is enabled or 8 byte if it is disabled. If \l frameType() is \l RemoteRequestFrame
+ and the \e {Flexible Data-Rate} mode is enabled at the same time \c false is also returned.
Otherwise this function returns \c true.
*/
@@ -89,7 +91,8 @@ QT_BEGIN_NAMESPACE
\fn QCanBusFrame::setPayload(const QByteArray &data)
Sets \a data as the payload for the CAN frame. The maximum size of payload is 8 bytes, which can
- be extended up to 64 bytes by supporting \e {Flexible Data-Rate}. Flexible Data-Rate has to be
+ be extended up to 64 bytes by supporting \e {Flexible Data-Rate}. If \a data contains more than
+ 8 byte the \e {Flexible Data-Rate} flag is automatically set. Flexible Data-Rate has to be
enabled on the \l QCanBusDevice by setting the \l QCanBusDevice::CanFdKey.
Frames of type \l RemoteRequestFrame (RTR) do not have a payload. However they have to
@@ -103,12 +106,11 @@ QT_BEGIN_NAMESPACE
frame.setPayload(QByteArray(expectedResponseLength, 0));
\endcode
-
- \sa payload()
+ \sa payload(), hasFlexibleDataRateFormat()
*/
/*!
- \fn QCanBusFrame::setTimeStamp(const TimeStamp &ts)
+ \fn QCanBusFrame::setTimeStamp(TimeStamp ts)
Sets \a ts as the timestamp for the CAN frame. Usually, this function is not needed, because the
timestamp is created during the read operation and not needed during the write operation.
@@ -232,9 +234,28 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \fn bool QCanBusFrame::hasFlexibleDataRateFormat() const
+
+ Returns \c true if the CAN frame uses \e {Flexible Data-Rate} which allows up to 64 data bytes,
+ otherwise \c false, implying at most 8 byte of payload.
+
+ \sa setFlexibleDataRateFormat(), payload()
+*/
+
+/*!
+ \fn void QCanBusFrame::setFlexibleDataRateFormat(bool isFlexibleData)
+
+ Sets the \e {Flexible Data-Rate} flag to \a isFlexibleData. Those frames can be sent using
+ a higher speed on supporting controllers. Additionally the payload length limit is raised to
+ 64 byte.
+
+ \sa hasFlexibleDataRateFormat()
+*/
+
+/*!
\class QCanBusFrame::TimeStamp
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief The TimeStamp class provides timestamp information with microsecond precision.
*/
@@ -243,39 +264,93 @@ QT_BEGIN_NAMESPACE
\fn TimeStamp::TimeStamp(qint64 s, qint64 usec)
Constructs a TimeStamp in seconds, \a s, and microseconds, \a usec.
+
+ \note The TimeStamp is not normalized, i.e. microseconds greater 1000000 are not
+ converted to seconds.
+*/
+
+/*!
+ \fn static TimeStamp TimeStamp::fromMicroSeconds(qint64 usec)
+
+ Constructs a normalized TimeStamp from microseconds \a usec.
+
+ The created TimeStamp is normalized, i.e. microseconds greater 1000000 are converted
+ to seconds.
*/
/*!
\fn qint64 TimeStamp::seconds() const
Returns the seconds of the timestamp.
-
- \sa TimeStamp::setSeconds()
*/
/*!
\fn qint64 TimeStamp::microSeconds() const
Returns the microseconds of the timestamp.
-
- \sa TimeStamp::setMicroSeconds
*/
/*!
- \fn TimeStamp::setSeconds(qint64 s)
+ Returns the CAN frame as a formatted string.
- Sets the seconds in the timestamp type to \a s.
+ The output contains the CAN identfier in hexadecimal format, right
+ adjusted to 32 bit, followed by the data length in square brackets
+ and the payload in hexadecimal format.
- \sa TimeStamp::seconds()
-*/
+ Standard identifiers are filled with spaces while extended identifiers
+ are filled with zeros.
-/*!
- \fn TimeStamp::setMicroSeconds(qint64 usec)
-
- Sets the microseconds in the timestamp type to \a usec.
+ Typical outputs are:
- \sa TimeStamp::microSeconds
+ \code
+ (Error) - error frame
+ 7FF [1] 01 - data frame with standard identifier
+ 1FFFFFFF [8] 01 23 45 67 89 AB CD EF - data frame with extended identifier
+ 400 [10] 01 23 45 67 ... EF 01 23 - CAN FD frame
+ 123 [5] Remote Request - remote frame with standard identifier
+ 00000234 [0] Remote Request - remote frame with extended identifier
+ \endcode
*/
+QString QCanBusFrame::toString() const
+{
+ const FrameType type = frameType();
+
+ switch (type) {
+ case InvalidFrame:
+ return QStringLiteral("(Invalid)");
+ case ErrorFrame:
+ return QStringLiteral("(Error)");
+ case UnknownFrame:
+ return QStringLiteral("(Unknown)");
+ default:
+ break;
+ }
+
+ const char *idFormat = hasExtendedFrameFormat() ? "%08X" : " %03X";
+ const char *dlcFormat = hasFlexibleDataRateFormat() ? " [%02d]" : " [%d]";
+ QString result;
+ result.append(QString::asprintf(idFormat, static_cast<uint>(frameId())));
+ result.append(QString::asprintf(dlcFormat, payload().size()));
+
+ if (type == RemoteRequestFrame) {
+ result.append(QLatin1String(" Remote Request"));
+ } else {
+ const QByteArray data = payload().toHex().toUpper();
+ const int length = data.size();
+ if (length) {
+ const QLatin1String l1(data.data(), length);
+
+ result.append(QLatin1Char(' '));
+
+ for (int i = 0; i < length; i += 2) {
+ result.append(QLatin1Char(' '));
+ result.append(l1.mid(i, 2));
+ }
+ }
+ }
+
+ return result;
+}
#ifndef QT_NO_DATASTREAM
@@ -290,6 +365,7 @@ QDataStream &operator<<(QDataStream &out, const QCanBusFrame &frame)
out << static_cast<quint8>(frame.frameType());
out << static_cast<quint8>(frame.version);
out << frame.hasExtendedFrameFormat();
+ out << frame.hasFlexibleDataRateFormat();
out << frame.payload();
const QCanBusFrame::TimeStamp stamp = frame.timeStamp();
out << stamp.seconds();
@@ -308,11 +384,12 @@ QDataStream &operator>>(QDataStream &in, QCanBusFrame &frame)
quint8 frameType;
quint8 version;
bool extendedFrameFormat;
+ bool flexibleDataRate;
QByteArray payload;
qint64 seconds;
qint64 microSeconds;
- in >> frameId >> frameType >> version >> extendedFrameFormat
+ in >> frameId >> frameType >> version >> extendedFrameFormat >> flexibleDataRate
>> payload >> seconds >> microSeconds;
frame.setFrameId(frameId);
@@ -320,6 +397,7 @@ QDataStream &operator>>(QDataStream &in, QCanBusFrame &frame)
frame.setFrameType(static_cast<QCanBusFrame::FrameType>(frameType));
frame.setExtendedFrameFormat(extendedFrameFormat);
+ frame.setFlexibleDataRateFormat(flexibleDataRate);
frame.setPayload(payload);
frame.setTimeStamp(QCanBusFrame::TimeStamp(seconds, microSeconds));
diff --git a/src/serialbus/qcanbusframe.h b/src/serialbus/qcanbusframe.h
index 9b87f88..486d996 100644
--- a/src/serialbus/qcanbusframe.h
+++ b/src/serialbus/qcanbusframe.h
@@ -45,18 +45,20 @@ QT_BEGIN_NAMESPACE
class QDataStream;
-class QCanBusFrame
+class Q_SERIALBUS_EXPORT QCanBusFrame
{
public:
class TimeStamp {
public:
Q_DECL_CONSTEXPR TimeStamp(qint64 s = 0, qint64 usec = 0) Q_DECL_NOTHROW
: secs(s), usecs(usec) {}
+
+ Q_DECL_CONSTEXPR static TimeStamp fromMicroSeconds(qint64 usec) Q_DECL_NOTHROW
+ { return TimeStamp(usec / 1000000, usec % 1000000); }
+
Q_DECL_CONSTEXPR qint64 seconds() const Q_DECL_NOTHROW { return secs; }
Q_DECL_CONSTEXPR qint64 microSeconds() const Q_DECL_NOTHROW { return usecs; }
- Q_DECL_RELAXED_CONSTEXPR void setSeconds(qint64 s) Q_DECL_NOTHROW { secs = s; }
- Q_DECL_RELAXED_CONSTEXPR void setMicroSeconds(qint64 usec) Q_DECL_NOTHROW { usecs = usec; }
private:
qint64 secs;
qint64 usecs;
@@ -70,13 +72,13 @@ public:
InvalidFrame = 0x4
};
- explicit QCanBusFrame(QCanBusFrame::FrameType type) :
- canId(0x0),
+ explicit QCanBusFrame(FrameType type = DataFrame) Q_DECL_NOTHROW :
isExtendedFrame(0x0),
version(0x0),
- extra(0x0)
+ isFlexibleDataRate(0x0)
{
memset(reserved, 0, sizeof(reserved));
+ setFrameId(0x0);
setFrameType(type);
}
@@ -98,19 +100,18 @@ public:
Q_DECLARE_FLAGS(FrameErrors, FrameError)
Q_FLAGS(FrameErrors)
- explicit QCanBusFrame(quint32 identifier = 0, const QByteArray &data = QByteArray()) :
- canId(identifier & 0x1FFFFFFFU),
+ explicit QCanBusFrame(quint32 identifier, const QByteArray &data) :
format(DataFrame),
- isExtendedFrame((identifier & 0x1FFFF800U) ? 0x1 : 0x0),
+ isExtendedFrame(0x0),
version(0x0),
- extra(0x0),
+ isFlexibleDataRate(data.length() > 8 ? 0x1 : 0x0),
load(data)
{
- Q_UNUSED(extra);
memset(reserved, 0, sizeof(reserved));
+ setFrameId(identifier);
}
- bool isValid() const
+ bool isValid() const Q_DECL_NOTHROW
{
if (format == InvalidFrame)
return false;
@@ -119,14 +120,24 @@ public:
if (!isExtendedFrame && (canId & 0x1FFFF800U))
return false;
- // maximum permitted payload size in CAN FD
- if (load.length() > 64)
+ if (!isValidFrameId)
return false;
+ // maximum permitted payload size in CAN or CAN FD
+ if (isFlexibleDataRate) {
+ if (load.length() > 64)
+ return false;
+ if (format == RemoteRequestFrame)
+ return false;
+ } else {
+ if (load.length() > 8)
+ return false;
+ }
+
return true;
}
- FrameType frameType() const
+ FrameType frameType() const Q_DECL_NOTHROW
{
switch (format) {
case 0x1: return DataFrame;
@@ -139,7 +150,7 @@ public:
return UnknownFrame;
}
- void setFrameType(FrameType newFormat)
+ void setFrameType(FrameType newFormat) Q_DECL_NOTHROW
{
switch (newFormat) {
case DataFrame:
@@ -155,44 +166,63 @@ public:
}
}
- inline bool hasExtendedFrameFormat() const { return (isExtendedFrame & 0x1); }
- inline void setExtendedFrameFormat(bool isExtended)
+ bool hasExtendedFrameFormat() const Q_DECL_NOTHROW { return (isExtendedFrame & 0x1); }
+ void setExtendedFrameFormat(bool isExtended) Q_DECL_NOTHROW
{
isExtendedFrame = (isExtended & 0x1);
}
- inline quint32 frameId() const
+ quint32 frameId() const Q_DECL_NOTHROW
{
if (format == ErrorFrame)
return 0;
return (canId & 0x1FFFFFFFU);
}
- inline void setFrameId(quint32 newFrameId)
+ void setFrameId(quint32 newFrameId)
{
- canId = (newFrameId & 0x1FFFFFFFU);
- setExtendedFrameFormat(isExtendedFrame || (newFrameId & 0x1FFFF800U));
+ if (newFrameId < 0x20000000U) {
+ isValidFrameId = true;
+ canId = newFrameId;
+ setExtendedFrameFormat(isExtendedFrame || (newFrameId & 0x1FFFF800U));
+ } else {
+ isValidFrameId = false;
+ canId = 0;
+ }
}
- inline void setPayload(const QByteArray &data) { load = data; }
- inline void setTimeStamp(const TimeStamp &ts) { stamp = ts; }
+ void setPayload(const QByteArray &data)
+ {
+ load = data;
+ if (data.length() > 8)
+ isFlexibleDataRate = 0x1;
+ }
+ void setTimeStamp(TimeStamp ts) Q_DECL_NOTHROW { stamp = ts; }
QByteArray payload() const { return load; }
- TimeStamp timeStamp() const { return stamp; }
+ TimeStamp timeStamp() const Q_DECL_NOTHROW { return stamp; }
- QCanBusFrame::FrameErrors error() const
+ FrameErrors error() const Q_DECL_NOTHROW
{
if (format != ErrorFrame)
- return QCanBusFrame::FrameErrors(QCanBusFrame::NoError);
+ return NoError;
return FrameErrors(canId & 0x1FFFFFFFU);
}
- void setError(QCanBusFrame::FrameErrors e)
+ void setError(FrameErrors e)
{
if (format != ErrorFrame)
return;
canId = (e & AnyError);
}
+ QString toString() const;
+
+ bool hasFlexibleDataRateFormat() const Q_DECL_NOTHROW { return (isFlexibleDataRate & 0x1); }
+ void setFlexibleDataRateFormat(bool isFlexibleData) Q_DECL_NOTHROW
+ {
+ isFlexibleDataRate = (isFlexibleData & 0x1);
+ }
+
#ifndef QT_NO_DATASTREAM
friend Q_SERIALBUS_EXPORT QDataStream &operator<<(QDataStream &, const QCanBusFrame &);
friend Q_SERIALBUS_EXPORT QDataStream &operator>>(QDataStream &, QCanBusFrame &);
@@ -204,7 +234,8 @@ private:
quint8 isExtendedFrame:1;
quint8 version:5;
- quint8 extra: 2; // unused
+ quint8 isValidFrameId:1;
+ quint8 isFlexibleDataRate:1;
// reserved for future use
quint8 reserved[3];
diff --git a/src/serialbus/qmodbusclient.cpp b/src/serialbus/qmodbusclient.cpp
index a54f739..fcbad8b 100644
--- a/src/serialbus/qmodbusclient.cpp
+++ b/src/serialbus/qmodbusclient.cpp
@@ -50,7 +50,7 @@ Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS)
/*!
\class QModbusClient
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief The QModbusClient class is the interface to send Modbus requests.
diff --git a/src/serialbus/qmodbusdataunit.cpp b/src/serialbus/qmodbusdataunit.cpp
index de06bc2..0f8821f 100644
--- a/src/serialbus/qmodbusdataunit.cpp
+++ b/src/serialbus/qmodbusdataunit.cpp
@@ -41,7 +41,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QModbusDataUnit
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief QModbusDataUnit is a container class representing single bit and
\c 16 bit word entries in the Modbus register.
@@ -206,7 +206,7 @@ QT_BEGIN_NAMESPACE
/*!
\typedef QModbusDataUnitMap
\relates QModbusDataUnit
- \since 5.6
+ \since 5.8
Synonym for QMap<QModbusDataUnit::RegisterType, QModbusDataUnit>.
*/
diff --git a/src/serialbus/qmodbusdevice.cpp b/src/serialbus/qmodbusdevice.cpp
index 0eecac7..08718d6 100644
--- a/src/serialbus/qmodbusdevice.cpp
+++ b/src/serialbus/qmodbusdevice.cpp
@@ -45,7 +45,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QModbusDevice
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief The QModbusDevice class is the base class for Modbus classes, \l QModbusServer
and \l QModbusClient.
diff --git a/src/serialbus/qmodbusdeviceidentification.cpp b/src/serialbus/qmodbusdeviceidentification.cpp
index 5ae3f54..3aa54fb 100644
--- a/src/serialbus/qmodbusdeviceidentification.cpp
+++ b/src/serialbus/qmodbusdeviceidentification.cpp
@@ -42,7 +42,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QModbusDeviceIdentification
\inmodule QtSerialBus
- \since 5.7
+ \since 5.8
\brief The QModbusDeviceIdentification is a container class representing
the physical and functional description of a Modbus server.
diff --git a/src/serialbus/qmodbuspdu.cpp b/src/serialbus/qmodbuspdu.cpp
index 5b1a940..32a3b43 100644
--- a/src/serialbus/qmodbuspdu.cpp
+++ b/src/serialbus/qmodbuspdu.cpp
@@ -184,7 +184,7 @@ static QDataStream &pduFromStream(QDataStream &stream, QModbusPdu &pdu, Type typ
/*!
\class QModbusPdu
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief QModbusPdu is a abstract container class containing the function code and
payload that is stored inside a Modbus ADU.
@@ -464,7 +464,7 @@ QDataStream &operator<<(QDataStream &stream, const QModbusPdu &pdu)
/*!
\class QModbusRequest
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief QModbusRequest is a container class containing the function code and payload that is
stored inside a Modbus ADU.
@@ -618,7 +618,7 @@ QDataStream &operator>>(QDataStream &stream, QModbusRequest &pdu)
/*!
\class QModbusResponse
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief QModbusResponse is a container class containing the function code and payload that is
stored inside a Modbus ADU.
@@ -796,7 +796,7 @@ QDataStream &operator>>(QDataStream &stream, QModbusResponse &pdu)
/*!
\class QModbusExceptionResponse
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief QModbusExceptionResponse is a container class containing the function and error code
inside a Modbus ADU.
diff --git a/src/serialbus/qmodbusreply.cpp b/src/serialbus/qmodbusreply.cpp
index 2a8ee19..f88a3bd 100644
--- a/src/serialbus/qmodbusreply.cpp
+++ b/src/serialbus/qmodbusreply.cpp
@@ -58,7 +58,7 @@ public:
/*!
\class QModbusReply
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief The QModbusReply class contains the data for a request sent with
a \l QModbusClient derived class.
diff --git a/src/serialbus/qmodbusrtuserialslave.cpp b/src/serialbus/qmodbusrtuserialslave.cpp
index 644ad07..87698d3 100644
--- a/src/serialbus/qmodbusrtuserialslave.cpp
+++ b/src/serialbus/qmodbusrtuserialslave.cpp
@@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QModbusRtuSerialSlave
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief The QModbusRtuSerialSlave class represents a Modbus server
that uses a serial port for its communication with the Modbus client.
diff --git a/src/serialbus/qmodbusserver.cpp b/src/serialbus/qmodbusserver.cpp
index ffb673a..1a16347 100644
--- a/src/serialbus/qmodbusserver.cpp
+++ b/src/serialbus/qmodbusserver.cpp
@@ -53,7 +53,7 @@ Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS)
/*!
\class QModbusServer
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief The QModbusServer class is the interface to receive and process Modbus requests.
diff --git a/src/serialbus/qmodbustcpclient.cpp b/src/serialbus/qmodbustcpclient.cpp
index 94fa174..a34c241 100644
--- a/src/serialbus/qmodbustcpclient.cpp
+++ b/src/serialbus/qmodbustcpclient.cpp
@@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QModbusTcpClient
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief The QModbusTcpClient class is the interface class for Modbus TCP client device.
diff --git a/src/serialbus/qmodbustcpclient_p.h b/src/serialbus/qmodbustcpclient_p.h
index 3156feb..2d7a365 100644
--- a/src/serialbus/qmodbustcpclient_p.h
+++ b/src/serialbus/qmodbustcpclient_p.h
@@ -193,7 +193,9 @@ public:
});
if (element.timer) {
- q->connect(q, &QModbusClient::timeoutChanged, element.timer.data(), &QTimer::setInterval);
+ using TypeId = void (QTimer::*)(int);
+ q->connect(q, &QModbusClient::timeoutChanged,
+ element.timer.data(), static_cast<TypeId>(&QTimer::setInterval));
QObject::connect(element.timer.data(), &QTimer::timeout, [this, writeToSocket, tId]() {
if (!m_transactionStore.contains(tId))
return;
diff --git a/src/serialbus/qmodbustcpserver.cpp b/src/serialbus/qmodbustcpserver.cpp
index 0f9aff2..07a3363 100644
--- a/src/serialbus/qmodbustcpserver.cpp
+++ b/src/serialbus/qmodbustcpserver.cpp
@@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QModbusTcpServer
\inmodule QtSerialBus
- \since 5.6
+ \since 5.8
\brief The QModbusTcpServer class represents a Modbus server that uses a
TCP server for its communication with the Modbus client.
diff --git a/src/tools/canbusutil/canbusutil.cpp b/src/tools/canbusutil/canbusutil.cpp
index 8421caf..d614583 100644
--- a/src/tools/canbusutil/canbusutil.cpp
+++ b/src/tools/canbusutil/canbusutil.cpp
@@ -36,215 +36,142 @@
#include "canbusutil.h"
-static const qint8 MAXNORMALPAYLOADSIZE = 8;
-static const qint8 MAXEXTENDEDPAYLOADSIZE = 64;
-
CanBusUtil::CanBusUtil(QTextStream &output, QCoreApplication &app, QObject *parent)
: QObject(parent),
- canBus(QCanBus::instance()),
- output(output),
- app(app),
- readTask(new ReadTask(output, this))
+ m_canBus(QCanBus::instance()),
+ m_output(output),
+ m_app(app),
+ m_readTask(new ReadTask(output, this))
+{
+}
+
+void CanBusUtil::setShowTimeStamp(bool showTimeStamp)
{
+ m_readTask->setShowTimeStamp(showTimeStamp);
}
-bool CanBusUtil::start(int argc, char *argv[])
+bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, const QString &data)
{
- if (!canBus) {
- output << "Unable to create QCanBus" << endl;
+ if (!m_canBus) {
+ m_output << "Unable to create QCanBus" << endl;
return false;
}
- if (!parseArgs(argc, argv))
- return false;
+ m_pluginName = pluginName;
+ m_deviceName = deviceName;
+ m_data = data;
+ m_listening = data.isEmpty();
if (!connectCanDevice())
return false;
- if (listening) {
- connect(canDevice.data(), &QCanBusDevice::framesReceived, readTask, &ReadTask::checkMessages);
+ if (m_listening) {
+ connect(m_canDevice.data(), &QCanBusDevice::framesReceived, m_readTask, &ReadTask::checkMessages);
} else {
if (!sendData())
return false;
- QTimer::singleShot(0, &app, SLOT(quit()));
+ QTimer::singleShot(0, &m_app, SLOT(quit()));
}
return true;
}
-void CanBusUtil::printUsage()
-{
- output << "Usage: canbusutil [options] <plugin> <device> [data]" << endl
- << "-l start listening CAN data on device" << endl
- << "--list-plugins lists all available plugins" << endl
- << "<plugin> plugin name to use. See --list-plugins." << endl
- << "<device> device to use" << endl
- << "[data] Data to send if -l is not specified. Format: " << endl
- << " <id>#{payload} (CAN 2.0 data frames)," << endl
- << " <id>#Rxx (CAN 2.0 RTR frames with xx bytes data length)," << endl
- << " <id>##{payload} (CAN FD data frames)," << endl
- << " where {payload} has 0..8 (0..64 CAN FD) ASCII hex-value pairs," << endl
- << " e.g. 1#1a2b3c" << endl;
-}
-
void CanBusUtil::printPlugins()
{
- QList<QByteArray> plugins = canBus->plugins();
- output << "Plugins: " << endl;
+ const QStringList plugins = m_canBus->plugins();
for (int i = 0; i < plugins.size(); i++)
- output << plugins.at(i) << endl;
-}
-
-void CanBusUtil::printDataUsage()
-{
- output << "Invalid [data] field, use format: " << endl
- << " <id>#{payload} (CAN 2.0 data frames)," << endl
- << " <id>#Rxx (CAN 2.0 RTR frames with xx bytes data length)," << endl
- << " <id>##{payload} (CAN FD data frames)," << endl
- << "{payload} has 0..8 (0..64 CAN FD) ASCII hex-value pairs" << endl;
-}
-
-
-bool CanBusUtil::parseArgs(int argc, char *argv[])
-{
- QString arg1(argv[1]);
-
- if (arg1 == QStringLiteral("--list-plugins")) {
- printPlugins();
- return false;
- }
-
- if (argc != 4) {
- printUsage();
- return false;
- }
-
- listening = arg1 == QStringLiteral("-l");
- if (listening) {
- pluginName = QString(argv[2]);
- deviceName = QString(argv[3]);
- } else {
- pluginName = QString(arg1);
- deviceName = QString(argv[2]);
- data = QString(argv[3]);
- }
- return true;
+ m_output << plugins.at(i) << endl;
}
bool CanBusUtil::parseDataField(qint32 &id, QString &payload)
{
- int hashMarkPos = data.indexOf('#');
+ int hashMarkPos = m_data.indexOf('#');
if (hashMarkPos < 0) {
- output << "No hash mark found!" << endl;
- printDataUsage();
+ m_output << "Data field invalid: No hash mark found!" << endl;
return false;
}
- id = data.left(hashMarkPos).toInt(nullptr, 16);
- payload = data.right(data.length() - hashMarkPos - 1);
+ id = m_data.left(hashMarkPos).toInt(nullptr, 16);
+ payload = m_data.right(m_data.length() - hashMarkPos - 1);
return true;
}
-bool CanBusUtil::parsePayloadField(QString payload, bool &rtrFrame,
- bool &fdFrame, QByteArray &bytes)
+bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame)
{
- fdFrame = false;
- rtrFrame = false;
+ if (!payload.isEmpty() && payload.at(0).toUpper() == 'R') {
+ bool validPayloadLength = false;
- if (payload.size() == 0)
- return true;
+ frame->setFrameType(QCanBusFrame::RemoteRequestFrame);
- if (payload[0].toUpper() == 'R') {
- rtrFrame = true;
- bool validPayloadLength = false;
- if (payload.size() > 1) {
+ 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 <= 64) {
- bytes = QByteArray(rtrFrameLength, 0);
- if (rtrFrameLength > 8)
- fdFrame = true;
+ if (validPayloadLength && rtrFrameLength >= 0 && rtrFrameLength <= 8) {
+ frame->setPayload(QByteArray(rtrFrameLength, 0));
} else if (validPayloadLength) {
- output << "The length must be larger than 0 and not exceed 64." << endl;
+ m_output << "The length must be between 0 and 8 (including)." << endl;
validPayloadLength = false;
}
}
if (!validPayloadLength) {
- output << "RTR data frame length not specified/valid." << endl;
- printDataUsage();
+ m_output << "Data field invalid: RTR data frame length not specified/valid." << endl;
}
return validPayloadLength;
- } else if (payload[0] == '#') {
- fdFrame = true;
+ } else if (!payload.isEmpty() && payload.at(0) == '#') {
+ frame->setFlexibleDataRateFormat(true);
payload = payload.mid(1);
}
- if (payload.size() % 2 == 0) {
- for (int i=0; i < payload.size(); i+=2) {
- bool numberConverOk = true;
- quint8 high = QString(payload[i]).toInt(&numberConverOk, 16);
- if (!numberConverOk) {
- output << "Could not convert '" << QString(payload[i]) << "' to a number"<< endl;
- printDataUsage();
- return false;
- }
-
- quint8 low = QString(payload[i+1]).toInt(&numberConverOk, 16);
- if (!numberConverOk) {
- output << "Could not convert '" << QString(payload[i+1]) << "' to a number" << endl;
- printDataUsage();
- return false;
- }
+ if (payload.size() % 2 != 0) {
+ m_output << "Data field invalid: Size is not multiple of two." << endl;
+ return false;
+ }
- quint8 byte = (high << 4) | (low);
- bytes.append(byte);
- }
- qint8 size = bytes.size();
- qint8 maxsize = fdFrame ? MAXEXTENDEDPAYLOADSIZE : MAXNORMALPAYLOADSIZE;
- if (size > maxsize) {
- output << "Warning! payload size too great. Size: " << size << ", max allowed size in frame: " << maxsize << endl
- << "Clipping payload to fit frame..." << endl;
- size = maxsize;
- bytes = bytes.left(size);
- }
- } else {
- output << "Payload size not multiple of two!" << endl;
- printDataUsage();
+ const QRegularExpression re(QStringLiteral("^[0-9A-Fa-f]*$"));
+ if (!re.match(payload).hasMatch()) {
+ m_output << "Data field invalid: Only hex numbers allowed." << endl;
return false;
}
+
+ QByteArray bytes = QByteArray::fromHex(payload.toLatin1());
+
+ 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);
+ }
+
+ frame->setPayload(bytes);
+
return true;
}
bool CanBusUtil::connectCanDevice()
{
- bool foundPlugin = false;
- QList<QByteArray> plugins = canBus->plugins();
- for (int i = 0; i < plugins.size(); i++)
- {
- if (plugins.at(i) == pluginName) {
- canDevice.reset(canBus->createDevice(plugins.at(i), deviceName));
- if (!canDevice) {
- output << "Unable to create QCanBusDevice with device name: " << deviceName << endl;
- return false;
- }
- connect(canDevice.data(), &QCanBusDevice::errorOccurred, readTask, &ReadTask::receiveError);
- if (!canDevice->connectDevice()) {
- output << "Unable to connect QCanBusDevice with device name: " << deviceName << endl;
- return false;
- }
- foundPlugin = true;
- }
+ if (!m_canBus->plugins().contains(m_pluginName)) {
+ m_output << "Could not find suitable plugin." << endl;
+ m_output << "Available plugins:" << endl;
+ printPlugins();
+ return false;
}
- if (!foundPlugin) {
- output << "Could not find suitable plugin." << endl;
- printPlugins();
+ 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;
+ return false;
+ }
+ connect(m_canDevice.data(), &QCanBusDevice::errorOccurred, m_readTask, &ReadTask::receiveError);
+ if (!m_canDevice->connectDevice()) {
+ m_output << "Unable to connect QCanBusDevice with device name: " << m_deviceName << endl;
return false;
}
+
return true;
}
@@ -252,30 +179,23 @@ bool CanBusUtil::sendData()
{
qint32 id;
QString payload;
- bool rtrFrame;
- bool fdFrame;
- QByteArray bytes;
QCanBusFrame frame;
if (parseDataField(id, payload) == false)
return false;
- if (parsePayloadField(payload, rtrFrame, fdFrame, bytes) == false)
+ if (setFrameFromPayload(payload, &frame) == false)
return false;
if (id < 0 || id > 0x1FFFFFFF) { // 29 bits
id = 0x1FFFFFFF;
- output << "Warning! Id does not fit into Extended Frame Format, setting id to: " << id << endl;
+ m_output << "Warning! Id does not fit into Extended Frame Format, setting id to: " << id << endl;
}
- if (rtrFrame)
- frame.setFrameType(QCanBusFrame::RemoteRequestFrame);
-
- frame.setPayload(bytes);
frame.setFrameId(id);
- if (fdFrame)
- canDevice->setConfigurationParameter(QCanBusDevice::CanFdKey, true);
+ if (frame.hasFlexibleDataRateFormat())
+ m_canDevice->setConfigurationParameter(QCanBusDevice::CanFdKey, true);
- return canDevice->writeFrame(frame);
+ return m_canDevice->writeFrame(frame);
}
diff --git a/src/tools/canbusutil/canbusutil.h b/src/tools/canbusutil/canbusutil.h
index 3ae20ec..e17fe7b 100644
--- a/src/tools/canbusutil/canbusutil.h
+++ b/src/tools/canbusutil/canbusutil.h
@@ -44,35 +44,39 @@
#include "readtask.h"
+QT_BEGIN_NAMESPACE
+
+class QCanBusFrame;
+
+QT_END_NAMESPACE
+
class CanBusUtil : public QObject
{
Q_OBJECT
public:
explicit CanBusUtil(QTextStream &output, QCoreApplication &app, QObject *parent = nullptr);
- bool start(int argc, char *argv[]);
+ void setShowTimeStamp(bool showTimeStamp);
+ bool start(const QString &pluginName, const QString &deviceName, const QString &data = QString());
+ void printPlugins();
private:
- void printUsage();
- void printPlugins();
- void printDataUsage();
- bool parseArgs(int argc, char *argv[]);
bool parseDataField(qint32 &id, QString &payload);
- bool parsePayloadField(QString payload, bool &rtrFrame, bool &fdFrame, QByteArray &bytes);
+ bool setFrameFromPayload(QString payload, QCanBusFrame *frame);
bool connectCanDevice();
bool startListeningOnCanDevice();
bool sendData();
private:
- QCanBus *canBus;
- QTextStream &output;
- QCoreApplication &app;
- bool listening;
- QString pluginName;
- QString deviceName;
- QString data;
- QScopedPointer<QCanBusDevice> canDevice;
- ReadTask *readTask;
+ QCanBus *m_canBus;
+ QTextStream &m_output;
+ QCoreApplication &m_app;
+ bool m_listening;
+ QString m_pluginName;
+ QString m_deviceName;
+ QString m_data;
+ QScopedPointer<QCanBusDevice> m_canDevice;
+ ReadTask *m_readTask;
};
#endif // CANBUSUTIL_H
diff --git a/src/tools/canbusutil/main.cpp b/src/tools/canbusutil/main.cpp
index ee7d30e..c760c31 100644
--- a/src/tools/canbusutil/main.cpp
+++ b/src/tools/canbusutil/main.cpp
@@ -34,6 +34,7 @@
**
****************************************************************************/
+#include <QCommandLineParser>
#include <QCoreApplication>
#include <QTextStream>
#include <QScopedPointer>
@@ -43,19 +44,76 @@
#include "canbusutil.h"
#include "sigtermhandler.h"
+#define PROGRAMNAME "canbusutil"
+
int main(int argc, char *argv[])
{
- QCoreApplication a(argc, argv);
+ QCoreApplication app(argc, argv);
+ QCoreApplication::setApplicationName(QStringLiteral(PROGRAMNAME));
+ QCoreApplication::setApplicationVersion(QStringLiteral(QT_VERSION_STR));
QScopedPointer<SigTermHandler> s(SigTermHandler::instance());
if (signal(SIGINT, SigTermHandler::handle) == SIG_ERR)
return -1;
- QObject::connect(s.data(), &SigTermHandler::sigTermSignal, &a, &QCoreApplication::quit);
+ QObject::connect(s.data(), &SigTermHandler::sigTermSignal, &app, &QCoreApplication::quit);
QTextStream output(stdout);
- CanBusUtil util(output, a);
- if (!util.start(argc, argv))
+ CanBusUtil util(output, app);
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription(CanBusUtil::tr(
+ "Sends arbitrary CAN bus frames.\n"
+ "If the -l option is set, all received CAN bus packages are dumped."));
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ parser.addPositionalArgument(QStringLiteral("plugin"),
+ CanBusUtil::tr("Plugin name to use. See --list-plugins."));
+
+ parser.addPositionalArgument(QStringLiteral("device"),
+ CanBusUtil::tr("Device to use."));
+
+ parser.addPositionalArgument(QStringLiteral("data"),
+ CanBusUtil::tr(
+ "Data to send if -l is not specified. Format:\n"
+ "\t\t<id>#{payload} (CAN 2.0 data frames),\n"
+ "\t\t<id>#Rxx (CAN 2.0 RTR frames with xx bytes data length),\n"
+ "\t\t<id>##{payload} (CAN FD data frames),\n"
+ "where {payload} has 0..8 (0..64 CAN FD) ASCII hex-value pairs, "
+ "e.g. 1#1a2b3c\n"), QStringLiteral("[data]"));
+
+ const QCommandLineOption listeningOption({"l", "listen"},
+ CanBusUtil::tr("Start listening CAN data on device."));
+ parser.addOption(listeningOption);
+
+ const QCommandLineOption listOption({"L", "list-plugins"},
+ CanBusUtil::tr("List all available plugins."));
+ parser.addOption(listOption);
+
+ const QCommandLineOption showTimeStampOption({"t", "timestamp"},
+ CanBusUtil::tr("Show timestamp for each received message."));
+ parser.addOption(showTimeStampOption);
+
+ parser.process(app);
+
+ if (parser.isSet(listOption)) {
+ util.printPlugins();
+ return 0;
+ }
+
+ QString data;
+ const QStringList args = parser.positionalArguments();
+ if (!parser.isSet(listeningOption) && args.size() == 3) {
+ data = args[2];
+ } else if (!parser.isSet(listeningOption) || args.size() != 2) {
+ fprintf(stderr, "Invalid number of arguments (%d given).\n\n%s",
+ args.size(), qPrintable(parser.helpText()));
+ return 1;
+ }
+
+ util.setShowTimeStamp(parser.isSet(showTimeStampOption));
+ if (!util.start(args[0], args[1], data))
return -1;
- return a.exec();
+ return app.exec();
}
diff --git a/src/tools/canbusutil/readtask.cpp b/src/tools/canbusutil/readtask.cpp
index a83586e..90858c2 100644
--- a/src/tools/canbusutil/readtask.cpp
+++ b/src/tools/canbusutil/readtask.cpp
@@ -36,12 +36,15 @@
#include "readtask.h"
-using namespace std;
-
ReadTask::ReadTask(QTextStream &output, QObject *parent)
: QObject(parent),
output(output) { }
+void ReadTask::setShowTimeStamp(bool showTimeStamp)
+{
+ m_showTimeStamp = showTimeStamp;
+}
+
void ReadTask::checkMessages() {
auto canDevice = qobject_cast<QCanBusDevice *>(QObject::sender());
if (canDevice == nullptr) {
@@ -49,36 +52,22 @@ void ReadTask::checkMessages() {
return;
}
- const QCanBusFrame frame = canDevice->readFrame();
+ while (canDevice->framesAvailable()) {
+ const QCanBusFrame frame = canDevice->readFrame();
- const qint32 id = frame.frameId();
- const qint8 dataLength = frame.payload().size();
+ QString view;
- QString view;
- if (frame.frameType() == QCanBusFrame::ErrorFrame) {
- view = canDevice->interpretErrorFrame(frame);
- } else {
- const char *format =
- frame.hasExtendedFrameFormat() ? "Id: %08X" : "Id: %03X";
- view += QString::asprintf(format, static_cast<uint>(id));
- view += QLatin1String(" bytes: ");
- view += QString::number(dataLength, 10);
- if (frame.frameType() != QCanBusFrame::RemoteRequestFrame) {
- view += QLatin1String(" data:");
- QByteArray array = frame.payload();
- for (int i=0; i < array.size(); i++) {
- view += QLatin1String(" 0x");
- quint8 number = array[i];
- view += QString::number(number, 16);
- }
+ if (m_showTimeStamp) {
+ view = QString::fromLatin1("%1.%2 ")
+ .arg(frame.timeStamp().seconds(), 10, 10, QLatin1Char(' '))
+ .arg(frame.timeStamp().microSeconds() / 100, 4, 10, QLatin1Char('0'));
}
- }
- if (frame.frameType() == QCanBusFrame::RemoteRequestFrame) {
- output << "RTR: " << view << endl;
- } else if (frame.frameType() == QCanBusFrame::ErrorFrame) {
- output << "ERR: " << view << endl;
- } else {
+ if (frame.frameType() == QCanBusFrame::ErrorFrame)
+ view += canDevice->interpretErrorFrame(frame);
+ else
+ view += frame.toString();
+
output << view << endl;
}
}
diff --git a/src/tools/canbusutil/readtask.h b/src/tools/canbusutil/readtask.h
index a59f218..7f7613b 100644
--- a/src/tools/canbusutil/readtask.h
+++ b/src/tools/canbusutil/readtask.h
@@ -46,6 +46,7 @@ class ReadTask : public QObject
Q_OBJECT
public:
explicit ReadTask(QTextStream &output, QObject *parent = nullptr);
+ void setShowTimeStamp(bool showStamp);
signals:
void sigTermSignal();
@@ -56,6 +57,7 @@ public slots:
private:
QTextStream &output;
+ bool m_showTimeStamp = false;
};
#endif // READTASK_H
diff --git a/src/tools/tools.pro b/src/tools/tools.pro
index 56051d4..1e278da 100644
--- a/src/tools/tools.pro
+++ b/src/tools/tools.pro
@@ -1,2 +1,3 @@
TEMPLATE = subdirs
-SUBDIRS += canbusutil
+
+!android|android_app: SUBDIRS += canbusutil
diff --git a/tests/auto/plugins/genericcanbus/dummybackend.cpp b/tests/auto/plugins/genericcanbus/dummybackend.cpp
index 71d1c0f..acfa520 100644
--- a/tests/auto/plugins/genericcanbus/dummybackend.cpp
+++ b/tests/auto/plugins/genericcanbus/dummybackend.cpp
@@ -36,20 +36,18 @@
#include "dummybackend.h"
+#include <QtCore/qdatetime.h>
#include <QtCore/qdebug.h>
#include <QtCore/qtimer.h>
-#include <QtSerialBus/qcanbusdevice.h>
-
QT_BEGIN_NAMESPACE
DummyBackend::DummyBackend() :
- sendTimer(new QTimer(this)),
- byteArray("abc")
+ sendTimer(new QTimer(this))
{
sendTimer->setInterval(1000);
sendTimer->setSingleShot(false);
- connect(sendTimer, SIGNAL(timeout()), this, SLOT(sendMessage()));
+ connect(sendTimer, &QTimer::timeout, this, &DummyBackend::sendMessage);
sendTimer->start();
}
@@ -66,16 +64,18 @@ void DummyBackend::close()
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.payload();
+ qDebug() << "DummyBackend::writeFrame: " << data.toString();
return true;
}
diff --git a/tests/auto/plugins/genericcanbus/dummybackend.h b/tests/auto/plugins/genericcanbus/dummybackend.h
index 1c708b3..3142181 100644
--- a/tests/auto/plugins/genericcanbus/dummybackend.h
+++ b/tests/auto/plugins/genericcanbus/dummybackend.h
@@ -39,8 +39,6 @@
#include <QtSerialBus/qcanbusdevice.h>
-#include <QtCore/qbytearray.h>
-
QT_BEGIN_NAMESPACE
class QTimer;
@@ -63,7 +61,6 @@ public Q_SLOTS:
private:
QTimer *sendTimer;
- QByteArray byteArray;
};
QT_END_NAMESPACE
diff --git a/tests/auto/plugins/genericcanbus/main.cpp b/tests/auto/plugins/genericcanbus/main.cpp
index 3fc6225..eb07dfa 100644
--- a/tests/auto/plugins/genericcanbus/main.cpp
+++ b/tests/auto/plugins/genericcanbus/main.cpp
@@ -50,9 +50,14 @@ class GenericBusPlugin : public QObject, public QCanBusFactory
Q_INTERFACES(QCanBusFactory)
public:
- QCanBusDevice *createDevice(const QString &interfaceName) const
+ QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
{
- Q_UNUSED(interfaceName)
+ if (interfaceName == QStringLiteral("invalid")) {
+ if (errorMessage)
+ *errorMessage = tr("No such interface: '%1'").arg(interfaceName);
+
+ return nullptr;
+ }
auto device = new DummyBackend();
return device;
}
diff --git a/tests/auto/qcanbus/tst_qcanbus.cpp b/tests/auto/qcanbus/tst_qcanbus.cpp
index b549f09..63f98cc 100644
--- a/tests/auto/qcanbus/tst_qcanbus.cpp
+++ b/tests/auto/qcanbus/tst_qcanbus.cpp
@@ -77,7 +77,7 @@ void tst_QCanBus::initTestCase()
void tst_QCanBus::plugins()
{
- QList<QByteArray> pluginList = bus->plugins();
+ const QStringList pluginList = bus->plugins();
QVERIFY(!pluginList.isEmpty());
QVERIFY(pluginList.contains("generic"));
@@ -85,12 +85,19 @@ void tst_QCanBus::plugins()
void tst_QCanBus::createDevice()
{
+ QString error, error2;
QCanBusDevice *dummy = bus->createDevice("generic", "unused");
QCanBusDevice *dummy2 = bus->createDevice("generic", "unused");
- QCanBusDevice *faulty = bus->createDevice("faulty", "faulty");
+ QCanBusDevice *faulty = bus->createDevice("generic", "invalid", &error);
+ QCanBusDevice *faulty2 = bus->createDevice("faulty", "faulty", &error2);
QVERIFY(dummy);
QVERIFY(dummy2);
+
QVERIFY(!faulty);
+ QCOMPARE(error, tr("No such interface: 'invalid'"));
+
+ QVERIFY(!faulty2);
+ QCOMPARE(error2, tr("No such plugin: 'faulty'"));
delete dummy;
delete dummy2;
diff --git a/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp b/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp
index c8ffb40..bf7ceed 100644
--- a/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp
+++ b/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp
@@ -41,25 +41,30 @@
#include <QSignalSpy>
#include <QScopedPointer>
+#include <memory>
+
class tst_Backend : public QCanBusDevice
{
Q_OBJECT
public:
tst_Backend() :
- firstOpen(true)
+ firstOpen(true), writeBufferUsed(true)
{
referenceFrame.setFrameId(5);
referenceFrame.setPayload(QByteArray("FOOBAR"));
- QCanBusFrame::TimeStamp stamp;
- stamp.setSeconds(22);
- stamp.setMicroSeconds(23);
- referenceFrame.setTimeStamp(stamp);
+ referenceFrame.setTimeStamp({ 22, 23 });
referenceFrame.setExtendedFrameFormat(1);
+ }
+
+ bool triggerNewFrame()
+ {
+ if (state() != QCanBusDevice::ConnectedState)
+ return false;
+
+ // line below triggers framesReceived() signal
+ enqueueReceivedFrames(QVector<QCanBusFrame>() << referenceFrame);
- // this test assumes that each readFrame() call returns referenceFrame
- // fill the buffer with frames
- for (int i = 0; i < 5; i++)
- enqueueReceivedFrames(QVector<QCanBusFrame>() << referenceFrame);
+ return true;
}
bool open()
@@ -77,12 +82,17 @@ public:
setState(QCanBusDevice::UnconnectedState);
}
- bool writeFrame(const QCanBusFrame &/*data*/)
+ bool writeFrame(const QCanBusFrame &data)
{
if (state() != QCanBusDevice::ConnectedState)
return false;
- emit written();
+ if (writeBufferUsed) {
+ enqueueOutgoingFrame(data);
+ QTimer::singleShot(2000, this, [this](){ triggerDelayedWrites(); });
+ } else {
+ emit framesWritten(1);
+ }
return true;
}
@@ -96,12 +106,30 @@ public:
return QString();
}
-signals:
- void written();
+ bool isWriteBuffered() const { return writeBufferUsed; }
+ void setWriteBuffered(bool isBuffered)
+ {
+ // permits switching between buffered and unbuffered write mode
+ writeBufferUsed = isBuffered;
+ }
+
+public slots:
+ void triggerDelayedWrites()
+ {
+ if (framesToWrite() == 0)
+ return;
+
+ dequeueOutgoingFrame();
+ emit framesWritten(1);
+
+ if (framesToWrite() > 0)
+ QTimer::singleShot(2000, this, [this](){ triggerDelayedWrites(); });
+ }
private:
QCanBusFrame referenceFrame;
bool firstOpen;
+ bool writeBufferUsed;
};
class tst_QCanBusDevice : public QObject
@@ -118,9 +146,12 @@ private slots:
void error();
void cleanupTestCase();
void tst_filtering();
+ void tst_bufferingAttribute();
+ void tst_waitForFramesReceived();
+ void tst_waitForFramesWritten();
private:
- QScopedPointer<QCanBusDevice> device;
+ QScopedPointer<tst_Backend> device;
};
tst_QCanBusDevice::tst_QCanBusDevice()
@@ -181,7 +212,11 @@ void tst_QCanBusDevice::conf()
void tst_QCanBusDevice::write()
{
- QSignalSpy spy(device.data(), SIGNAL(written()));
+ // we assume unbuffered writing in this function
+ device->setWriteBuffered(false);
+ QCOMPARE(device->isWriteBuffered(), false);
+
+ QSignalSpy spy(device.data(), SIGNAL(framesWritten(qint64)));
QSignalSpy stateSpy(device.data(),
SIGNAL(stateChanged(QCanBusDevice::CanBusDeviceState)));
@@ -236,6 +271,7 @@ void tst_QCanBusDevice::read()
QCOMPARE(stateSpy[1].at(0).value<QCanBusDevice::CanBusDeviceState>(),
QCanBusDevice::ConnectedState);
+ device->triggerNewFrame();
const QCanBusFrame frame2 = device->readFrame();
QVERIFY(!frame1.frameId());
QVERIFY(!frame1.isValid());
@@ -341,6 +377,162 @@ void tst_QCanBusDevice::tst_filtering()
QVERIFY(!(newFilter.at(1).format & QCanBusDevice::Filter::MatchExtendedFormat));
}
+void tst_QCanBusDevice::tst_bufferingAttribute()
+{
+ std::unique_ptr<tst_Backend> canDevice(new tst_Backend);
+ QVERIFY((bool)canDevice);
+ // by default buffered set to true
+ QCOMPARE(canDevice->isWriteBuffered(), true);
+
+ canDevice->setWriteBuffered(false);
+ QCOMPARE(canDevice->isWriteBuffered(), false);
+ canDevice->setWriteBuffered(true);
+ QCOMPARE(canDevice->isWriteBuffered(), true);
+}
+
+void tst_QCanBusDevice::tst_waitForFramesReceived()
+{
+ if (device->state() != QCanBusDevice::ConnectedState) {
+ QVERIFY(device->connectDevice());
+ QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState,
+ 5000);
+ }
+
+ QVERIFY(!device->framesAvailable());
+ QVERIFY(device->triggerNewFrame());
+ QVERIFY(device->framesAvailable());
+
+ // frame is available, function blocks and times out
+ bool result = device->waitForFramesReceived(2000);
+ QVERIFY(!result);
+
+ QCanBusFrame frame = device->readFrame();
+ QVERIFY(frame.isValid());
+ QCOMPARE(frame.payload(), QByteArray("FOOBAR"));
+ QVERIFY(!device->framesAvailable());
+
+ QElapsedTimer elapsed;
+ elapsed.start();
+ // no pending frame (should trigger active wait & timeout)
+ result = device->waitForFramesReceived(5000);
+ QVERIFY(elapsed.hasExpired(4000)); // should have caused time elapse
+ QVERIFY(!result);
+
+ QTimer::singleShot(2000, [&]() { device->triggerNewFrame(); });
+ elapsed.restart();
+ // frame will be inserted after 2s
+ result = device->waitForFramesReceived(8000);
+ QVERIFY(!elapsed.hasExpired(8000));
+ QVERIFY(result);
+
+ frame = device->readFrame();
+ QVERIFY(frame.isValid());
+ QCOMPARE(frame.payload(), QByteArray("FOOBAR"));
+ QVERIFY(!device->framesAvailable());
+
+ QTimer::singleShot(2000, [&]() {
+ device->emulateError(QString("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->error(), QCanBusDevice::ReadError);
+
+ // test recursive calling of waitForFramesReceived() behavior
+ int handleCounter = 0;
+ QTimer::singleShot(1000, [&]() {
+ device->triggerNewFrame();
+ device->triggerNewFrame();
+ });
+ QTimer::singleShot(2000, [&]() { device->triggerNewFrame(); });
+ QObject::connect(device.data(), &QCanBusDevice::framesReceived, [this, &handleCounter]() {
+ handleCounter++;
+ //this should trigger a recursion which we want to catch
+ device->waitForFramesReceived(5000);
+ });
+ result = device->waitForFramesReceived(8000);
+ QVERIFY(result);
+ QTRY_COMPARE_WITH_TIMEOUT(handleCounter, 3, 5000);
+}
+
+void tst_QCanBusDevice::tst_waitForFramesWritten()
+{
+ if (device->state() != QCanBusDevice::ConnectedState) {
+ QVERIFY(device->connectDevice());
+ QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState,
+ 5000);
+ }
+
+ device->setWriteBuffered(false);
+ bool result = device->waitForFramesWritten(1000);
+ QVERIFY(!result); //no buffer, wait not possible
+
+ device->setWriteBuffered(true);
+
+ QVERIFY(device->framesToWrite() == 0);
+ result = device->waitForFramesWritten(1000);
+ 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->writeFrame(frame);
+ QElapsedTimer elapsed;
+ elapsed.start();
+
+ // error will be triggered
+ result = device->waitForFramesWritten(8000);
+ QVERIFY(!elapsed.hasExpired(8000));
+ QVERIFY(!result);
+ QCOMPARE(device->errorString(), QString("TriggerWriteError"));
+ QCOMPARE(device->error(), QCanBusDevice::ReadError);
+
+ // flush remaining frames out to reset the test
+ QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000);
+
+ // test timeout
+ device->writeFrame(frame);
+ result = device->waitForFramesWritten(500);
+ QVERIFY(elapsed.hasExpired(500));
+ QVERIFY(!result);
+
+ // flush remaining frames out to reset the test
+ QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000);
+
+ device->writeFrame(frame);
+ device->writeFrame(frame);
+ elapsed.restart();
+ result = device->waitForFramesWritten(8000);
+ QVERIFY(!elapsed.hasExpired(8000));
+ QVERIFY(result);
+
+ // flush remaining frames out to reset the test
+ QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000);
+
+ // test recursive calling of waitForFramesWritten() behavior
+ int handleCounter = 0;
+ device->writeFrame(frame);
+ QTimer::singleShot(1000, [&]() { device->writeFrame(frame); });
+ 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
+ device->waitForFramesWritten(5000);
+ });
+ result = device->waitForFramesWritten(8000);
+ QVERIFY(result);
+ QTRY_COMPARE_WITH_TIMEOUT(handleCounter, 3, 5000);
+
+ device->setWriteBuffered(false);
+}
+
QTEST_MAIN(tst_QCanBusDevice)
#include "tst_qcanbusdevice.moc"
diff --git a/tests/auto/qcanbusframe/tst_qcanbusframe.cpp b/tests/auto/qcanbusframe/tst_qcanbusframe.cpp
index eb427fb..78f35da 100644
--- a/tests/auto/qcanbusframe/tst_qcanbusframe.cpp
+++ b/tests/auto/qcanbusframe/tst_qcanbusframe.cpp
@@ -52,6 +52,9 @@ private slots:
void tst_isValid_data();
void tst_isValid();
+ void tst_toString_data();
+ void tst_toString();
+
void streaming_data();
void streaming();
@@ -67,31 +70,39 @@ void tst_QCanBusFrame::constructors()
QCanBusFrame frame1;
QCanBusFrame frame2(123, "tst");
QCanBusFrame frame3(123456, "tst");
+ QCanBusFrame frame4(1234, "tst tst tst");
QCanBusFrame::TimeStamp timeStamp1;
QCanBusFrame::TimeStamp timeStamp2(5, 5);
QVERIFY(frame1.payload().isEmpty());
QVERIFY(!frame1.frameId());
+ QVERIFY(!frame1.hasFlexibleDataRateFormat());
+ QVERIFY(!frame1.hasExtendedFrameFormat());
+ QCOMPARE(frame1.frameType(), QCanBusFrame::DataFrame);
QVERIFY(!frame2.payload().isEmpty());
QVERIFY(frame2.frameId());
+ QVERIFY(!frame2.hasFlexibleDataRateFormat());
+ QVERIFY(!frame2.hasExtendedFrameFormat());
+ QCOMPARE(frame2.frameType(), QCanBusFrame::DataFrame);
QVERIFY(!frame3.payload().isEmpty());
QVERIFY(frame3.frameId());
+ QVERIFY(!frame3.hasFlexibleDataRateFormat());
+ QVERIFY(frame3.hasExtendedFrameFormat());
+ QCOMPARE(frame3.frameType(), QCanBusFrame::DataFrame);
+
+ QVERIFY(!frame4.payload().isEmpty());
+ QVERIFY(frame4.frameId());
+ QVERIFY(frame4.hasFlexibleDataRateFormat());
+ QVERIFY(!frame4.hasExtendedFrameFormat());
+ QCOMPARE(frame4.frameType(), QCanBusFrame::DataFrame);
QVERIFY(!timeStamp1.seconds());
QVERIFY(!timeStamp1.microSeconds());
QVERIFY(timeStamp2.seconds());
QVERIFY(timeStamp2.microSeconds());
-
- QVERIFY(frame1.hasExtendedFrameFormat() == false);
- QVERIFY(frame2.hasExtendedFrameFormat() == false);
- QVERIFY(frame3.hasExtendedFrameFormat() == true);
-
- QCOMPARE(frame1.frameType(), QCanBusFrame::DataFrame);
- QCOMPARE(frame2.frameType(), QCanBusFrame::DataFrame);
- QCOMPARE(frame3.frameType(), QCanBusFrame::DataFrame);
}
void tst_QCanBusFrame::id()
@@ -101,22 +112,32 @@ void tst_QCanBusFrame::id()
frame.setExtendedFrameFormat(false);
frame.setFrameId(2047u);
QCOMPARE(frame.frameId(), 2047u);
- QVERIFY(frame.hasExtendedFrameFormat() == false);
+ QVERIFY(frame.isValid());
+ QVERIFY(!frame.hasExtendedFrameFormat());
// id > 2^11 -> extended format
frame.setExtendedFrameFormat(false);
frame.setFrameId(2048u);
QCOMPARE(frame.frameId(), 2048u);
- QVERIFY(frame.hasExtendedFrameFormat() == true);
+ QVERIFY(frame.isValid());
+ QVERIFY(frame.hasExtendedFrameFormat());
// id < 2^11 -> no extended format
frame.setExtendedFrameFormat(false);
frame.setFrameId(512u);
QCOMPARE(frame.frameId(), 512u);
- QVERIFY(frame.hasExtendedFrameFormat() == false);
+ QVERIFY(frame.isValid());
+ QVERIFY(!frame.hasExtendedFrameFormat());
// id < 2^11 -> keep extended format
frame.setExtendedFrameFormat(true);
frame.setFrameId(512u);
QCOMPARE(frame.frameId(), 512u);
- QVERIFY(frame.hasExtendedFrameFormat() == true);
+ QVERIFY(frame.isValid());
+ QVERIFY(frame.hasExtendedFrameFormat());
+ // id >= 2^29 -> invalid
+ frame.setExtendedFrameFormat(false);
+ frame.setFrameId(536870912u);
+ QCOMPARE(frame.frameId(), 0u);
+ QVERIFY(!frame.isValid());
+ QVERIFY(!frame.hasExtendedFrameFormat());
}
void tst_QCanBusFrame::payload()
@@ -125,6 +146,15 @@ void tst_QCanBusFrame::payload()
QVERIFY(frame.payload().isEmpty());
frame.setPayload("test");
QCOMPARE(frame.payload().data(), "test");
+ QVERIFY(!frame.hasFlexibleDataRateFormat());
+ // setting long payload should automatically set hasFlexibleDataRateFormat()
+ frame.setPayload("testtesttest");
+ QCOMPARE(frame.payload().data(), "testtesttest");
+ QVERIFY(frame.hasFlexibleDataRateFormat());
+ // setting short payload should not change hasFlexibleDataRateFormat()
+ frame.setPayload("test");
+ QCOMPARE(frame.payload().data(), "test");
+ QVERIFY(frame.hasFlexibleDataRateFormat());
}
void tst_QCanBusFrame::timeStamp()
@@ -133,10 +163,21 @@ void tst_QCanBusFrame::timeStamp()
QCanBusFrame::TimeStamp timeStamp = frame.timeStamp();
QVERIFY(!timeStamp.seconds());
QVERIFY(!timeStamp.microSeconds());
- timeStamp.setMicroSeconds(5);
- timeStamp.setSeconds(4);
- QCOMPARE(timeStamp.seconds(), 4);
- QCOMPARE(timeStamp.microSeconds(), 5);
+
+ // fromMicroSeconds: no microsecond overflow
+ timeStamp = QCanBusFrame::TimeStamp::fromMicroSeconds(999999);
+ QCOMPARE(timeStamp.seconds(), 0);
+ QCOMPARE(timeStamp.microSeconds(), 999999);
+
+ // fromMicroSeconds: microsecond overflow
+ timeStamp = QCanBusFrame::TimeStamp::fromMicroSeconds(1000000);
+ QCOMPARE(timeStamp.seconds(), 1);
+ QCOMPARE(timeStamp.microSeconds(), 0);
+
+ // fromMicroSeconds: microsecond overflow
+ timeStamp = QCanBusFrame::TimeStamp::fromMicroSeconds(2000001);
+ QCOMPARE(timeStamp.seconds(), 2);
+ QCOMPARE(timeStamp.microSeconds(), 1);
}
void tst_QCanBusFrame::tst_isValid_data()
@@ -146,40 +187,53 @@ void tst_QCanBusFrame::tst_isValid_data()
QTest::addColumn<QByteArray>("payload");
QTest::addColumn<uint>("id");
QTest::addColumn<bool>("extended");
+ QTest::addColumn<bool>("flexibleData");
QTest::newRow("invalid frame")
<< QCanBusFrame::InvalidFrame << false
- << QByteArray() << 0u << false;
+ << QByteArray() << 0u << false << false;
QTest::newRow("data frame")
<< QCanBusFrame::DataFrame << true
- << QByteArray() << 0u << false;
+ << QByteArray() << 0u << false << false;
QTest::newRow("error frame")
<< QCanBusFrame::ErrorFrame << true
- << QByteArray() << 0u << false;
+ << QByteArray() << 0u << false << false;
QTest::newRow("remote request frame")
<< QCanBusFrame::RemoteRequestFrame << true
- << QByteArray() << 0u << false;
+ << QByteArray() << 0u << false << false;
+ QTest::newRow("remote request CAN FD frame")
+ << QCanBusFrame::RemoteRequestFrame << false
+ << QByteArray() << 0u << false << true;
QTest::newRow("unknown frame")
<< QCanBusFrame::UnknownFrame << true
- << QByteArray() << 0u << false;
+ << QByteArray() << 0u << false << false;
QTest::newRow("data frame CAN max payload")
<< QCanBusFrame::DataFrame << true
- << QByteArray(8, 0) << 0u << false;
+ << QByteArray(8, 0) << 0u << false << false;
QTest::newRow("data frame CAN FD max payload")
<< QCanBusFrame::DataFrame << true
- << QByteArray(64, 0) << 0u << false;
+ << QByteArray(64, 0) << 0u << false << true;
QTest::newRow("data frame CAN too much payload")
<< QCanBusFrame::DataFrame << false
- << QByteArray(65, 0) << 0u << false;
+ << QByteArray(65, 0) << 0u << false << false;
QTest::newRow("data frame short id")
<< QCanBusFrame::DataFrame << true
- << QByteArray(8, 0) << (1u << 11) - 1 << false;
+ << QByteArray(8, 0) << (1u << 11) - 1 << false << false;
QTest::newRow("data frame long id")
<< QCanBusFrame::DataFrame << true
- << QByteArray(8, 0) << (1u << 11) << true;
+ << QByteArray(8, 0) << (1u << 11) << true << false;
QTest::newRow("data frame bad long id")
<< QCanBusFrame::DataFrame << false
- << QByteArray(8, 0) << (1u << 11) << false;
+ << QByteArray(8, 0) << (1u << 11) << false << false;
+ QTest::newRow("data frame CAN too long payload")
+ << QCanBusFrame::DataFrame << false
+ << QByteArray(9, 0) << 512u << false << false;
+ QTest::newRow("data frame CAN FD long payload")
+ << QCanBusFrame::DataFrame << true
+ << QByteArray(9, 0) << 512u << false << true;
+ QTest::newRow("data frame CAN FD too long payload")
+ << QCanBusFrame::DataFrame << false
+ << QByteArray(65, 0) << 512u << false << true;
}
void tst_QCanBusFrame::tst_isValid()
@@ -189,22 +243,101 @@ void tst_QCanBusFrame::tst_isValid()
QFETCH(QByteArray, payload);
QFETCH(uint, id);
QFETCH(bool, extended);
+ QFETCH(bool, flexibleData);
QCanBusFrame frame(frameType);
frame.setPayload(payload);
frame.setFrameId(id);
frame.setExtendedFrameFormat(extended);
+ frame.setFlexibleDataRateFormat(flexibleData);
QCOMPARE(frame.isValid(), isValid);
QCOMPARE(frame.frameType(), frameType);
QCOMPARE(frame.payload(), payload);
QCOMPARE(frame.frameId(), id);
QCOMPARE(frame.hasExtendedFrameFormat(), extended);
+ QCOMPARE(frame.hasFlexibleDataRateFormat(), flexibleData);
frame.setFrameType(QCanBusFrame::InvalidFrame);
QCOMPARE(frame.isValid(), false);
QCOMPARE(QCanBusFrame::InvalidFrame, frame.frameType());
}
+void tst_QCanBusFrame::tst_toString_data()
+{
+ QTest::addColumn<QCanBusFrame::FrameType>("frameType");
+ QTest::addColumn<quint32>("id");
+ QTest::addColumn<bool>("extended");
+ QTest::addColumn<QByteArray>("payload");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("invalid frame")
+ << QCanBusFrame::InvalidFrame << 0x0u << false
+ << QByteArray()
+ << "(Invalid)";
+ QTest::newRow("error frame")
+ << QCanBusFrame::ErrorFrame << 0x0u << false
+ << QByteArray()
+ << "(Error)";
+ QTest::newRow("unknown frame")
+ << QCanBusFrame::UnknownFrame << 0x0u << false
+ << QByteArray()
+ << "(Unknown)";
+ QTest::newRow("remote request frame")
+ << QCanBusFrame::RemoteRequestFrame << 0x123u << false
+ << QByteArray::fromHex("01") // fake data to get a DLC > 0
+ << QString(" 123 [1] Remote Request");
+ QTest::newRow("data frame min std id")
+ << QCanBusFrame::DataFrame << 0x0u << false
+ << QByteArray()
+ << QString(" 000 [0]");
+ QTest::newRow("data frame max std id")
+ << QCanBusFrame::DataFrame << 0x7FFu << false
+ << QByteArray()
+ << QString(" 7FF [0]");
+ QTest::newRow("data frame min ext id")
+ << QCanBusFrame::DataFrame << 0x0u << true
+ << QByteArray()
+ << QString("00000000 [0]");
+ QTest::newRow("data frame max ext id")
+ << QCanBusFrame::DataFrame << 0x1FFFFFFFu << true
+ << QByteArray()
+ << QString("1FFFFFFF [0]");
+ QTest::newRow("data frame minimal size")
+ << QCanBusFrame::DataFrame << 0x7FFu << false
+ << QByteArray::fromHex("01")
+ << QString(" 7FF [1] 01");
+ QTest::newRow("data frame maximal size")
+ << QCanBusFrame::DataFrame << 0x1FFFFFFFu << true
+ << QByteArray::fromHex("0123456789ABCDEF")
+ << QString("1FFFFFFF [8] 01 23 45 67 89 AB CD EF");
+ QTest::newRow("short data frame FD")
+ << QCanBusFrame::DataFrame << 0x123u << false
+ << QByteArray::fromHex("001122334455667788")
+ << QString(" 123 [09] 00 11 22 33 44 55 66 77 88");
+ QTest::newRow("long data frame FD")
+ << QCanBusFrame::DataFrame << 0x123u << false
+ << QByteArray::fromHex("00112233445566778899")
+ << QString(" 123 [10] 00 11 22 33 44 55 66 77 88 99");
+}
+
+void tst_QCanBusFrame::tst_toString()
+{
+ QFETCH(QCanBusFrame::FrameType, frameType);
+ QFETCH(quint32, id);
+ QFETCH(bool, extended);
+ QFETCH(QByteArray, payload);
+ QFETCH(QString, expected);
+ QCanBusFrame frame;
+ frame.setFrameType(frameType);
+ frame.setFrameId(id);
+ frame.setExtendedFrameFormat(extended);
+ frame.setPayload(payload);
+
+ const QString result = frame.toString();
+
+ QCOMPARE(result, expected);
+}
+
void tst_QCanBusFrame::streaming_data()
{
QTest::addColumn<quint32>("frameId");
@@ -212,30 +345,34 @@ void tst_QCanBusFrame::streaming_data()
QTest::addColumn<qint64>("seconds");
QTest::addColumn<qint64>("microSeconds");
QTest::addColumn<bool>("isExtended");
+ QTest::addColumn<bool>("isFlexibleDataRate");
QTest::addColumn<QCanBusFrame::FrameType>("frameType");
QTest::newRow("emptyFrame") << quint32(0) << QByteArray()
<< qint64(0) << qint64(0)
- << false << QCanBusFrame::DataFrame;
+ << false << false << QCanBusFrame::DataFrame;
QTest::newRow("fullFrame1") << quint32(123) << QByteArray("abcde1")
<< qint64(456) << qint64(784)
- << true << QCanBusFrame::DataFrame;
+ << true << false << QCanBusFrame::DataFrame;
QTest::newRow("fullFrame2") << quint32(123) << QByteArray("abcde2")
<< qint64(457) << qint64(785)
- << false << QCanBusFrame::DataFrame;
+ << false << false << QCanBusFrame::DataFrame;
+ QTest::newRow("fullFrameFD") << quint32(123) << QByteArray("abcdfd")
+ << qint64(457) << qint64(785)
+ << false << true << QCanBusFrame::DataFrame;
QTest::newRow("fullFrame3") << quint32(123) << QByteArray("abcde3")
<< qint64(458) << qint64(786)
- << true << QCanBusFrame::RemoteRequestFrame;
+ << true << false << QCanBusFrame::RemoteRequestFrame;
QTest::newRow("fullFrame4") << quint32(123) << QByteArray("abcde4")
<< qint64(459) << qint64(787)
- << false << QCanBusFrame::RemoteRequestFrame;
+ << false << false << QCanBusFrame::RemoteRequestFrame;
QTest::newRow("fullFrame5") << quint32(123) << QByteArray("abcde5")
<< qint64(460) << qint64(789)
- << true << QCanBusFrame::ErrorFrame;
+ << true << false << QCanBusFrame::ErrorFrame;
QTest::newRow("fullFrame6") << quint32(123) << QByteArray("abcde6")
<< qint64(453) << qint64(788)
- << false << QCanBusFrame::ErrorFrame;
+ << false << false << QCanBusFrame::ErrorFrame;
}
void tst_QCanBusFrame::streaming()
@@ -245,6 +382,7 @@ void tst_QCanBusFrame::streaming()
QFETCH(qint64, seconds);
QFETCH(qint64, microSeconds);
QFETCH(bool, isExtended);
+ QFETCH(bool, isFlexibleDataRate);
QFETCH(QCanBusFrame::FrameType, frameType);
QCanBusFrame originalFrame(frameId, payload);
@@ -252,6 +390,7 @@ void tst_QCanBusFrame::streaming()
originalFrame.setTimeStamp(originalStamp);
originalFrame.setExtendedFrameFormat(isExtended);
+ originalFrame.setFlexibleDataRateFormat(isFlexibleDataRate);
originalFrame.setFrameType(frameType);
QByteArray buffer;
@@ -272,11 +411,13 @@ void tst_QCanBusFrame::streaming()
QCOMPARE(restoredFrame.frameType(), originalFrame.frameType());
QCOMPARE(restoredFrame.hasExtendedFrameFormat(),
originalFrame.hasExtendedFrameFormat());
+ QCOMPARE(restoredFrame.hasFlexibleDataRateFormat(),
+ originalFrame.hasFlexibleDataRateFormat());
}
void tst_QCanBusFrame::tst_error()
{
- QCanBusFrame frame(1);
+ QCanBusFrame frame(1, QByteArray());
QCOMPARE(frame.frameType(), QCanBusFrame::DataFrame);
QCOMPARE(frame.frameId(), 1u);
QCOMPARE(frame.error(), QCanBusFrame::NoError);