summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf3
-rw-r--r--examples/serialbus/can/bitratebox.cpp111
-rw-r--r--examples/serialbus/can/bitratebox.h69
-rw-r--r--examples/serialbus/can/can.pro12
-rw-r--r--examples/serialbus/can/connectdialog.cpp99
-rw-r--r--examples/serialbus/can/connectdialog.h13
-rw-r--r--examples/serialbus/can/connectdialog.ui63
-rw-r--r--examples/serialbus/can/mainwindow.cpp31
-rw-r--r--examples/serialbus/can/mainwindow.h13
-rw-r--r--examples/serialbus/can/mainwindow.ui22
-rw-r--r--examples/serialbus/modbus/adueditor/mainwindow.cpp3
-rw-r--r--examples/serialbus/modbus/slave/mainwindow.cpp8
-rw-r--r--src/plugins/canbus/canbus.pro2
-rw-r--r--src/plugins/canbus/peakcan/main.cpp13
-rw-r--r--src/plugins/canbus/peakcan/peakcan_symbols_p.h55
-rw-r--r--src/plugins/canbus/peakcan/peakcanbackend.cpp160
-rw-r--r--src/plugins/canbus/peakcan/peakcanbackend.h2
-rw-r--r--src/plugins/canbus/peakcan/peakcanbackend_p.h17
-rw-r--r--src/plugins/canbus/socketcan/main.cpp11
-rw-r--r--src/plugins/canbus/socketcan/socketcanbackend.cpp141
-rw-r--r--src/plugins/canbus/socketcan/socketcanbackend.h9
-rw-r--r--src/plugins/canbus/systeccan/main.cpp77
-rw-r--r--src/plugins/canbus/systeccan/plugin.json3
-rw-r--r--src/plugins/canbus/systeccan/systeccan.pro19
-rw-r--r--src/plugins/canbus/systeccan/systeccan_symbols_p.h308
-rw-r--r--src/plugins/canbus/systeccan/systeccanbackend.cpp525
-rw-r--r--src/plugins/canbus/systeccan/systeccanbackend.h78
-rw-r--r--src/plugins/canbus/systeccan/systeccanbackend_p.h110
-rw-r--r--src/plugins/canbus/tinycan/main.cpp11
-rw-r--r--src/plugins/canbus/tinycan/tinycanbackend.cpp59
-rw-r--r--src/plugins/canbus/tinycan/tinycanbackend.h2
-rw-r--r--src/plugins/canbus/tinycan/tinycanbackend_p.h8
-rw-r--r--src/plugins/canbus/vectorcan/main.cpp16
-rw-r--r--src/plugins/canbus/vectorcan/vectorcan_symbols_p.h3
-rw-r--r--src/plugins/canbus/vectorcan/vectorcanbackend.cpp100
-rw-r--r--src/plugins/canbus/vectorcan/vectorcanbackend.h2
-rw-r--r--src/plugins/canbus/vectorcan/vectorcanbackend_p.h7
-rw-r--r--src/serialbus/doc/src/qtcanbus-backends.qdoc6
-rw-r--r--src/serialbus/doc/src/socketcan.qdoc33
-rw-r--r--src/serialbus/doc/src/systeccan.qdoc100
-rw-r--r--src/serialbus/doc/src/vectorcan.qdoc6
-rw-r--r--src/serialbus/qcanbus.cpp103
-rw-r--r--src/serialbus/qcanbus.h5
-rw-r--r--src/serialbus/qcanbusdevice.cpp49
-rw-r--r--src/serialbus/qcanbusdevice.h6
-rw-r--r--src/serialbus/qcanbusdevice_p.h10
-rw-r--r--src/serialbus/qcanbusdeviceinfo.cpp131
-rw-r--r--src/serialbus/qcanbusdeviceinfo.h85
-rw-r--r--src/serialbus/qcanbusdeviceinfo_p.h72
-rw-r--r--src/serialbus/qcanbusfactory.cpp44
-rw-r--r--src/serialbus/qcanbusfactory.h14
-rw-r--r--src/serialbus/qcanbusframe.cpp85
-rw-r--r--src/serialbus/qcanbusframe.h48
-rw-r--r--src/serialbus/qmodbusserver.cpp4
-rw-r--r--src/serialbus/qmodbustcpclient_p.h5
-rw-r--r--src/serialbus/qmodbustcpserver.cpp2
-rw-r--r--src/serialbus/serialbus.pro3
-rw-r--r--src/tools/canbusutil/canbusutil.cpp39
-rw-r--r--src/tools/canbusutil/canbusutil.h2
-rw-r--r--src/tools/canbusutil/main.cpp30
-rw-r--r--src/tools/canbusutil/readtask.cpp23
-rw-r--r--src/tools/canbusutil/readtask.h3
-rw-r--r--tests/auto/plugins/genericcanbus/dummybackend.cpp5
-rw-r--r--tests/auto/plugins/genericcanbus/dummybackend.h2
-rw-r--r--tests/auto/plugins/genericcanbus/main.cpp14
-rw-r--r--tests/auto/plugins/genericcanbusv1/dummybackendv1.cpp80
-rw-r--r--tests/auto/plugins/genericcanbusv1/dummybackendv1.h65
-rw-r--r--tests/auto/plugins/genericcanbusv1/genericcanbusv1.pro17
-rw-r--r--tests/auto/plugins/genericcanbusv1/main.cpp66
-rw-r--r--tests/auto/plugins/genericcanbusv1/plugin.json3
-rw-r--r--tests/auto/plugins/plugins.pro2
-rw-r--r--tests/auto/qcanbus/tst_qcanbus.cpp19
-rw-r--r--tests/auto/qcanbusframe/tst_qcanbusframe.cpp108
-rw-r--r--tests/auto/qmodbusdataunit/tst_qmodbusdataunit.cpp8
74 files changed, 3046 insertions, 436 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 9d7f045..74c762b 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -1,4 +1,5 @@
load(qt_build_config)
CONFIG += warning_clean
+DEFINES += QT_NO_FOREACH
-MODULE_VERSION = 5.8.1
+MODULE_VERSION = 5.9.0
diff --git a/examples/serialbus/can/bitratebox.cpp b/examples/serialbus/can/bitratebox.cpp
new file mode 100644
index 0000000..07aad9c
--- /dev/null
+++ b/examples/serialbus/can/bitratebox.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtSerialBus module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "bitratebox.h"
+
+#include <QLineEdit>
+
+BitRateBox::BitRateBox(QWidget *parent) :
+ QComboBox(parent),
+ m_customSpeedValidator(new QIntValidator(0, 1000000, this))
+{
+ fillBitRates();
+
+ connect(this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
+ this, &BitRateBox::checkCustomSpeedPolicy);
+}
+
+BitRateBox::~BitRateBox()
+{
+ delete m_customSpeedValidator;
+}
+
+int BitRateBox::bitRate() const
+{
+ if (currentIndex() == (count() - 1))
+ return currentText().toInt();
+
+ return itemData(currentIndex()).toInt();
+}
+
+bool BitRateBox::isFlexibleDataRateEnabled() const
+{
+ return m_isFlexibleDataRateEnabled;
+}
+
+void BitRateBox::setFlexibleDateRateEnabled(bool enabled)
+{
+ m_isFlexibleDataRateEnabled = enabled;
+ m_customSpeedValidator->setTop(enabled ? 10000000 : 1000000);
+ fillBitRates();
+}
+
+void BitRateBox::checkCustomSpeedPolicy(int idx)
+{
+ const bool isCustomSpeed = !itemData(idx).isValid();
+ setEditable(isCustomSpeed);
+ if (isCustomSpeed) {
+ clearEditText();
+ lineEdit()->setValidator(m_customSpeedValidator);
+ }
+}
+
+void BitRateBox::fillBitRates()
+{
+ const QList<int> rates = {
+ 10000, 20000, 50000, 100000, 125000, 250000, 500000, 800000, 1000000
+ };
+ const QList<int> dataRates = {
+ 2000000, 4000000, 8000000
+ };
+
+ clear();
+
+ for (int rate : rates)
+ addItem(QString::number(rate), rate);
+
+ if (isFlexibleDataRateEnabled()) {
+ for (int rate : dataRates)
+ addItem(QString::number(rate), rate);
+ }
+
+ addItem(tr("Custom"));
+ setCurrentIndex(6); // default is 500000 bits/sec
+}
diff --git a/examples/serialbus/can/bitratebox.h b/examples/serialbus/can/bitratebox.h
new file mode 100644
index 0000000..e9df635
--- /dev/null
+++ b/examples/serialbus/can/bitratebox.h
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the examples of the QtSerialBus module.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BITRATEBOX_H
+#define BITRATEBOX_H
+
+#include <QComboBox>
+
+class QIntValidator;
+
+class BitRateBox : public QComboBox
+{
+public:
+ BitRateBox(QWidget *parent = nullptr);
+ ~BitRateBox();
+
+ int bitRate() const;
+
+ bool isFlexibleDataRateEnabled() const;
+ void setFlexibleDateRateEnabled(bool enabled);
+
+private slots:
+ void checkCustomSpeedPolicy(int idx);
+
+private:
+ void fillBitRates();
+
+ int m_isFlexibleDataRateEnabled = false;
+ QIntValidator *m_customSpeedValidator = nullptr;
+};
+
+#endif // BITRATEBOX_H
diff --git a/examples/serialbus/can/can.pro b/examples/serialbus/can/can.pro
index aa2234f..c667dbb 100644
--- a/examples/serialbus/can/can.pro
+++ b/examples/serialbus/can/can.pro
@@ -3,12 +3,16 @@ QT += serialbus widgets
TARGET = can
TEMPLATE = app
-SOURCES += main.cpp \
+SOURCES += \
+ bitratebox.cpp \
+ connectdialog.cpp \
+ main.cpp \
mainwindow.cpp \
- connectdialog.cpp
-HEADERS += mainwindow.h \
- connectdialog.h
+HEADERS += \
+ bitratebox.h \
+ connectdialog.h \
+ mainwindow.h \
FORMS += mainwindow.ui \
connectdialog.ui
diff --git a/examples/serialbus/can/connectdialog.cpp b/examples/serialbus/can/connectdialog.cpp
index 780f0ce..e8088e3 100644
--- a/examples/serialbus/can/connectdialog.cpp
+++ b/examples/serialbus/can/connectdialog.cpp
@@ -42,16 +42,13 @@
#include "ui_connectdialog.h"
#include <QCanBus>
-#include <QDebug>
ConnectDialog::ConnectDialog(QWidget *parent) :
QDialog(parent),
- m_ui(new Ui::ConnectDialog),
- m_customSpeedValidator(0)
+ m_ui(new Ui::ConnectDialog)
{
m_ui->setupUi(this);
- m_customSpeedValidator = new QIntValidator(0, 1000000, this);
m_ui->errorFilterEdit->setValidator(new QIntValidator(0, 0x1FFFFFFFU, this));
m_ui->loopbackBox->addItem(tr("unspecified"), QVariant());
@@ -65,19 +62,20 @@ ConnectDialog::ConnectDialog(QWidget *parent) :
m_ui->canFdBox->addItem(tr("false"), QVariant(false));
m_ui->canFdBox->addItem(tr("true"), QVariant(true));
+ m_ui->dataBitrateBox->setFlexibleDateRateEnabled(true);
+
connect(m_ui->okButton, &QPushButton::clicked, this, &ConnectDialog::ok);
connect(m_ui->cancelButton, &QPushButton::clicked, this, &ConnectDialog::cancel);
- connect(m_ui->useConfigurationBox, &QCheckBox::clicked, m_ui->configurationBox, &QGroupBox::setEnabled);
-
- connect(m_ui->speedBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
- this, &ConnectDialog::checkCustomSpeedPolicy);
+ connect(m_ui->useConfigurationBox, &QCheckBox::clicked,
+ m_ui->configurationBox, &QGroupBox::setEnabled);
connect(m_ui->backendListBox, &QComboBox::currentTextChanged,
this, &ConnectDialog::backendChanged);
+ connect(m_ui->interfaceListBox, &QComboBox::currentTextChanged,
+ this, &ConnectDialog::interfaceChanged);
m_ui->rawFilterEdit->hide();
m_ui->rawFilterLabel->hide();
m_ui->backendListBox->addItems(QCanBus::instance()->plugins());
- fillSpeeds();
updateSettings();
}
@@ -92,29 +90,26 @@ ConnectDialog::Settings ConnectDialog::settings() const
return m_currentSettings;
}
-void ConnectDialog::checkCustomSpeedPolicy(int idx)
+void ConnectDialog::backendChanged(const QString &backend)
{
- const bool isCustomSpeed = !m_ui->speedBox->itemData(idx).isValid();
- m_ui->speedBox->setEditable(isCustomSpeed);
- if (isCustomSpeed) {
- m_ui->speedBox->clearEditText();
- QLineEdit *edit = m_ui->speedBox->lineEdit();
- edit->setValidator(m_customSpeedValidator);
- }
+ m_ui->interfaceListBox->clear();
+ m_interfaces = QCanBus::instance()->availableDevices(backend);
+ for (const QCanBusDeviceInfo &info : qAsConst(m_interfaces))
+ m_ui->interfaceListBox->addItem(info.name());
}
-void ConnectDialog::backendChanged(const QString &backend)
+void ConnectDialog::interfaceChanged(const QString &interface)
{
- if (backend == QStringLiteral("generic"))
- m_ui->interfaceNameEdit->setPlaceholderText(QStringLiteral("can0"));
- else if (backend == QStringLiteral("peakcan"))
- 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("can0.0"));
- else if (backend == QStringLiteral("vectorcan"))
- m_ui->interfaceNameEdit->setPlaceholderText(QStringLiteral("can0"));
+ m_ui->isVirtual->setChecked(false);
+ m_ui->isFlexibleDataRateCapable->setChecked(false);
+
+ for (const QCanBusDeviceInfo &info : qAsConst(m_interfaces)) {
+ if (info.name() == interface) {
+ m_ui->isVirtual->setChecked(info.isVirtual());
+ m_ui->isFlexibleDataRateCapable->setChecked(info.hasFlexibleDataRate());
+ break;
+ }
+ }
}
void ConnectDialog::ok()
@@ -133,7 +128,7 @@ QString ConnectDialog::configurationValue(QCanBusDevice::ConfigurationKey key)
{
QVariant result;
- foreach (const ConfigurationItem &item, m_currentSettings.configurations) {
+ for (const ConfigurationItem &item : qAsConst(m_currentSettings.configurations)) {
if (item.first == key) {
result = item.second;
break;
@@ -152,7 +147,7 @@ QString ConnectDialog::configurationValue(QCanBusDevice::ConfigurationKey key)
void ConnectDialog::revertSettings()
{
m_ui->backendListBox->setCurrentText(m_currentSettings.backendName);
- m_ui->interfaceNameEdit->setText(m_currentSettings.deviceInterfaceName);
+ m_ui->interfaceListBox->setCurrentText(m_currentSettings.deviceInterfaceName);
m_ui->useConfigurationBox->setChecked(m_currentSettings.useConfigurationEnabled);
QString value = configurationValue(QCanBusDevice::LoopbackKey);
@@ -165,16 +160,19 @@ void ConnectDialog::revertSettings()
m_ui->errorFilterEdit->setText(value);
value = configurationValue(QCanBusDevice::BitRateKey);
- m_ui->speedBox->setCurrentText(value);
+ m_ui->bitrateBox->setCurrentText(value);
value = configurationValue(QCanBusDevice::CanFdKey);
m_ui->canFdBox->setCurrentText(value);
+
+ value = configurationValue(QCanBusDevice::DataBitRateKey);
+ m_ui->dataBitrateBox->setCurrentText(value);
}
void ConnectDialog::updateSettings()
{
m_currentSettings.backendName = m_ui->backendListBox->currentText();
- m_currentSettings.deviceInterfaceName = m_ui->interfaceNameEdit->text();
+ m_currentSettings.deviceInterfaceName = m_ui->interfaceListBox->currentText();
m_currentSettings.useConfigurationEnabled = m_ui->useConfigurationBox->isChecked();
if (m_currentSettings.useConfigurationEnabled) {
@@ -214,17 +212,9 @@ void ConnectDialog::updateSettings()
}
// process bitrate
- bool ok = false;
- int bitrate = 0;
- if (m_ui->speedBox->currentIndex() == (m_ui->speedBox->count() - 1))
- bitrate = m_ui->speedBox->currentText().toInt(&ok);
- else
- bitrate = m_ui->speedBox->itemData(m_ui->speedBox->currentIndex()).toInt(&ok);
-
- if (ok && (bitrate > 0)) {
- ConfigurationItem item;
- item.first = QCanBusDevice::BitRateKey;
- item.second = QVariant(bitrate);
+ const int bitrate = m_ui->bitrateBox->bitRate();
+ if (bitrate > 0) {
+ const ConfigurationItem item(QCanBusDevice::BitRateKey, QVariant(bitrate));
m_currentSettings.configurations.append(item);
}
@@ -233,21 +223,12 @@ void ConnectDialog::updateSettings()
fdItem.first = QCanBusDevice::CanFdKey;
fdItem.second = m_ui->canFdBox->currentData();
m_currentSettings.configurations.append(fdItem);
- }
-}
-void ConnectDialog::fillSpeeds()
-{
- m_ui->speedBox->addItem(QStringLiteral("10000"), 10000);
- m_ui->speedBox->addItem(QStringLiteral("20000"), 20000);
- m_ui->speedBox->addItem(QStringLiteral("50000"), 50000);
- m_ui->speedBox->addItem(QStringLiteral("100000"), 100000);
- m_ui->speedBox->addItem(QStringLiteral("125000"), 125000);
- m_ui->speedBox->addItem(QStringLiteral("250000"), 250000);
- m_ui->speedBox->addItem(QStringLiteral("500000"), 500000);
- m_ui->speedBox->addItem(QStringLiteral("800000"), 800000);
- m_ui->speedBox->addItem(QStringLiteral("1000000"), 1000000);
- m_ui->speedBox->addItem(tr("Custom"));
-
- m_ui->speedBox->setCurrentIndex(6); // setup 500000 bits/sec by default
+ // process data bitrate
+ const int dataBitrate = m_ui->dataBitrateBox->bitRate();
+ if (dataBitrate > 0) {
+ const ConfigurationItem item(QCanBusDevice::DataBitRateKey, QVariant(dataBitrate));
+ m_currentSettings.configurations.append(item);
+ }
+ }
}
diff --git a/examples/serialbus/can/connectdialog.h b/examples/serialbus/can/connectdialog.h
index a392990..ba90ba6 100644
--- a/examples/serialbus/can/connectdialog.h
+++ b/examples/serialbus/can/connectdialog.h
@@ -42,6 +42,7 @@
#define CONNECTDIALOG_H
#include <QCanBusDevice>
+#include <QCanBusDeviceInfo>
#include <QDialog>
@@ -51,8 +52,6 @@ namespace Ui {
class ConnectDialog;
}
-class QIntValidator;
-
QT_END_NAMESPACE
class ConnectDialog : public QDialog
@@ -66,7 +65,7 @@ public:
QString backendName;
QString deviceInterfaceName;
QList<ConfigurationItem> configurations;
- bool useConfigurationEnabled;
+ bool useConfigurationEnabled = false;
};
explicit ConnectDialog(QWidget *parent = nullptr);
@@ -75,8 +74,8 @@ public:
Settings settings() const;
private slots:
- void checkCustomSpeedPolicy(int idx);
void backendChanged(const QString &backend);
+ void interfaceChanged(const QString &interface);
void ok();
void cancel();
@@ -84,12 +83,10 @@ private:
QString configurationValue(QCanBusDevice::ConfigurationKey key);
void revertSettings();
void updateSettings();
- void fillSpeeds();
-private:
- Ui::ConnectDialog *m_ui;
- QIntValidator *m_customSpeedValidator;
+ Ui::ConnectDialog *m_ui = nullptr;
Settings m_currentSettings;
+ QList<QCanBusDeviceInfo> m_interfaces;
};
#endif // CONNECTDIALOG_H
diff --git a/examples/serialbus/can/connectdialog.ui b/examples/serialbus/can/connectdialog.ui
index 5f35b14..aad6bef 100644
--- a/examples/serialbus/can/connectdialog.ui
+++ b/examples/serialbus/can/connectdialog.ui
@@ -7,14 +7,14 @@
<x>0</x>
<y>0</y>
<width>441</width>
- <height>281</height>
+ <height>341</height>
</rect>
</property>
<property name="windowTitle">
<string>Connect</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
- <item row="4" column="0" colspan="2">
+ <item row="5" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
@@ -66,7 +66,7 @@
</layout>
</widget>
</item>
- <item row="0" column="1" rowspan="4">
+ <item row="0" column="1" rowspan="5">
<widget class="QGroupBox" name="configurationBox">
<property name="enabled">
<bool>false</bool>
@@ -127,14 +127,14 @@
<widget class="QComboBox" name="receiveOwnBox"/>
</item>
<item row="4" column="0">
- <widget class="QLabel" name="speedLabel">
+ <widget class="QLabel" name="bitrateLabel">
<property name="text">
- <string>Speed</string>
+ <string>Bitrate</string>
</property>
</widget>
</item>
<item row="4" column="1">
- <widget class="QComboBox" name="speedBox"/>
+ <widget class="BitRateBox" name="bitrateBox"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="canFdLabel">
@@ -146,6 +146,16 @@
<item row="5" column="1">
<widget class="QComboBox" name="canFdBox"/>
</item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="dataBitrateLabel">
+ <property name="text">
+ <string>Data Bitrate</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="BitRateBox" name="dataBitrateBox"/>
+ </item>
</layout>
</widget>
</item>
@@ -156,20 +166,57 @@
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
- <widget class="QLineEdit" name="interfaceNameEdit"/>
+ <widget class="QComboBox" name="interfaceListBox">
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
</item>
</layout>
</widget>
</item>
- <item row="3" column="0">
+ <item row="4" column="0">
<widget class="QCheckBox" name="useConfigurationBox">
<property name="text">
<string>Custom configuration</string>
</property>
</widget>
</item>
+ <item row="2" column="0">
+ <widget class="QGroupBox" name="interfacePropertiesBox">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>CAN interface properties</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QCheckBox" name="isFlexibleDataRateCapable">
+ <property name="text">
+ <string>Flexible Data Rate</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="isVirtual">
+ <property name="text">
+ <string>Virtual</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>BitRateBox</class>
+ <extends>QComboBox</extends>
+ <header>bitratebox.h</header>
+ </customwidget>
+ </customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/examples/serialbus/can/mainwindow.cpp b/examples/serialbus/can/mainwindow.cpp
index 7c0925b..f950938 100644
--- a/examples/serialbus/can/mainwindow.cpp
+++ b/examples/serialbus/can/mainwindow.cpp
@@ -50,8 +50,7 @@
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
- m_ui(new Ui::MainWindow),
- m_canDevice(nullptr)
+ m_ui(new Ui::MainWindow)
{
m_ui->setupUi(this);
@@ -130,7 +129,7 @@ void MainWindow::connectDevice()
this, &MainWindow::framesWritten);
if (p.useConfigurationEnabled) {
- foreach (const ConnectDialog::ConfigurationItem &item, p.configurations)
+ for (const ConnectDialog::ConfigurationItem &item : p.configurations)
m_canDevice->setConfigurationParameter(item.first, item.second);
}
@@ -185,6 +184,17 @@ void MainWindow::closeEvent(QCloseEvent *event)
event->accept();
}
+static QString frameFlags(const QCanBusFrame &frame)
+{
+ if (frame.hasBitrateSwitch() && frame.hasErrorStateIndicator())
+ return QStringLiteral(" B E ");
+ if (frame.hasBitrateSwitch())
+ return QStringLiteral(" B - ");
+ if (frame.hasErrorStateIndicator())
+ return QStringLiteral(" - E ");
+ return QStringLiteral(" - - ");
+}
+
void MainWindow::checkMessages()
{
if (!m_canDevice)
@@ -195,7 +205,7 @@ void MainWindow::checkMessages()
QString view;
if (frame.frameType() == QCanBusFrame::ErrorFrame)
- interpretError(view, frame);
+ view = m_canDevice->interpretErrorFrame(frame);
else
view = frame.toString();
@@ -203,7 +213,9 @@ void MainWindow::checkMessages()
.arg(frame.timeStamp().seconds(), 10, 10, QLatin1Char(' '))
.arg(frame.timeStamp().microSeconds() / 100, 4, 10, QLatin1Char('0'));
- m_ui->receivedMessagesEdit->append(time + view);
+ const QString flags = frameFlags(frame);
+
+ m_ui->receivedMessagesEdit->append(time + flags + view);
}
}
@@ -233,6 +245,7 @@ void MainWindow::sendMessage() const
frame.setFrameId(id);
frame.setExtendedFrameFormat(m_ui->effBox->checkState());
frame.setFlexibleDataRateFormat(m_ui->fdBox->checkState());
+ frame.setBitrateSwitch(m_ui->bitrateSwitchBox->checkState());
if (m_ui->remoteFrame->isChecked())
frame.setFrameType(QCanBusFrame::RemoteRequestFrame);
@@ -243,11 +256,3 @@ void MainWindow::sendMessage() const
m_canDevice->writeFrame(frame);
}
-
-void MainWindow::interpretError(QString &view, const QCanBusFrame &frame)
-{
- if (!m_canDevice)
- return;
-
- view = m_canDevice->interpretErrorFrame(frame);
-}
diff --git a/examples/serialbus/can/mainwindow.h b/examples/serialbus/can/mainwindow.h
index dca2777..7e9a4ff 100644
--- a/examples/serialbus/can/mainwindow.h
+++ b/examples/serialbus/can/mainwindow.h
@@ -66,7 +66,7 @@ public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
-private Q_SLOTS:
+private slots:
void checkMessages();
void sendMessage() const;
void receiveError(QCanBusDevice::CanBusError) const;
@@ -75,17 +75,16 @@ private Q_SLOTS:
void framesWritten(qint64);
protected:
- void closeEvent(QCloseEvent *event);
+ void closeEvent(QCloseEvent *event) override;
private:
void showStatusMessage(const QString &message);
void initActionsConnections();
- void interpretError(QString &, const QCanBusFrame &);
- Ui::MainWindow *m_ui;
- QLabel *m_status;
- ConnectDialog *m_connectDialog;
- QCanBusDevice *m_canDevice;
+ Ui::MainWindow *m_ui = nullptr;
+ QLabel *m_status = nullptr;
+ ConnectDialog *m_connectDialog = nullptr;
+ QCanBusDevice *m_canDevice = nullptr;
};
#endif // MAINWINDOW_H
diff --git a/examples/serialbus/can/mainwindow.ui b/examples/serialbus/can/mainwindow.ui
index 38e1157..72dc62b 100644
--- a/examples/serialbus/can/mainwindow.ui
+++ b/examples/serialbus/can/mainwindow.ui
@@ -40,7 +40,7 @@
</property>
</widget>
</item>
- <item row="0" column="2" rowspan="3">
+ <item row="0" column="2" rowspan="4">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Frame Type</string>
@@ -84,7 +84,7 @@
</property>
</widget>
</item>
- <item row="1" column="3">
+ <item row="2" column="3">
<widget class="QCheckBox" name="effBox">
<property name="toolTip">
<string>Allow extended frames with 29 bit identifier.
@@ -96,21 +96,21 @@ Otherwise, the standard format with 11 bit idenfier is used.</string>
</property>
</widget>
</item>
- <item row="2" column="3">
+ <item row="3" column="3">
<widget class="QPushButton" name="sendButton">
<property name="text">
<string>&amp;Send</string>
</property>
</widget>
</item>
- <item row="1" column="1">
+ <item row="2" column="1">
<widget class="QLineEdit" name="lineEdit">
<property name="placeholderText">
<string>12 34 AB CE</string>
</property>
</widget>
</item>
- <item row="1" column="0">
+ <item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>&amp;Data (hex)</string>
@@ -120,6 +120,14 @@ Otherwise, the standard format with 11 bit idenfier is used.</string>
</property>
</widget>
</item>
+ <item row="1" column="3">
+ <widget class="QCheckBox" name="bitrateSwitchBox">
+ <property name="text">
+ <string>&amp;Bitrate Switch
+(Higher data rate)</string>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
</layout>
@@ -143,7 +151,7 @@ Otherwise, the standard format with 11 bit idenfier is used.</string>
<enum>QFrame::StyledPanel</enum>
</property>
<property name="text">
- <string>Timestamp CAN-ID DLC Data</string>
+ <string>Timestamp Flags CAN-ID DLC Data</string>
</property>
</widget>
</item>
@@ -180,7 +188,7 @@ Otherwise, the standard format with 11 bit idenfier is used.</string>
<x>0</x>
<y>0</y>
<width>551</width>
- <height>21</height>
+ <height>19</height>
</rect>
</property>
<widget class="QMenu" name="menuCalls">
diff --git a/examples/serialbus/modbus/adueditor/mainwindow.cpp b/examples/serialbus/modbus/adueditor/mainwindow.cpp
index 2be46d3..60a5448 100644
--- a/examples/serialbus/modbus/adueditor/mainwindow.cpp
+++ b/examples/serialbus/modbus/adueditor/mainwindow.cpp
@@ -70,7 +70,8 @@ MainWindow::MainWindow(QWidget *parent)
setupUi(this);
s_instance = this;
- foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
+ const auto ports = QSerialPortInfo::availablePorts();
+ for (const QSerialPortInfo &info : ports)
serialPortCombo->addItem(info.portName(), false);
serialPortCombo->insertSeparator(serialPortCombo->count());
serialPortCombo->addItem(QStringLiteral("Add port..."), true);
diff --git a/examples/serialbus/modbus/slave/mainwindow.cpp b/examples/serialbus/modbus/slave/mainwindow.cpp
index 70529d1..ac8cfdd 100644
--- a/examples/serialbus/modbus/slave/mainwindow.cpp
+++ b/examples/serialbus/modbus/slave/mainwindow.cpp
@@ -283,7 +283,7 @@ void MainWindow::setupDeviceData()
}
bool ok;
- foreach (QLineEdit *widget, registers) {
+ for (QLineEdit *widget : qAsConst(registers)) {
if (widget->objectName().startsWith(QStringLiteral("inReg"))) {
modbusDevice->setData(QModbusDataUnit::InputRegisters, widget->property("ID").toInt(),
widget->text().toInt(&ok, 16));
@@ -301,19 +301,19 @@ void MainWindow::setupWidgetContainers()
QRegularExpression regexp(QStringLiteral("coils_(?<ID>\\d+)"));
const QList<QCheckBox *> coils = findChildren<QCheckBox *>(regexp);
- foreach (QCheckBox *cbx, coils)
+ for (QCheckBox *cbx : coils)
coilButtons.addButton(cbx, regexp.match(cbx->objectName()).captured("ID").toInt());
connect(&coilButtons, SIGNAL(buttonClicked(int)), this, SLOT(coilChanged(int)));
regexp.setPattern(QStringLiteral("disc_(?<ID>\\d+)"));
const QList<QCheckBox *> discs = findChildren<QCheckBox *>(regexp);
- foreach (QCheckBox *cbx, discs)
+ for (QCheckBox *cbx : discs)
discreteButtons.addButton(cbx, regexp.match(cbx->objectName()).captured("ID").toInt());
connect(&discreteButtons, SIGNAL(buttonClicked(int)), this, SLOT(discreteInputChanged(int)));
regexp.setPattern(QLatin1String("(in|hold)Reg_(?<ID>\\d+)"));
const QList<QLineEdit *> qle = findChildren<QLineEdit *>(regexp);
- foreach (QLineEdit *lineEdit, qle) {
+ for (QLineEdit *lineEdit : qle) {
registers.insert(lineEdit->objectName(), lineEdit);
lineEdit->setProperty("ID", regexp.match(lineEdit->objectName()).captured("ID").toInt());
lineEdit->setValidator(new QRegExpValidator(QRegExp(QStringLiteral("[0-9a-f]{0,4}"),
diff --git a/src/plugins/canbus/canbus.pro b/src/plugins/canbus/canbus.pro
index 5ad85a4..37db8a3 100644
--- a/src/plugins/canbus/canbus.pro
+++ b/src/plugins/canbus/canbus.pro
@@ -8,5 +8,5 @@ qtConfig(socketcan) {
qtConfig(library) {
SUBDIRS += peakcan tinycan
- win32:SUBDIRS += vectorcan
+ win32:SUBDIRS += systeccan vectorcan
}
diff --git a/src/plugins/canbus/peakcan/main.cpp b/src/plugins/canbus/peakcan/main.cpp
index 10bdfd1..7444f6d 100644
--- a/src/plugins/canbus/peakcan/main.cpp
+++ b/src/plugins/canbus/peakcan/main.cpp
@@ -43,14 +43,21 @@
QT_BEGIN_NAMESPACE
-class PeakCanBusPlugin : public QObject, public QCanBusFactory
+class PeakCanBusPlugin : public QObject, public QCanBusFactoryV2
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QCanBusFactory" FILE "plugin.json")
- Q_INTERFACES(QCanBusFactory)
-
+ Q_INTERFACES(QCanBusFactoryV2)
public:
+ QList<QCanBusDeviceInfo> availableDevices(QString *errorMessage) const override
+ {
+ if (Q_UNLIKELY(!PeakCanBackend::canCreate(errorMessage)))
+ return QList<QCanBusDeviceInfo>();
+
+ return PeakCanBackend::interfaces();
+ }
+
QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
{
QString errorReason;
diff --git a/src/plugins/canbus/peakcan/peakcan_symbols_p.h b/src/plugins/canbus/peakcan/peakcan_symbols_p.h
index c3ba252..7c91750 100644
--- a/src/plugins/canbus/peakcan/peakcan_symbols_p.h
+++ b/src/plugins/canbus/peakcan/peakcan_symbols_p.h
@@ -83,23 +83,39 @@ extern "C"
#define PCAN_DNGBUS1 0x31 // PCAN-Dongle/LPT interface, channel 1
-#define PCAN_PCIBUS1 0x41 // PCAN-PCI interface, channel 1
-#define PCAN_PCIBUS2 0x42 // PCAN-PCI interface, channel 2
-#define PCAN_PCIBUS3 0x43 // PCAN-PCI interface, channel 3
-#define PCAN_PCIBUS4 0x44 // PCAN-PCI interface, channel 4
-#define PCAN_PCIBUS5 0x45 // PCAN-PCI interface, channel 5
-#define PCAN_PCIBUS6 0x46 // PCAN-PCI interface, channel 6
-#define PCAN_PCIBUS7 0x47 // PCAN-PCI interface, channel 7
-#define PCAN_PCIBUS8 0x48 // PCAN-PCI interface, channel 8
-
-#define PCAN_USBBUS1 0x51 // PCAN-USB interface, channel 1
-#define PCAN_USBBUS2 0x52 // PCAN-USB interface, channel 2
-#define PCAN_USBBUS3 0x53 // PCAN-USB interface, channel 3
-#define PCAN_USBBUS4 0x54 // PCAN-USB interface, channel 4
-#define PCAN_USBBUS5 0x55 // PCAN-USB interface, channel 5
-#define PCAN_USBBUS6 0x56 // PCAN-USB interface, channel 6
-#define PCAN_USBBUS7 0x57 // PCAN-USB interface, channel 7
-#define PCAN_USBBUS8 0x58 // PCAN-USB interface, channel 8
+#define PCAN_PCIBUS1 0x41 // PCAN-PCI interface, channel 1
+#define PCAN_PCIBUS2 0x42 // PCAN-PCI interface, channel 2
+#define PCAN_PCIBUS3 0x43 // PCAN-PCI interface, channel 3
+#define PCAN_PCIBUS4 0x44 // PCAN-PCI interface, channel 4
+#define PCAN_PCIBUS5 0x45 // PCAN-PCI interface, channel 5
+#define PCAN_PCIBUS6 0x46 // PCAN-PCI interface, channel 6
+#define PCAN_PCIBUS7 0x47 // PCAN-PCI interface, channel 7
+#define PCAN_PCIBUS8 0x48 // PCAN-PCI interface, channel 8
+#define PCAN_PCIBUS9 0x409 // PCAN-PCI interface, channel 9
+#define PCAN_PCIBUS10 0x40A // PCAN-PCI interface, channel 10
+#define PCAN_PCIBUS11 0x40B // PCAN-PCI interface, channel 11
+#define PCAN_PCIBUS12 0x40C // PCAN-PCI interface, channel 12
+#define PCAN_PCIBUS13 0x40D // PCAN-PCI interface, channel 13
+#define PCAN_PCIBUS14 0x40E // PCAN-PCI interface, channel 14
+#define PCAN_PCIBUS15 0x40F // PCAN-PCI interface, channel 15
+#define PCAN_PCIBUS16 0x410 // PCAN-PCI interface, channel 16
+
+#define PCAN_USBBUS1 0x51 // PCAN-USB interface, channel 1
+#define PCAN_USBBUS2 0x52 // PCAN-USB interface, channel 2
+#define PCAN_USBBUS3 0x53 // PCAN-USB interface, channel 3
+#define PCAN_USBBUS4 0x54 // PCAN-USB interface, channel 4
+#define PCAN_USBBUS5 0x55 // PCAN-USB interface, channel 5
+#define PCAN_USBBUS6 0x56 // PCAN-USB interface, channel 6
+#define PCAN_USBBUS7 0x57 // PCAN-USB interface, channel 7
+#define PCAN_USBBUS8 0x58 // PCAN-USB interface, channel 8
+#define PCAN_USBBUS9 0x509 // PCAN-USB interface, channel 9
+#define PCAN_USBBUS10 0x50A // PCAN-USB interface, channel 10
+#define PCAN_USBBUS11 0x50B // PCAN-USB interface, channel 11
+#define PCAN_USBBUS12 0x50C // PCAN-USB interface, channel 12
+#define PCAN_USBBUS13 0x50D // PCAN-USB interface, channel 13
+#define PCAN_USBBUS14 0x50E // PCAN-USB interface, channel 14
+#define PCAN_USBBUS15 0x50F // PCAN-USB interface, channel 15
+#define PCAN_USBBUS16 0x510 // PCAN-USB interface, channel 16
#define PCAN_PCCBUS1 0x61 // PCAN-PC Card interface, channel 1
#define PCAN_PCCBUS2 0x62 // PCAN-PC Card interface, channel 2
@@ -162,6 +178,9 @@ extern "C"
#define PCAN_TRACE_SIZE 0x13 // Configuration of the maximum file size of a CAN trace
#define PCAN_TRACE_CONFIGURE 0x14 // Configuration of the trace file storing mode (TRACE_FILE_***)
#define PCAN_CHANNEL_IDENTIFYING 0x15 // Phisical identification of a USB based PCAN-Channel by blinking its associated LED
+#define PCAN_CHANNEL_FEATURES 0x16 // Capabilities of a PCAN device (FEATURE_***)
+
+#define FEATURE_FD_CAPABLE 0x01 // Device supports flexible data-rate (CAN-FD)
// PCAN parameter values
#define PCAN_PARAMETER_OFF 0x00 // The PCAN parameter is not set (inactive)
@@ -225,7 +244,7 @@ extern "C"
#define PCAN_TYPE_DNG_SJA_EPP 0x06 // PCAN-Dongle EPP SJA1000
// Type definitions
-#define TPCANHandle quint8 // Represents a PCAN hardware channel handle
+#define TPCANHandle quint16 // Represents a PCAN hardware channel handle
#define TPCANStatus quint32 // Represents a PCAN status/error code
#define TPCANParameter quint8 // Represents a PCAN parameter to be read or set
#define TPCANDevice quint8 // Represents a PCAN device
diff --git a/src/plugins/canbus/peakcan/peakcanbackend.cpp b/src/plugins/canbus/peakcan/peakcanbackend.cpp
index 57f954e..b6eb30e 100644
--- a/src/plugins/canbus/peakcan/peakcanbackend.cpp
+++ b/src/plugins/canbus/peakcan/peakcanbackend.cpp
@@ -64,7 +64,7 @@ bool PeakCanBackend::canCreate(QString *errorReason)
return true;
#else
static bool symbolsResolved = resolveSymbols(pcanLibrary());
- if (!symbolsResolved) {
+ if (Q_UNLIKELY(!symbolsResolved)) {
*errorReason = pcanLibrary()->errorString();
return false;
}
@@ -72,6 +72,65 @@ bool PeakCanBackend::canCreate(QString *errorReason)
#endif
}
+struct PcanChannel{
+ char name[6];
+ TPCANHandle index;
+};
+PcanChannel pcanChannels[] = {
+ { "usb0", PCAN_USBBUS1 },
+ { "usb1", PCAN_USBBUS2 },
+ { "usb2", PCAN_USBBUS3 },
+ { "usb3", PCAN_USBBUS4 },
+ { "usb4", PCAN_USBBUS5 },
+ { "usb5", PCAN_USBBUS6 },
+ { "usb6", PCAN_USBBUS7 },
+ { "usb7", PCAN_USBBUS8 },
+ { "usb8", PCAN_USBBUS9 },
+ { "usb9", PCAN_USBBUS10 },
+ { "usb10", PCAN_USBBUS11 },
+ { "usb11", PCAN_USBBUS12 },
+ { "usb12", PCAN_USBBUS13 },
+ { "usb13", PCAN_USBBUS14 },
+ { "usb14", PCAN_USBBUS15 },
+ { "usb15", PCAN_USBBUS16 },
+ { "pci0", PCAN_PCIBUS1 },
+ { "pci1", PCAN_PCIBUS2 },
+ { "pci2", PCAN_PCIBUS3 },
+ { "pci3", PCAN_PCIBUS4 },
+ { "pci4", PCAN_PCIBUS5 },
+ { "pci5", PCAN_PCIBUS6 },
+ { "pci6", PCAN_PCIBUS7 },
+ { "pci7", PCAN_PCIBUS8 },
+ { "pci8", PCAN_PCIBUS9 },
+ { "pci9", PCAN_PCIBUS10 },
+ { "pci10", PCAN_PCIBUS11 },
+ { "pci11", PCAN_PCIBUS12 },
+ { "pci12", PCAN_PCIBUS13 },
+ { "pci13", PCAN_PCIBUS14 },
+ { "pci14", PCAN_PCIBUS15 },
+ { "pci15", PCAN_PCIBUS16 },
+ { "none", PCAN_NONEBUS }
+};
+
+QList<QCanBusDeviceInfo> PeakCanBackend::interfaces()
+{
+ QList<QCanBusDeviceInfo> result;
+
+ for (int i = 0; pcanChannels[i].index != PCAN_NONEBUS; ++i) {
+ int value;
+ const TPCANStatus stat = ::CAN_GetValue(pcanChannels[i].index, PCAN_CHANNEL_CONDITION,
+ &value, sizeof(value));
+ if ((stat == PCAN_ERROR_OK) && (value & PCAN_CHANNEL_AVAILABLE)) {
+ const TPCANStatus fdStat = ::CAN_GetValue(pcanChannels[i].index, PCAN_CHANNEL_FEATURES,
+ &value, sizeof(value));
+ const bool isFd = (fdStat == PCAN_ERROR_OK) && (value & FEATURE_FD_CAPABLE);
+ result.append(createDeviceInfo(QLatin1String(pcanChannels[i].name), false, isFd));
+ }
+ }
+
+ return result;
+}
+
#if defined(Q_OS_WIN32)
class ReadNotifier : public QWinEventNotifier
{
@@ -149,15 +208,6 @@ private:
PeakCanBackendPrivate::PeakCanBackendPrivate(PeakCanBackend *q)
: q_ptr(q)
- , isOpen(false)
- , channelIndex(PCAN_NONEBUS)
- , writeNotifier(nullptr)
- , readNotifier(nullptr)
-#if defined(Q_OS_WIN32)
- , readHandle(INVALID_HANDLE_VALUE)
-#else
- , readHandle(-1)
-#endif
{
}
@@ -209,7 +259,7 @@ bool PeakCanBackendPrivate::open()
const int bitrateCode = bitrateCodeFromBitrate(bitrate);
const TPCANStatus st = ::CAN_Initialize(channelIndex, bitrateCode, 0, 0, 0);
- if (st != PCAN_ERROR_OK) {
+ if (Q_UNLIKELY(st != PCAN_ERROR_OK)) {
q->setError(systemErrorString(st), QCanBusDevice::ConnectionError);
return false;
}
@@ -225,7 +275,7 @@ bool PeakCanBackendPrivate::open()
#endif
const TPCANStatus err = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &readHandle, sizeof(readHandle));
- if (err != PCAN_ERROR_OK) {
+ if (Q_UNLIKELY(err != PCAN_ERROR_OK)) {
q->setError(systemErrorString(err), QCanBusDevice::ConnectionError);
return false;
}
@@ -252,16 +302,16 @@ void PeakCanBackendPrivate::close()
quint32 value = 0;
const TPCANStatus err = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &value, sizeof(value));
- if (err != PCAN_ERROR_OK)
+ if (Q_UNLIKELY(err != PCAN_ERROR_OK))
emit q->setError(systemErrorString(err), QCanBusDevice::ConnectionError);
const TPCANStatus st = ::CAN_Uninitialize(channelIndex);
- if (st != PCAN_ERROR_OK)
+ if (Q_UNLIKELY(st != PCAN_ERROR_OK))
q->setError(systemErrorString(st), QCanBusDevice::ConnectionError);
#if defined(Q_OS_WIN32)
if (readHandle && (readHandle != INVALID_HANDLE_VALUE)) {
- if (!::CloseHandle(readHandle))
+ if (Q_UNLIKELY(!::CloseHandle(readHandle)))
q->setError(qt_error_string(::GetLastError()), QCanBusDevice::ConnectionError);
readHandle = INVALID_HANDLE_VALUE;
}
@@ -286,47 +336,12 @@ bool PeakCanBackendPrivate::setConfigurationParameter(int key, const QVariant &v
}
}
-static int channelIndexFromName(const QString &interfaceName)
-{
- if (interfaceName == QStringLiteral("usb0"))
- return PCAN_USBBUS1;
- else if (interfaceName == QStringLiteral("usb1"))
- return PCAN_USBBUS2;
- else if (interfaceName == QStringLiteral("usb2"))
- return PCAN_USBBUS3;
- else if (interfaceName == QStringLiteral("usb3"))
- return PCAN_USBBUS4;
- else if (interfaceName == QStringLiteral("usb4"))
- return PCAN_USBBUS5;
- else if (interfaceName == QStringLiteral("usb5"))
- return PCAN_USBBUS6;
- else if (interfaceName == QStringLiteral("usb6"))
- return PCAN_USBBUS7;
- else if (interfaceName == QStringLiteral("usb7"))
- return PCAN_USBBUS8;
- else if (interfaceName == QStringLiteral("pci0"))
- return PCAN_PCIBUS1;
- else if (interfaceName == QStringLiteral("pci1"))
- return PCAN_PCIBUS2;
- else if (interfaceName == QStringLiteral("pci2"))
- return PCAN_PCIBUS3;
- else if (interfaceName == QStringLiteral("pci3"))
- return PCAN_PCIBUS4;
- else if (interfaceName == QStringLiteral("pci4"))
- return PCAN_PCIBUS5;
- else if (interfaceName == QStringLiteral("pci5"))
- return PCAN_PCIBUS6;
- else if (interfaceName == QStringLiteral("pci6"))
- return PCAN_PCIBUS7;
- else if (interfaceName == QStringLiteral("pci7"))
- return PCAN_PCIBUS8;
- else // TODO: Add other indexes here
- return PCAN_NONEBUS;
-}
-
-void PeakCanBackendPrivate::setupChannel(const QString &interfaceName)
+void PeakCanBackendPrivate::setupChannel(const QByteArray &interfaceName)
{
- channelIndex = channelIndexFromName(interfaceName);
+ const PcanChannel *chn = pcanChannels;
+ while (chn->index != PCAN_NONEBUS && chn->name != interfaceName)
+ ++chn;
+ channelIndex = chn->index;
}
// Calls only when the device is closed
@@ -340,7 +355,7 @@ void PeakCanBackendPrivate::setupDefaultConfigurations()
QString PeakCanBackendPrivate::systemErrorString(int errorCode)
{
QByteArray buffer(256, 0);
- if (::CAN_GetErrorText(errorCode, 0, buffer.data()) != PCAN_ERROR_OK)
+ if (Q_UNLIKELY(::CAN_GetErrorText(errorCode, 0, buffer.data()) != PCAN_ERROR_OK))
return PeakCanBackend::tr("Unable to retrieve an error string");
return QString::fromLatin1(buffer);
}
@@ -370,7 +385,7 @@ void PeakCanBackendPrivate::startWrite()
::memcpy(message.DATA, payload.constData(), sizeof(message.DATA));
const TPCANStatus st = ::CAN_Write(channelIndex, &message);
- if (st != PCAN_ERROR_OK)
+ if (Q_UNLIKELY(st != PCAN_ERROR_OK))
q->setError(systemErrorString(st), QCanBusDevice::WriteError);
else
emit q->framesWritten(qint64(1));
@@ -385,7 +400,7 @@ void PeakCanBackendPrivate::startRead()
QVector<QCanBusFrame> newFrames;
- forever {
+ for (;;) {
TPCANMsg message;
::memset(&message, 0, sizeof(message));
TPCANTimestamp timestamp;
@@ -393,7 +408,7 @@ void PeakCanBackendPrivate::startRead()
const TPCANStatus st = ::CAN_Read(channelIndex, &message, &timestamp);
if (st != PCAN_ERROR_OK) {
- if (st != PCAN_ERROR_QRCVEMPTY)
+ if (Q_UNLIKELY(st != PCAN_ERROR_QRCVEMPTY))
q->setError(systemErrorString(st), QCanBusDevice::ReadError);
break;
}
@@ -415,13 +430,13 @@ bool PeakCanBackendPrivate::verifyBitRate(int bitrate)
{
Q_Q(PeakCanBackend);
- if (isOpen) {
+ if (Q_UNLIKELY(isOpen)) {
q->setError(PeakCanBackend::tr("Impossible to reconfigure bitrate for the opened device"),
QCanBusDevice::ConfigurationError);
return false;
}
- if (bitrateCodeFromBitrate(bitrate) == -1) {
+ if (Q_UNLIKELY(bitrateCodeFromBitrate(bitrate) == -1)) {
q->setError(PeakCanBackend::tr("Unsupported bitrate value"),
QCanBusDevice::ConfigurationError);
return false;
@@ -436,7 +451,7 @@ PeakCanBackend::PeakCanBackend(const QString &name, QObject *parent)
{
Q_D(PeakCanBackend);
- d->setupChannel(name);
+ d->setupChannel(name.toLatin1());
d->setupDefaultConfigurations();
}
@@ -455,19 +470,20 @@ bool PeakCanBackend::open()
Q_D(PeakCanBackend);
if (!d->isOpen) {
- if (!d->open())
+ if (Q_UNLIKELY(!d->open()))
return false;
// apply all stored configurations except bitrate, because
// the bitrate can not be applied after opening of device
- foreach (int key, configurationKeys()) {
+ const auto keys = configurationKeys();
+ for (int key : keys) {
if (key == QCanBusDevice::BitRateKey)
continue;
const QVariant param = configurationParameter(key);
const bool success = d->setConfigurationParameter(key, param);
- if (!success) {
- qWarning() << "Cannot apply parameter:" << key
- << "with value:" << param;
+ if (Q_UNLIKELY(!success)) {
+ qWarning("Cannot apply parameter: %d with value: %ls.",
+ key, qUtf16Printable(param.toString()));
}
}
}
@@ -497,23 +513,23 @@ bool PeakCanBackend::writeFrame(const QCanBusFrame &newData)
{
Q_D(PeakCanBackend);
- if (state() != QCanBusDevice::ConnectedState)
+ if (Q_UNLIKELY(state() != QCanBusDevice::ConnectedState))
return false;
- if (!newData.isValid()) {
+ if (Q_UNLIKELY(!newData.isValid())) {
setError(tr("Cannot write invalid QCanBusFrame"), QCanBusDevice::WriteError);
return false;
}
- if (newData.frameType() != QCanBusFrame::DataFrame
- && newData.frameType() != QCanBusFrame::RemoteRequestFrame) {
+ if (Q_UNLIKELY(newData.frameType() != QCanBusFrame::DataFrame
+ && newData.frameType() != QCanBusFrame::RemoteRequestFrame)) {
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() > 8) {
+ if (Q_UNLIKELY(newData.payload().size() > 8)) {
setError(tr("CAN FD frame format not supported."), QCanBusDevice::WriteError);
return false;
}
diff --git a/src/plugins/canbus/peakcan/peakcanbackend.h b/src/plugins/canbus/peakcan/peakcanbackend.h
index 5e26156..7f7083e 100644
--- a/src/plugins/canbus/peakcan/peakcanbackend.h
+++ b/src/plugins/canbus/peakcan/peakcanbackend.h
@@ -40,6 +40,7 @@
#include <QtSerialBus/qcanbusframe.h>
#include <QtSerialBus/qcanbusdevice.h>
+#include <QtSerialBus/qcanbusdeviceinfo.h>
#include <QtCore/qvariant.h>
#include <QtCore/qvector.h>
@@ -68,6 +69,7 @@ public:
QString interpretErrorFrame(const QCanBusFrame &errorFrame) override;
static bool canCreate(QString *errorReason);
+ static QList<QCanBusDeviceInfo> interfaces();
private:
PeakCanBackendPrivate * const d_ptr;
diff --git a/src/plugins/canbus/peakcan/peakcanbackend_p.h b/src/plugins/canbus/peakcan/peakcanbackend_p.h
index c9db554..a6c6458 100644
--- a/src/plugins/canbus/peakcan/peakcanbackend_p.h
+++ b/src/plugins/canbus/peakcan/peakcanbackend_p.h
@@ -39,6 +39,7 @@
#define PEAKCANBACKEND_P_H
#include "peakcanbackend.h"
+#include "peakcan_symbols_p.h"
#if defined(Q_OS_WIN32)
# include <qt_windows.h>
@@ -72,7 +73,7 @@ public:
bool open();
void close();
bool setConfigurationParameter(int key, const QVariant &value);
- void setupChannel(const QString &interfaceName);
+ void setupChannel(const QByteArray &interfaceName);
void setupDefaultConfigurations();
QString systemErrorString(int errorCode);
void startWrite();
@@ -81,16 +82,16 @@ public:
PeakCanBackend * const q_ptr;
- bool isOpen;
- int channelIndex;
- QTimer *writeNotifier;
+ bool isOpen = false;
+ int channelIndex = PCAN_NONEBUS;
+ QTimer *writeNotifier = nullptr;
#if defined(Q_OS_WIN32)
- QWinEventNotifier *readNotifier;
- HANDLE readHandle;
+ QWinEventNotifier *readNotifier = nullptr;
+ HANDLE readHandle = INVALID_HANDLE_VALUE;
#else
- QSocketNotifier *readNotifier;
- int readHandle;
+ QSocketNotifier *readNotifier = nullptr;
+ int readHandle = -1;
#endif
};
diff --git a/src/plugins/canbus/socketcan/main.cpp b/src/plugins/canbus/socketcan/main.cpp
index 715b26f..92e759b 100644
--- a/src/plugins/canbus/socketcan/main.cpp
+++ b/src/plugins/canbus/socketcan/main.cpp
@@ -45,14 +45,19 @@
QT_BEGIN_NAMESPACE
//! [SocketCanFactory]
-class SocketCanBusPlugin : public QObject, public QCanBusFactory
+class SocketCanBusPlugin : public QObject, public QCanBusFactoryV2
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QCanBusFactory" FILE "plugin.json")
- Q_INTERFACES(QCanBusFactory)
-
+ Q_INTERFACES(QCanBusFactoryV2)
public:
+ QList<QCanBusDeviceInfo> availableDevices(QString *errorMessage) const override
+ {
+ Q_UNUSED(errorMessage);
+ return SocketCanBackend::interfaces();
+ }
+
QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
{
Q_UNUSED(errorMessage);
diff --git a/src/plugins/canbus/socketcan/socketcanbackend.cpp b/src/plugins/canbus/socketcan/socketcanbackend.cpp
index def1c53..930221d 100644
--- a/src/plugins/canbus/socketcan/socketcanbackend.cpp
+++ b/src/plugins/canbus/socketcan/socketcanbackend.cpp
@@ -36,8 +36,10 @@
#include "socketcanbackend.h"
-#include <QtCore/qdebug.h>
#include <QtCore/qdatastream.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qdiriterator.h>
+#include <QtCore/qfile.h>
#include <QtCore/qsocketnotifier.h>
#include <linux/can/error.h>
@@ -74,11 +76,78 @@ struct canfd_frame {
QT_BEGIN_NAMESPACE
+const char sysClassNetC[] = "/sys/class/net/";
+const char flagsC[] = "/flags";
+const char mtuC[] = "/mtu";
+const char typeC[] = "/type";
+const char virtualC[] = "virtual";
+
+enum {
+ CanFlexibleDataRateMtu = 72,
+ TypeSocketCan = 280,
+ DeviceIsActive = 1
+};
+
+static QByteArray fileContent(const QString &fileName)
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly))
+ return QByteArray();
+
+ return file.readAll().trimmed();
+}
+
+static bool isFlexibleDataRateCapable(const QString &canDevice)
+{
+ const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(mtuC);
+ const int mtu = fileContent(path).toInt();
+ return mtu == CanFlexibleDataRateMtu;
+}
+
+static bool isVirtual(const QString &canDevice)
+{
+ const QFileInfo fi(QLatin1String(sysClassNetC) + canDevice);
+ return fi.canonicalPath().contains(QLatin1String(virtualC));
+}
+
+static quint32 flags(const QString &canDevice)
+{
+ const QString path = QLatin1String(sysClassNetC) + canDevice + QLatin1String(flagsC);
+ const quint32 result = fileContent(path).toUInt(nullptr, 0);
+ return result;
+}
+
+QList<QCanBusDeviceInfo> SocketCanBackend::interfaces()
+{
+ QList<QCanBusDeviceInfo> result;
+ QDirIterator it(sysClassNetC,
+ QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot,
+ QDirIterator::Subdirectories);
+
+ while (it.hasNext()) {
+ const QString dirEntry = it.next();
+ if (fileContent(dirEntry + QLatin1String(typeC)).toInt() != TypeSocketCan)
+ continue;
+
+ const QString deviceName = dirEntry.mid(strlen(sysClassNetC));
+ if (!(flags(deviceName) & DeviceIsActive))
+ continue;
+
+ auto info = createDeviceInfo(deviceName, isVirtual(deviceName),
+ isFlexibleDataRateCapable(deviceName));
+ result.append(info);
+ }
+
+ std::sort(result.begin(), result.end(),
+ [](const QCanBusDeviceInfo &a, const QCanBusDeviceInfo &b) {
+ return a.name() < b.name();
+ });
+
+ return result;
+}
+
SocketCanBackend::SocketCanBackend(const QString &name) :
- canSocket(-1),
- notifier(nullptr),
- canSocketName(name),
- canFdOptionEnabled(false)
+ canSocketName(name)
{
resetConfigurations();
}
@@ -130,7 +199,8 @@ bool SocketCanBackend::applyConfigurationParameter(int key, const QVariant &valu
case QCanBusDevice::LoopbackKey:
{
const int loopback = value.toBool() ? 1 : 0;
- if (setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback)) < 0) {
+ if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_LOOPBACK,
+ &loopback, sizeof(loopback)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
@@ -141,8 +211,8 @@ bool SocketCanBackend::applyConfigurationParameter(int key, const QVariant &valu
case QCanBusDevice::ReceiveOwnKey:
{
const int receiveOwnMessages = value.toBool() ? 1 : 0;
- if (setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,
- &receiveOwnMessages, sizeof(receiveOwnMessages)) < 0) {
+ if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,
+ &receiveOwnMessages, sizeof(receiveOwnMessages)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
@@ -153,8 +223,8 @@ bool SocketCanBackend::applyConfigurationParameter(int key, const QVariant &valu
case QCanBusDevice::ErrorFilterKey:
{
const int errorMask = value.value<QCanBusFrame::FrameErrors>();
- if (setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
- &errorMask, sizeof(errorMask)) < 0) {
+ if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
+ &errorMask, sizeof(errorMask)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
@@ -170,9 +240,9 @@ bool SocketCanBackend::applyConfigurationParameter(int key, const QVariant &valu
// permit every frame - no restrictions (filter reset)
can_filter filters = {0, 0};
socklen_t s = sizeof(can_filter);
- if (setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FILTER,
- &filters, s) != 0) {
- qWarning() << "Cannot unset socket filters";
+ if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FILTER,
+ &filters, s) != 0)) {
+ qWarning("Cannot unset socket filters");
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
@@ -224,8 +294,8 @@ bool SocketCanBackend::applyConfigurationParameter(int key, const QVariant &valu
filters[i] = filter;
}
- if (setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FILTER,
- filters.constData(), sizeof(filters[0]) * filters.size()) < 0) {
+ if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FILTER,
+ filters.constData(), sizeof(filters[0]) * filters.size()) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
@@ -236,7 +306,8 @@ bool SocketCanBackend::applyConfigurationParameter(int key, const QVariant &valu
case QCanBusDevice::CanFdKey:
{
const int fd_frames = value.toBool() ? 1 : 0;
- if (setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, &fd_frames, sizeof(fd_frames)) < 0) {
+ if (Q_UNLIKELY(setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_FD_FRAMES,
+ &fd_frames, sizeof(fd_frames)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConfigurationError);
break;
@@ -259,14 +330,14 @@ bool SocketCanBackend::connectSocket()
struct sockaddr_can address;
struct ifreq interface;
- if ((canSocket = socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, CAN_RAW)) < 0) {
+ if (Q_UNLIKELY((canSocket = socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, CAN_RAW)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConnectionError);
return false;
}
qstrncpy(interface.ifr_name, canSocketName.toLatin1().constData(), sizeof(interface.ifr_name));
- if (ioctl(canSocket, SIOCGIFINDEX, &interface) < 0) {
+ if (Q_UNLIKELY(ioctl(canSocket, SIOCGIFINDEX, &interface) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConnectionError);
return false;
@@ -275,7 +346,7 @@ bool SocketCanBackend::connectSocket()
address.can_family = AF_CAN;
address.can_ifindex = interface.ifr_ifindex;
- if (bind(canSocket, reinterpret_cast<struct sockaddr *>(&address), sizeof(address)) < 0) {
+ if (Q_UNLIKELY(bind(canSocket, reinterpret_cast<struct sockaddr *>(&address), sizeof(address)) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ConnectionError);
return false;
@@ -288,12 +359,13 @@ bool SocketCanBackend::connectSocket()
this, &SocketCanBackend::readSocket);
//apply all stored configurations
- foreach (int key, configurationKeys()) {
+ const auto keys = configurationKeys();
+ for (int key : keys) {
const QVariant param = configurationParameter(key);
bool success = applyConfigurationParameter(key, param);
- if (!success) {
- qWarning() << "Cannot apply parameter:" << QCanBusDevice::ConfigurationKey(key)
- << "with value:" << param;
+ if (Q_UNLIKELY(!success)) {
+ qWarning("Cannot apply parameter: %d with value: %ls",
+ key, qUtf16Printable(param.toString()));
}
}
@@ -305,9 +377,8 @@ void SocketCanBackend::setConfigurationParameter(int key, const QVariant &value)
if (key == QCanBusDevice::RawFilterKey) {
//verify valid/supported filters
- QList<QCanBusDevice::Filter> filters
- = value.value<QList<QCanBusDevice::Filter> >();
- foreach (QCanBusDevice::Filter f, filters) {
+ const auto filters = value.value<QList<QCanBusDevice::Filter> >();
+ for (QCanBusDevice::Filter f : filters) {
switch (f.type) {
case QCanBusFrame::UnknownFrame:
@@ -345,7 +416,7 @@ bool SocketCanBackend::writeFrame(const QCanBusFrame &newData)
if (state() != ConnectedState)
return false;
- if (!newData.isValid()) {
+ if (Q_UNLIKELY(!newData.isValid())) {
setError(tr("Cannot write invalid QCanBusFrame"), QCanBusDevice::WriteError);
return false;
}
@@ -374,6 +445,8 @@ bool SocketCanBackend::writeFrame(const QCanBusFrame &newData)
memset(&frame, 0, sizeof(frame));
frame.len = newData.payload().size();
frame.can_id = canId;
+ frame.flags = newData.hasBitrateSwitch() ? CANFD_BRS : 0;
+ frame.flags |= newData.hasErrorStateIndicator() ? CANFD_ESI : 0;
::memcpy(frame.data, newData.payload().constData(), frame.len);
bytesWritten = ::write(canSocket, &frame, sizeof(frame));
@@ -387,7 +460,7 @@ bool SocketCanBackend::writeFrame(const QCanBusFrame &newData)
bytesWritten = ::write(canSocket, &frame, sizeof(frame));
}
- if (bytesWritten < 0) {
+ if (Q_UNLIKELY(bytesWritten < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::WriteError);
return false;
@@ -565,7 +638,7 @@ void SocketCanBackend::readSocket()
{
QVector<QCanBusFrame> newFrames;
- while (true) {
+ for (;;) {
struct canfd_frame frame;
int bytesReceived;
@@ -573,18 +646,18 @@ void SocketCanBackend::readSocket()
if (bytesReceived <= 0) {
break;
- } else if (bytesReceived != CANFD_MTU && bytesReceived != CAN_MTU) {
+ } else if (Q_UNLIKELY(bytesReceived != CANFD_MTU && bytesReceived != CAN_MTU)) {
setError(tr("ERROR SocketCanBackend: incomplete CAN frame"),
QCanBusDevice::CanBusError::ReadError);
continue;
- } else if (frame.len > bytesReceived - offsetof(canfd_frame, data)) {
+ } else if (Q_UNLIKELY(frame.len > bytesReceived - offsetof(canfd_frame, data))) {
setError(tr("ERROR SocketCanBackend: invalid CAN frame length"),
QCanBusDevice::CanBusError::ReadError);
continue;
}
struct timeval timeStamp;
- if (ioctl(canSocket, SIOCGSTAMP, &timeStamp) < 0) {
+ if (Q_UNLIKELY(ioctl(canSocket, SIOCGSTAMP, &timeStamp) < 0)) {
setError(qt_error_string(errno),
QCanBusDevice::CanBusError::ReadError);
memset(&timeStamp, 0, sizeof(timeStamp));
@@ -602,6 +675,10 @@ void SocketCanBackend::readSocket()
bufferedFrame.setFrameType(QCanBusFrame::RemoteRequestFrame);
if (frame.can_id & CAN_ERR_FLAG)
bufferedFrame.setFrameType(QCanBusFrame::ErrorFrame);
+ if (frame.flags & CANFD_BRS)
+ bufferedFrame.setBitrateSwitch(true);
+ if (frame.flags & CANFD_ESI)
+ bufferedFrame.setErrorStateIndicator(true);
bufferedFrame.setFrameId(frame.can_id & CAN_EFF_MASK);
diff --git a/src/plugins/canbus/socketcan/socketcanbackend.h b/src/plugins/canbus/socketcan/socketcanbackend.h
index 8de92e4..70c47c8 100644
--- a/src/plugins/canbus/socketcan/socketcanbackend.h
+++ b/src/plugins/canbus/socketcan/socketcanbackend.h
@@ -39,6 +39,7 @@
#include <QtSerialBus/qcanbusframe.h>
#include <QtSerialBus/qcanbusdevice.h>
+#include <QtSerialBus/qcanbusdeviceinfo.h>
#include <QtCore/qsocketnotifier.h>
#include <QtCore/qstring.h>
@@ -62,6 +63,8 @@ public:
QString interpretErrorFrame(const QCanBusFrame &errorFrame) override;
+ static QList<QCanBusDeviceInfo> interfaces();
+
private Q_SLOTS:
void readSocket();
@@ -70,10 +73,10 @@ private:
bool connectSocket();
bool applyConfigurationParameter(int key, const QVariant &value);
- qint64 canSocket;
- QSocketNotifier *notifier;
+ qint64 canSocket = -1;
+ QSocketNotifier *notifier = nullptr;
QString canSocketName;
- bool canFdOptionEnabled;
+ bool canFdOptionEnabled = false;
};
QT_END_NAMESPACE
diff --git a/src/plugins/canbus/systeccan/main.cpp b/src/plugins/canbus/systeccan/main.cpp
new file mode 100644
index 0000000..fcb76a3
--- /dev/null
+++ b/src/plugins/canbus/systeccan/main.cpp
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "systeccanbackend.h"
+
+#include <QtSerialBus/qcanbus.h>
+#include <QtSerialBus/qcanbusdevice.h>
+#include <QtSerialBus/qcanbusfactory.h>
+
+QT_BEGIN_NAMESPACE
+
+class SystecCanBusPlugin : public QObject, public QCanBusFactoryV2
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QCanBusFactory" FILE "plugin.json")
+ Q_INTERFACES(QCanBusFactoryV2)
+
+public:
+ QList<QCanBusDeviceInfo> availableDevices(QString *errorMessage) const override
+ {
+ if (Q_UNLIKELY(!SystecCanBackend::canCreate(errorMessage)))
+ return QList<QCanBusDeviceInfo>();
+
+ return SystecCanBackend::interfaces();
+ }
+
+ QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
+ {
+ QString errorReason;
+ if (Q_UNLIKELY(!SystecCanBackend::canCreate(&errorReason))) {
+ qWarning("%ls", qUtf16Printable(errorReason));
+ if (errorMessage)
+ *errorMessage = errorReason;
+ return nullptr;
+ }
+
+ auto *device = new SystecCanBackend(interfaceName);
+ return device;
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/canbus/systeccan/plugin.json b/src/plugins/canbus/systeccan/plugin.json
new file mode 100644
index 0000000..982c78d
--- /dev/null
+++ b/src/plugins/canbus/systeccan/plugin.json
@@ -0,0 +1,3 @@
+{
+ "Key": "systeccan"
+}
diff --git a/src/plugins/canbus/systeccan/systeccan.pro b/src/plugins/canbus/systeccan/systeccan.pro
new file mode 100644
index 0000000..06d5537
--- /dev/null
+++ b/src/plugins/canbus/systeccan/systeccan.pro
@@ -0,0 +1,19 @@
+TARGET = qtsysteccanbus
+
+QT = core-private serialbus
+
+HEADERS += \
+ systeccanbackend.h \
+ systeccanbackend_p.h \
+ systeccan_symbols_p.h
+
+SOURCES += \
+ main.cpp \
+ systeccanbackend.cpp
+
+DISTFILES = plugin.json
+
+PLUGIN_TYPE = canbus
+PLUGIN_EXTENDS = serialbus
+PLUGIN_CLASS_NAME = SystecCanBusPlugin
+load(qt_plugin)
diff --git a/src/plugins/canbus/systeccan/systeccan_symbols_p.h b/src/plugins/canbus/systeccan/systeccan_symbols_p.h
new file mode 100644
index 0000000..b69db83
--- /dev/null
+++ b/src/plugins/canbus/systeccan/systeccan_symbols_p.h
@@ -0,0 +1,308 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYSTECCAN_SYMBOLS_P_H
+#define SYSTECCAN_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.
+//
+
+#include <QtCore/qlibrary.h>
+#include <QtCore/qstring.h>
+
+#include <windows.h>
+#define DRV_CALLBACK_TYPE WINAPI
+
+typedef quint8 UCANRET;
+typedef quint8 tUcanHandle;
+
+#define GENERATE_SYMBOL_VARIABLE(returnType, symbolName, ...) \
+ typedef returnType (DRV_CALLBACK_TYPE *fp_##symbolName)(__VA_ARGS__); \
+ static fp_##symbolName symbolName;
+
+#define RESOLVE_SYMBOL(symbolName) \
+ symbolName = (fp_##symbolName)systecLibrary->resolve(#symbolName); \
+ if (!symbolName) \
+ return false;
+
+typedef void (DRV_CALLBACK_TYPE *tCallbackFktEx) (tUcanHandle handle, quint32 event, quint8 channel, void *args);
+
+// The Callback function is called, if certain events did occur.
+// These Defines specify the event.
+#define USBCAN_EVENT_INITHW 0 // the USB-CANmodul has been initialized
+#define USBCAN_EVENT_INITCAN 1 // the CAN interface has been initialized
+#define USBCAN_EVENT_RECEIVE 2 // a new CAN message has been received
+#define USBCAN_EVENT_STATUS 3 // the error state in the module has changed
+#define USBCAN_EVENT_DEINITCAN 4 // the CAN interface has been deinitialized (UcanDeinitCan() was called)
+#define USBCAN_EVENT_DEINITHW 5 // the USB-CANmodul has been deinitialized (UcanDeinitHardware() was called)
+#define USBCAN_EVENT_CONNECT 6 // a new USB-CANmodul has been connected
+#define USBCAN_EVENT_DISCONNECT 7 // a USB-CANmodul has been disconnected
+#define USBCAN_EVENT_FATALDISCON 8 // a USB-CANmodul has been disconnected during operation
+#define USBCAN_EVENT_RESERVED1 0x80
+
+#define kUcanModeNormal 0x00 // normal mode (send and receive)
+#define kUcanModeListenOnly 0x01 // listen only mode (only receive)
+#define kUcanModeTxEcho 0x02 // CAN messages which was sent will be received at UcanReadCanMsg..
+#define kUcanModeRxOrderCh 0x04 // reserved (not implemented in this version)
+#define kUcanModeHighResTimer 0x08 // high resolution time stamps in received CAN messages (only available with STM derivates)
+
+// ABR and ACR for mode "receive all CAN messages"
+#define USBCAN_AMR_ALL (quint32) 0xffffffff
+#define USBCAN_ACR_ALL (quint32) 0x00000000
+
+#define USBCAN_OCR_DEFAULT 0x1A // default OCR for standard GW-002
+#define USBCAN_OCR_RS485_ISOLATED 0x1E // OCR for RS485 interface and galvanic isolation
+#define USBCAN_OCR_RS485_NOT_ISOLATED 0x0A // OCR for RS485 interface without galvanic isolation
+#define USBCAN_DEFAULT_BUFFER_ENTRIES 4096
+
+// Structure with init parameters for function UcanInitCanEx() and UcanInitCanEx2()
+#pragma pack(push, 1)
+typedef struct _tUcanInitCanParam {
+ quint32 m_dwSize; // [IN] size of this structure
+ quint8 m_bMode; // [IN] selects the mode of CAN controller (see kUcanMode...)
+
+ // Baudrate Registers for GW-001 or GW-002
+ quint8 m_bBTR0; // [IN] Bus Timing Register 0 (SJA1000 - use high byte USBCAN_BAUD_...)
+ quint8 m_bBTR1; // [IN] Bus Timing Register 1 (SJA1000 - use low byte USBCAN_BAUD_...)
+
+ quint8 m_bOCR; // [IN] Output Control Register of SJA1000 (should be 0x1A)
+ quint32 m_dwAMR; // [IN] Acceptance Mask Register (SJA1000)
+ quint32 m_dwACR; // [IN] Acceptance Code Register (SJA1000)
+
+ // since version V3.00 - is ignored from function UcanInitCanEx() and until m_dwSize < 20
+ quint32 m_dwBaudrate; // [IN] Baudrate Register for Multiport 3004006, USB-CANmodul1 3204000 or USB-CANmodul2 3204002
+ // (use USBCAN_BAUDEX_...)
+
+ // since version V3.05 - is ignored until m_dwSize < 24
+ quint16 m_wNrOfRxBufferEntries; // [IN] number of receive buffer entries (default is 4096)
+ quint16 m_wNrOfTxBufferEntries; // [IN] number of transmit buffer entries (default is 4096)
+} tUcanInitCanParam;
+
+#define USBCAN_BAUDEX_1MBit 0x00020354 // = 1000 kBit/s Sample Point: 68,75%
+#define USBCAN_BAUDEX_800kBit 0x00030254 // = 800 kBit/s Sample Point: 66,67%
+#define USBCAN_BAUDEX_500kBit 0x00050354 // = 500 kBit/s Sample Point: 68,75%
+#define USBCAN_BAUDEX_250kBit 0x000B0354 // = 250 kBit/s Sample Point: 68,75%
+#define USBCAN_BAUDEX_125kBit 0x00170354 // = 125 kBit/s Sample Point: 68,75%
+#define USBCAN_BAUDEX_100kBit 0x00171466 // = 100 kBit/s Sample Point: 65,00%
+#define USBCAN_BAUDEX_50kBit 0x002F1466 // = 50 kBit/s Sample Point: 65,00%
+#define USBCAN_BAUDEX_20kBit 0x00771466 // = 20 kBit/s Sample Point: 65,00%
+#define USBCAN_BAUDEX_10kBit 0x80771466 // = 10 kBit/s Sample Point: 65,00% (CLK = 1, see L-487 since version 15)
+
+// Frame format for a CAN message (bit oriented)
+#define USBCAN_MSG_FF_STD 0x00 // Standard Frame (11-Bit-ID)
+#define USBCAN_MSG_FF_ECHO 0x20 // Tx echo (message received from UcanReadCanMsg.. was previously sent by UcanWriteCanMsg..)
+#define USBCAN_MSG_FF_RTR 0x40 // Remote Transmission Request Frame
+#define USBCAN_MSG_FF_EXT 0x80 // Extended Frame (29-Bit-ID)
+
+typedef struct _tCanMsgStruct {
+ quint32 m_dwID; // CAN Identifier
+ quint8 m_bFF; // CAN Frame format (BIT7=1: 29BitID / BIT6=1: RTR-Frame / BIT5=1: Tx echo)
+ quint8 m_bDLC; // CAN Data Length Code
+ quint8 m_bData[8]; // CAN Data
+ quint32 m_dwTime; // Time in ms
+} tCanMsgStruct;
+
+// Function return codes (encoding)
+#define USBCAN_SUCCESSFUL 0x00 // no error
+#define USBCAN_ERR 0x01 // error in library; function has not been executed
+#define USBCAN_ERRCMD 0x40 // error in module; function has not been executed
+#define USBCAN_WARNING 0x80 // Warning; function has been executed anyway
+#define USBCAN_RESERVED 0xc0 // reserved return codes (up to 255)
+
+// Error messages, that can occur in the library
+#define USBCAN_ERR_RESOURCE 0x01 // could not create a resource (memory, Handle, ...)
+#define USBCAN_ERR_MAXMODULES 0x02 // the maximum number of open modules is exceeded
+#define USBCAN_ERR_HWINUSE 0x03 // a module is already in use
+#define USBCAN_ERR_ILLVERSION 0x04 // the software versions of the module and library are incompatible
+#define USBCAN_ERR_ILLHW 0x05 // the module with the corresponding device number is not connected
+#define USBCAN_ERR_ILLHANDLE 0x06 // wrong USB-CAN-Handle handed over to the function
+#define USBCAN_ERR_ILLPARAM 0x07 // wrong parameter handed over to the function
+#define USBCAN_ERR_BUSY 0x08 // instruction can not be processed at this time
+#define USBCAN_ERR_TIMEOUT 0x09 // no answer from the module
+#define USBCAN_ERR_IOFAILED 0x0a // a request for the driver failed
+#define USBCAN_ERR_DLL_TXFULL 0x0b // the message did not fit into the transmission queue
+#define USBCAN_ERR_MAXINSTANCES 0x0c // maximum number of applications is reached
+#define USBCAN_ERR_CANNOTINIT 0x0d // CAN-interface is not yet initialized
+#define USBCAN_ERR_DISCONNECT 0x0e // USB-CANmodul was disconnected
+#define USBCAN_ERR_DISCONECT USBCAN_ERR_DISCONNECT // renamed (still defined for compatibility reason)
+#define USBCAN_ERR_NOHWCLASS 0x0f // the needed device class does not exist
+#define USBCAN_ERR_ILLCHANNEL 0x10 // illegal CAN channel for GW-001/GW-002
+#define USBCAN_ERR_RESERVED1 0x11
+#define USBCAN_ERR_ILLHWTYPE 0x12 // the API function can not be used with this hardware
+#define USBCAN_ERR_SERVER_TIMEOUT 0x13 // the command server does not send an reply of an command
+
+// Error messages, that the module returns during the command sequence
+#define USBCAN_ERRCMD_NOTEQU 0x40 // the received response does not match with the transmitted command
+#define USBCAN_ERRCMD_REGTST 0x41 // no access to the CAN controller possible
+#define USBCAN_ERRCMD_ILLCMD 0x42 // the module could not interpret the command
+#define USBCAN_ERRCMD_EEPROM 0x43 // error while reading the EEPROM occurred
+#define USBCAN_ERRCMD_RESERVED1 0x44
+#define USBCAN_ERRCMD_RESERVED2 0x45
+#define USBCAN_ERRCMD_RESERVED3 0x46
+#define USBCAN_ERRCMD_ILLBDR 0x47 // illegal baudrate values for Multiport 3004006, USB-CANmodul1 3204000 or USB-CANmodul2 3204002 in BTR0/BTR1
+#define USBCAN_ERRCMD_NOTINIT 0x48 // CAN channel was not initialized
+#define USBCAN_ERRCMD_ALREADYINIT 0x49 // CAN channel was already initialized
+#define USBCAN_ERRCMD_ILLSUBCMD 0x4A // illegal sub-command specified
+#define USBCAN_ERRCMD_ILLIDX 0x4B // illegal index specified (e.g. index for cyclic CAN message)
+#define USBCAN_ERRCMD_RUNNING 0x4C // cyclic CAN message(s) can not be defined because transmission of cyclic CAN messages is already running
+
+// Warning messages, that can occur in library
+// NOTE: These messages are only warnings. The function has been executed anyway.
+#define USBCAN_WARN_NODATA 0x80 // no CAN messages received
+#define USBCAN_WARN_SYS_RXOVERRUN 0x81 // overrun in the receive queue of the driver (but this CAN message is successfuly read)
+#define USBCAN_WARN_DLL_RXOVERRUN 0x82 // overrun in the receive queue of the library (but this CAN message is successfuly read)
+#define USBCAN_WARN_RESERVED1 0x83
+#define USBCAN_WARN_RESERVED2 0x84
+#define USBCAN_WARN_FW_TXOVERRUN 0x85 // overrun in the transmit queue of the firmware (but this CAN message was successfully stored in buffer)
+#define USBCAN_WARN_FW_RXOVERRUN 0x86 // overrun in the receive queue of the firmware (but this CAN message was successfully read)
+#define USBCAN_WARN_FW_TXMSGLOST 0x87 // (not implemented yet)
+#define USBCAN_WARN_NULL_PTR 0x90 // pointer to address is NULL (function will not work correctly)
+#define USBCAN_WARN_TXLIMIT 0x91 // function UcanWriteCanMsgEx() was called for sending more CAN messages than one
+ // But not all of them could be sent because the buffer is full.
+ // Variable pointed by pdwCount_p received the number of successfully sent CAN messages.
+#define USBCAN_WARN_BUSY 0x92 // place holder (only for internaly use)
+
+typedef struct _tUcanHardwareInfoEx {
+ DWORD m_dwSize; // [IN] size of this structure
+ tUcanHandle m_UcanHandle; // [OUT] USB-CAN-Handle assigned by the DLL
+ BYTE m_bDeviceNr; // [OUT] device number of the USB-CANmodul
+ DWORD m_dwSerialNr; // [OUT] serial number from USB-CANmodul
+ DWORD m_dwFwVersionEx; // [OUT] version of firmware
+ DWORD m_dwProductCode; // [OUT] product code (for differentiate between different hardware modules)
+ // see constants USBCAN_PRODCODE_...
+
+ DWORD m_adwUniqueId[4]; // [OUT] unique ID (available since V5.01) !!! m_dwSize must be >= USBCAN_HWINFO_SIZE_V2
+ DWORD m_dwFlags; // [OUT] additional flags
+} tUcanHardwareInfoEx;
+
+#define USBCAN_HWINFO_SIZE_V1 0x12 // size without m_adwDeviceId[]
+#define USBCAN_HWINFO_SIZE_V2 0x22 // size with m_adwDeviceId[]
+#define USBCAN_HWINFO_SIZE_V3 0x26 // size with m_adwDeviceId[] and m_dwFlags
+
+// definitions for product code in structure tUcanHardwareInfoEx
+#define USBCAN_PRODCODE_MASK_DID 0xFFFF0000L
+#define USBCAN_PRODCODE_MASK_MFU 0x00008000L
+#define USBCAN_PRODCODE_PID_TWO_CHA 0x00000001L
+#define USBCAN_PRODCODE_PID_TERM 0x00000001L
+#define USBCAN_PRODCODE_PID_RBUSER 0x00000001L
+#define USBCAN_PRODCODE_PID_RBCAN 0x00000001L
+#define USBCAN_PRODCODE_PID_G4 0x00000020L
+#define USBCAN_PRODCODE_PID_RESVD 0x00000040L
+#define USBCAN_PRODCODE_MASK_PID 0x00007FFFL
+#define USBCAN_PRODCODE_MASK_PIDG3 (USBCAN_PRODCODE_MASK_PID & ~USBCAN_PRODCODE_PID_RESVD)
+
+#define USBCAN_PRODCODE_PID_GW001 0x00001100L // order code GW-001 "USB-CANmodul" outdated
+#define USBCAN_PRODCODE_PID_GW002 0x00001102L // order code GW-002 "USB-CANmodul" outdated
+#define USBCAN_PRODCODE_PID_MULTIPORT 0x00001103L // order code 3004006/3404000/3404001 "Multiport CAN-to-USB"
+#define USBCAN_PRODCODE_PID_BASIC 0x00001104L // order code 3204000/3204001 "USB-CANmodul1"
+#define USBCAN_PRODCODE_PID_ADVANCED 0x00001105L // order code 3204002/3204003 "USB-CANmodul2"
+#define USBCAN_PRODCODE_PID_USBCAN8 0x00001107L // order code 3404000 "USB-CANmodul8"
+#define USBCAN_PRODCODE_PID_USBCAN16 0x00001109L // order code 3404001 "USB-CANmodul16"
+#define USBCAN_PRODCODE_PID_RESERVED3 0x00001110L
+#define USBCAN_PRODCODE_PID_ADVANCED_G4 0x00001121L // order code ------- "USB-CANmodul2" 4th generation
+#define USBCAN_PRODCODE_PID_BASIC_G4 0x00001122L // order code 3204000 "USB-CANmodul1" 4th generation
+#define USBCAN_PRODCODE_PID_RESERVED1 0x00001144L
+#define USBCAN_PRODCODE_PID_RESERVED2 0x00001145L
+#define USBCAN_PRODCODE_PID_RESERVED4 0x00001162L
+
+// checks if the module supports two CAN channels (at logical device)
+#define USBCAN_CHECK_SUPPORT_TWO_CHANNEL(pHwInfoEx) \
+ ((((pHwInfoEx)->m_dwProductCode & USBCAN_PRODCODE_MASK_PID) >= USBCAN_PRODCODE_PID_MULTIPORT) && \
+ (((pHwInfoEx)->m_dwProductCode & USBCAN_PRODCODE_PID_TWO_CHA) != 0) )
+
+typedef struct _tUcanHardwareInitInfo {
+ DWORD m_dwSize; // [IN] size of this structure
+ BOOL m_fDoInitialize; // [IN] specifies if the found module should be initialized by the DLL
+ tUcanHandle *m_pUcanHandle; // [IN] pointer to variable receiving the USB-CAN-Handle
+ tCallbackFktEx m_fpCallbackFktEx; // [IN] pointer to callback function
+ void *m_pCallbackArg; // [IN] pointer to user defined parameter for callback function
+ BOOL m_fTryNext; // [IN] specifies if a further module should be found
+} tUcanHardwareInitInfo;
+
+typedef void (DRV_CALLBACK_TYPE *tUcanEnumCallback) (
+ DWORD dwIndex_p, // [IN] gives a sequential number of the enumerated module
+ BOOL fIsUsed_p, // [IN] set to TRUE if the module is used by another application
+ tUcanHardwareInfoEx *pHwInfoEx_p, // [IN] pointer to the hardware info structure identifying the enumerated module
+ tUcanHardwareInitInfo *pInitInfo_p, // [IN] pointer to an init structure for initializing the module
+ void *pArg_p); // [IN] user argument which was overhand with UcanEnumerateHardware()
+
+#pragma pack(pop)
+
+GENERATE_SYMBOL_VARIABLE(quint32, UcanEnumerateHardware,
+ tUcanEnumCallback /* callback */, void * /* args */, BOOL /* used */,
+ quint8, quint8 /* device number low and high */,
+ quint32, quint32, /* serial low and high */
+ quint32, quint32 /* product code low and high */)
+GENERATE_SYMBOL_VARIABLE(UCANRET, UcanInitHardwareEx, tUcanHandle *, quint8 /* device */,
+ tCallbackFktEx /* callback */, void *)
+GENERATE_SYMBOL_VARIABLE(UCANRET, UcanDeinitHardware, tUcanHandle)
+GENERATE_SYMBOL_VARIABLE(UCANRET, UcanInitCanEx2, tUcanHandle, quint8 /* channel */, tUcanInitCanParam *)
+GENERATE_SYMBOL_VARIABLE(UCANRET, UcanDeinitCanEx, tUcanHandle, quint8 /* channel */)
+GENERATE_SYMBOL_VARIABLE(UCANRET, UcanReadCanMsgEx, tUcanHandle, quint8 *, tCanMsgStruct *, quint32 *)
+GENERATE_SYMBOL_VARIABLE(UCANRET, UcanWriteCanMsgEx, tUcanHandle, quint8, tCanMsgStruct *, quint32 *)
+
+inline bool resolveSymbols(QLibrary *systecLibrary)
+{
+ if (!systecLibrary->isLoaded()) {
+#ifdef Q_PROCESSOR_X86_64
+ systecLibrary->setFileName(QStringLiteral("usbcan64"));
+#else
+ systecLibrary->setFileName(QStringLiteral("usbcan32"));
+#endif
+ if (!systecLibrary->load())
+ return false;
+ }
+
+ RESOLVE_SYMBOL(UcanEnumerateHardware);
+ RESOLVE_SYMBOL(UcanInitHardwareEx);
+ RESOLVE_SYMBOL(UcanDeinitHardware);
+ RESOLVE_SYMBOL(UcanInitCanEx2);
+ RESOLVE_SYMBOL(UcanDeinitCanEx);
+ RESOLVE_SYMBOL(UcanReadCanMsgEx);
+ RESOLVE_SYMBOL(UcanWriteCanMsgEx);
+
+ return true;
+}
+
+#endif // SYSTECCAN_SYMBOLS_P_H
diff --git a/src/plugins/canbus/systeccan/systeccanbackend.cpp b/src/plugins/canbus/systeccan/systeccanbackend.cpp
new file mode 100644
index 0000000..652c8cb
--- /dev/null
+++ b/src/plugins/canbus/systeccan/systeccanbackend.cpp
@@ -0,0 +1,525 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "systeccanbackend.h"
+#include "systeccanbackend_p.h"
+#include "systeccan_symbols_p.h"
+
+#include <QtSerialBus/qcanbusdevice.h>
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QLibrary, systecLibrary)
+
+bool SystecCanBackend::canCreate(QString *errorReason)
+{
+ static bool symbolsResolved = resolveSymbols(systecLibrary());
+ if (Q_UNLIKELY(!symbolsResolved)) {
+ *errorReason = systecLibrary()->errorString();
+ return false;
+ }
+ return true;
+}
+
+static void DRV_CALLBACK_TYPE ucanEnumCallback(DWORD index, BOOL isUsed,
+ tUcanHardwareInfoEx *hardwareInfo,
+ tUcanHardwareInitInfo *initInfo,
+ void *args)
+{
+ auto result = static_cast<QStringList *>(args);
+
+ Q_UNUSED(isUsed);
+ Q_UNUSED(hardwareInfo);
+ Q_UNUSED(initInfo);
+
+ result->append(QString::fromLatin1("can%1.0").arg(index));
+ if (USBCAN_CHECK_SUPPORT_TWO_CHANNEL(hardwareInfo))
+ result->append(QString::fromLatin1("can%1.1").arg(index));
+}
+
+QList<QCanBusDeviceInfo> SystecCanBackend::interfaces()
+{
+ QList<QCanBusDeviceInfo> result;
+
+ QStringList devices;
+ ::UcanEnumerateHardware(&ucanEnumCallback, &devices, false,
+ 0, ~0, 0, ~0, 0, ~0);
+
+ for (const QString &s : qAsConst(devices))
+ result.append(createDeviceInfo(s, false, false));
+ return result;
+}
+
+class OutgoingEventNotifier : public QTimer
+{
+public:
+ OutgoingEventNotifier(SystecCanBackendPrivate *d, QObject *parent) :
+ QTimer(parent),
+ dptr(d)
+ {
+ }
+
+protected:
+ void timerEvent(QTimerEvent *e) override
+ {
+ if (e->timerId() == timerId()) {
+ dptr->startWrite();
+ return;
+ }
+ QTimer::timerEvent(e);
+ }
+
+private:
+ SystecCanBackendPrivate *dptr;
+};
+
+SystecCanBackendPrivate::SystecCanBackendPrivate(SystecCanBackend *q) :
+ q_ptr(q),
+ incomingEventHandler(new IncomingEventHandler(this, q))
+{
+}
+
+static uint bitrateCodeFromBitrate(int bitrate)
+{
+ struct BitrateItem {
+ int bitrate;
+ uint code;
+ } bitrateTable[] = {
+ { 10000, USBCAN_BAUDEX_10kBit },
+ { 20000, USBCAN_BAUDEX_20kBit },
+ { 50000, USBCAN_BAUDEX_50kBit },
+ { 100000, USBCAN_BAUDEX_100kBit },
+ { 125000, USBCAN_BAUDEX_125kBit },
+ { 250000, USBCAN_BAUDEX_250kBit },
+ { 500000, USBCAN_BAUDEX_500kBit },
+ { 800000, USBCAN_BAUDEX_800kBit },
+ { 1000000, USBCAN_BAUDEX_1MBit }
+ };
+
+ const int entries = (sizeof(bitrateTable) / sizeof(*bitrateTable));
+ for (int i = 0; i < entries; ++i)
+ if (bitrateTable[i].bitrate == bitrate)
+ return bitrateTable[i].code;
+
+ return 0;
+}
+
+void IncomingEventHandler::customEvent(QEvent *event)
+{
+ dptr->eventHandler(event);
+}
+
+/*
+ * Do not call functions of USBCAN32.DLL directly from this callback handler.
+ * Use events or windows messages to notify the event to the application.
+ */
+static void DRV_CALLBACK_TYPE ucanCallback(tUcanHandle, quint32 event, quint8, void *args)
+{
+ QEvent::Type type = static_cast<QEvent::Type>(QEvent::User + event);
+ IncomingEventHandler *handler = static_cast<IncomingEventHandler *>(args);
+ qApp->postEvent(handler, new QEvent(type));
+}
+
+bool SystecCanBackendPrivate::open()
+{
+ Q_Q(SystecCanBackend);
+
+ const UCANRET initHardwareRes = ::UcanInitHardwareEx(&handle, device, ucanCallback, incomingEventHandler);
+ if (Q_UNLIKELY(initHardwareRes != USBCAN_SUCCESSFUL)) {
+ q->setError(systemErrorString(initHardwareRes), QCanBusDevice::ConnectionError);
+ return false;
+ }
+
+ const int bitrate = q->configurationParameter(QCanBusDevice::BitRateKey).toInt();
+ const bool receiveOwn = q->configurationParameter(QCanBusDevice::ReceiveOwnKey).toBool();
+
+ tUcanInitCanParam param;
+ ::memset(&param, 0, sizeof(param));
+ param.m_dwSize = sizeof(param);
+ param.m_bMode = receiveOwn ? kUcanModeTxEcho : kUcanModeNormal;
+ param.m_bOCR = USBCAN_OCR_DEFAULT;
+ param.m_dwACR = USBCAN_ACR_ALL;
+ param.m_dwAMR = USBCAN_AMR_ALL;
+ param.m_dwBaudrate = bitrateCodeFromBitrate(bitrate);
+ param.m_wNrOfRxBufferEntries = USBCAN_DEFAULT_BUFFER_ENTRIES;
+ param.m_wNrOfTxBufferEntries = USBCAN_DEFAULT_BUFFER_ENTRIES;
+
+ const UCANRET initCanResult = ::UcanInitCanEx2(handle, channel, &param);
+ if (Q_UNLIKELY(initCanResult != USBCAN_SUCCESSFUL)) {
+ ::UcanDeinitHardware(handle);
+ q->setError(systemErrorString(initCanResult), QCanBusDevice::ConnectionError);
+ return false;
+ }
+
+ return true;
+}
+
+void SystecCanBackendPrivate::close()
+{
+ Q_Q(SystecCanBackend);
+
+ enableWriteNotification(false);
+
+ if (outgoingEventNotifier) {
+ delete outgoingEventNotifier;
+ outgoingEventNotifier = nullptr;
+ }
+
+ const UCANRET deinitCanRes = UcanDeinitCanEx(handle, channel);
+ if (Q_UNLIKELY(deinitCanRes != USBCAN_SUCCESSFUL))
+ q->setError(systemErrorString(deinitCanRes), QCanBusDevice::ConfigurationError);
+
+ // TODO: other channel keeps working?
+ const UCANRET deinitHardwareRes = UcanDeinitHardware(handle);
+ if (Q_UNLIKELY(deinitHardwareRes != USBCAN_SUCCESSFUL))
+ emit q->setError(systemErrorString(deinitHardwareRes), QCanBusDevice::ConnectionError);
+}
+
+void SystecCanBackendPrivate::eventHandler(QEvent *event)
+{
+ const int code = event->type() - QEvent::User;
+
+ if (code == USBCAN_EVENT_RECEIVE)
+ readAllReceivedMessages();
+}
+
+bool SystecCanBackendPrivate::setConfigurationParameter(int key, const QVariant &value)
+{
+ Q_Q(SystecCanBackend);
+
+ switch (key) {
+ case QCanBusDevice::BitRateKey:
+ return verifyBitRate(value.toInt());
+ case QCanBusDevice::ReceiveOwnKey:
+ if (Q_UNLIKELY(q->state() != QCanBusDevice::UnconnectedState)) {
+ q->setError(SystecCanBackend::tr("Cannot configure TxEcho for open device"),
+ QCanBusDevice::ConfigurationError);
+ return false;
+ }
+ return true;
+ default:
+ q->setError(SystecCanBackend::tr("Unsupported configuration key: %1").arg(key),
+ QCanBusDevice::ConfigurationError);
+ return false;
+ }
+}
+
+bool SystecCanBackendPrivate::setupChannel(const QString &interfaceName)
+{
+ Q_Q(SystecCanBackend);
+
+ const QRegularExpression re(QStringLiteral("can(\\d)\\.(\\d)"));
+ const QRegularExpressionMatch match = re.match(interfaceName);
+
+ if (Q_LIKELY(match.hasMatch())) {
+ device = match.captured(1).toInt();
+ channel = match.captured(2).toInt();
+ } else {
+ q->setError(SystecCanBackend::tr("Invalid interface '%1'.")
+ .arg(interfaceName), QCanBusDevice::ConnectionError);
+ return false;
+ }
+
+ return true;
+}
+
+void SystecCanBackendPrivate::setupDefaultConfigurations()
+{
+ Q_Q(SystecCanBackend);
+
+ q->setConfigurationParameter(QCanBusDevice::BitRateKey, 500000);
+ q->setConfigurationParameter(QCanBusDevice::ReceiveOwnKey, false);
+}
+
+QString SystecCanBackendPrivate::systemErrorString(int errorCode)
+{
+ switch (errorCode) {
+ case USBCAN_ERR_RESOURCE:
+ return SystecCanBackend::tr("Could not create a resource (memory, handle, ...)");
+ case USBCAN_ERR_MAXMODULES:
+ return SystecCanBackend::tr("The maximum number of open modules is exceeded");
+ case USBCAN_ERR_HWINUSE:
+ return SystecCanBackend::tr("The module is already in use");
+ case USBCAN_ERR_ILLVERSION:
+ return SystecCanBackend::tr("The software versions of the module and library are incompatible");
+ case USBCAN_ERR_ILLHW:
+ return SystecCanBackend::tr("The module with the corresponding device number is not connected");
+ case USBCAN_ERR_ILLHANDLE:
+ return SystecCanBackend::tr("Wrong USB-CAN-Handle handed over to the function");
+ case USBCAN_ERR_ILLPARAM:
+ return SystecCanBackend::tr("Wrong parameter handed over to the function");
+ case USBCAN_ERR_BUSY:
+ return SystecCanBackend::tr("Instruction can not be processed at this time");
+ case USBCAN_ERR_TIMEOUT:
+ return SystecCanBackend::tr("No answer from the module");
+ case USBCAN_ERR_IOFAILED:
+ return SystecCanBackend::tr("A request for the driver failed");
+ case USBCAN_ERR_DLL_TXFULL:
+ return SystecCanBackend::tr("The message did not fit into the transmission queue");
+ case USBCAN_ERR_MAXINSTANCES:
+ return SystecCanBackend::tr("Maximum number of applications is reached");
+ case USBCAN_ERR_CANNOTINIT:
+ return SystecCanBackend::tr("CAN-interface is not yet initialized");
+ case USBCAN_ERR_DISCONNECT:
+ return SystecCanBackend::tr("USB-CANmodul was disconnected");
+ case USBCAN_ERR_NOHWCLASS:
+ return SystecCanBackend::tr("The needed device class does not exist");
+ case USBCAN_ERR_ILLCHANNEL:
+ return SystecCanBackend::tr("Illegal CAN channel for GW-001/GW-002");
+ case USBCAN_ERR_ILLHWTYPE:
+ return SystecCanBackend::tr("The API function can not be used with this hardware");
+ case USBCAN_ERR_SERVER_TIMEOUT:
+ return SystecCanBackend::tr("The command server did not send a reply to a command");
+ default:
+ return SystecCanBackend::tr("Unknown error code '%1'.").arg(errorCode);
+ }
+}
+
+void SystecCanBackendPrivate::enableWriteNotification(bool enable)
+{
+ Q_Q(SystecCanBackend);
+
+ if (outgoingEventNotifier) {
+ if (enable) {
+ if (!outgoingEventNotifier->isActive())
+ outgoingEventNotifier->start();
+ } else {
+ outgoingEventNotifier->stop();
+ }
+ } else if (enable) {
+ outgoingEventNotifier = new OutgoingEventNotifier(this, q);
+ outgoingEventNotifier->start(0);
+ }
+}
+
+void SystecCanBackendPrivate::startWrite()
+{
+ Q_Q(SystecCanBackend);
+
+ if (!q->hasOutgoingFrames()) {
+ enableWriteNotification(false);
+ return;
+ }
+
+ const QCanBusFrame frame = q->dequeueOutgoingFrame();
+ const QByteArray payload = frame.payload();
+
+ tCanMsgStruct message;
+ ::memset(&message, 0, sizeof(message));
+
+ message.m_dwID = frame.frameId();
+ message.m_bDLC = payload.size();
+
+ message.m_bFF = frame.hasExtendedFrameFormat() ? USBCAN_MSG_FF_EXT : USBCAN_MSG_FF_STD;
+
+ if (frame.frameType() == QCanBusFrame::RemoteRequestFrame)
+ message.m_bFF |= USBCAN_MSG_FF_RTR; // remote request frame without payload
+ else
+ ::memcpy(message.m_bData, payload.constData(), sizeof(message.m_bData));
+
+ const UCANRET result = ::UcanWriteCanMsgEx(handle, channel, &message, nullptr);
+ if (Q_UNLIKELY(result != USBCAN_SUCCESSFUL))
+ q->setError(systemErrorString(result), QCanBusDevice::WriteError);
+ else
+ emit q->framesWritten(qint64(1));
+
+ if (q->hasOutgoingFrames())
+ enableWriteNotification(true);
+}
+
+void SystecCanBackendPrivate::readAllReceivedMessages()
+{
+ Q_Q(SystecCanBackend);
+
+ QVector<QCanBusFrame> newFrames;
+
+ for (;;) {
+ tCanMsgStruct message;
+ ::memset(&message, 0, sizeof(message));
+
+ const UCANRET result = ::UcanReadCanMsgEx(handle, &channel, &message, nullptr);
+ if (result == USBCAN_WARN_NODATA)
+ break;
+
+ if (Q_UNLIKELY(result != USBCAN_SUCCESSFUL)) {
+ // handle errors
+
+ q->setError(systemErrorString(result), QCanBusDevice::ReadError);
+ break;
+ }
+
+ QCanBusFrame frame(message.m_dwID,
+ QByteArray(reinterpret_cast<const char *>(message.m_bData),
+ int(message.m_bDLC)));
+
+ // TODO: Timestamp can also be set to 100 us resolution with kUcanModeHighResTimer
+ frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(message.m_dwTime * 1000));
+ frame.setExtendedFrameFormat(message.m_bFF & USBCAN_MSG_FF_EXT);
+ frame.setFrameType((message.m_bFF & USBCAN_MSG_FF_RTR)
+ ? QCanBusFrame::RemoteRequestFrame
+ : QCanBusFrame::DataFrame);
+
+ newFrames.append(frame);
+ }
+
+ q->enqueueReceivedFrames(newFrames);
+}
+
+bool SystecCanBackendPrivate::verifyBitRate(int bitrate)
+{
+ Q_Q(SystecCanBackend);
+
+ if (Q_UNLIKELY(q->state() != QCanBusDevice::UnconnectedState)) {
+ q->setError(SystecCanBackend::tr("Cannot configure bitrate for open device"),
+ QCanBusDevice::ConfigurationError);
+ return false;
+ }
+
+ if (Q_UNLIKELY(bitrateCodeFromBitrate(bitrate) == 0)) {
+ q->setError(SystecCanBackend::tr("Unsupported bitrate %1.").arg(bitrate),
+ QCanBusDevice::ConfigurationError);
+ return false;
+ }
+
+ return true;
+}
+
+SystecCanBackend::SystecCanBackend(const QString &name, QObject *parent) :
+ QCanBusDevice(parent),
+ d_ptr(new SystecCanBackendPrivate(this))
+{
+ Q_D(SystecCanBackend);
+
+ d->setupChannel(name);
+ d->setupDefaultConfigurations();
+}
+
+SystecCanBackend::~SystecCanBackend()
+{
+ if (state() == QCanBusDevice::ConnectedState)
+ close();
+
+ delete d_ptr;
+}
+
+bool SystecCanBackend::open()
+{
+ Q_D(SystecCanBackend);
+
+ if (!d->open())
+ return false;
+
+ // Apply all stored configurations except bitrate, because
+ // the bitrate can not be applied after opening the device
+ const QVector<int> keys = configurationKeys();
+ for (int key : keys) {
+ if (key == QCanBusDevice::BitRateKey)
+ continue;
+ const QVariant param = configurationParameter(key);
+ const bool success = d->setConfigurationParameter(key, param);
+ if (Q_UNLIKELY(!success)) {
+ qWarning("Cannot apply parameter %d with value %ls.",
+ key, qUtf16Printable(param.toString()));
+ }
+ }
+
+ setState(QCanBusDevice::ConnectedState);
+ return true;
+}
+
+void SystecCanBackend::close()
+{
+ Q_D(SystecCanBackend);
+
+ d->close();
+
+ setState(QCanBusDevice::UnconnectedState);
+}
+
+void SystecCanBackend::setConfigurationParameter(int key, const QVariant &value)
+{
+ Q_D(SystecCanBackend);
+
+ if (d->setConfigurationParameter(key, value))
+ QCanBusDevice::setConfigurationParameter(key, value);
+}
+
+bool SystecCanBackend::writeFrame(const QCanBusFrame &newData)
+{
+ Q_D(SystecCanBackend);
+
+ if (Q_UNLIKELY(state() != QCanBusDevice::ConnectedState))
+ return false;
+
+ if (Q_UNLIKELY(!newData.isValid())) {
+ setError(tr("Cannot write invalid QCanBusFrame"), QCanBusDevice::WriteError);
+ return false;
+ }
+
+ const QCanBusFrame::FrameType type = newData.frameType();
+ if (Q_UNLIKELY(type != QCanBusFrame::DataFrame && type != QCanBusFrame::RemoteRequestFrame)) {
+ setError(tr("Unable to write a frame with unacceptable type"),
+ QCanBusDevice::WriteError);
+ return false;
+ }
+
+ // CAN FD frame format is not supported by the hardware yet
+ if (Q_UNLIKELY(newData.payload().size() > 8)) {
+ setError(tr("CAN FD frame format not supported"), QCanBusDevice::WriteError);
+ return false;
+ }
+
+ enqueueOutgoingFrame(newData);
+ d->enableWriteNotification(true);
+
+ return true;
+}
+
+// TODO: Implement me
+QString SystecCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame)
+{
+ Q_UNUSED(errorFrame);
+
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/canbus/systeccan/systeccanbackend.h b/src/plugins/canbus/systeccan/systeccanbackend.h
new file mode 100644
index 0000000..7bf20f8
--- /dev/null
+++ b/src/plugins/canbus/systeccan/systeccanbackend.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYSTECCANBACKEND_H
+#define SYSTECCANBACKEND_H
+
+#include <QtSerialBus/qcanbusframe.h>
+#include <QtSerialBus/qcanbusdevice.h>
+#include <QtSerialBus/qcanbusdeviceinfo.h>
+
+#include <QtCore/qvariant.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class SystecCanBackendPrivate;
+
+class SystecCanBackend : public QCanBusDevice
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(SystecCanBackend)
+ Q_DISABLE_COPY(SystecCanBackend)
+public:
+ explicit SystecCanBackend(const QString &name, QObject *parent = nullptr);
+ ~SystecCanBackend();
+
+ 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 QList<QCanBusDeviceInfo> interfaces();
+ static bool canCreate(QString *errorReason);
+
+private:
+ SystecCanBackendPrivate * const d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // SYSTECCANBACKEND_H
diff --git a/src/plugins/canbus/systeccan/systeccanbackend_p.h b/src/plugins/canbus/systeccan/systeccanbackend_p.h
new file mode 100644
index 0000000..f180102
--- /dev/null
+++ b/src/plugins/canbus/systeccan/systeccanbackend_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYSTECCANBACKEND_P_H
+#define SYSTECCANBACKEND_P_H
+
+#include "systeccanbackend.h"
+#include "systeccan_symbols_p.h"
+
+#if defined(Q_OS_WIN32)
+# include <qt_windows.h>
+#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 QEvent;
+class QSocketNotifier;
+class QWinEventNotifier;
+class QTimer;
+
+class IncomingEventHandler : public QObject
+{
+ // no Q_OBJECT macro!
+public:
+ explicit IncomingEventHandler(SystecCanBackendPrivate *systecPrivate, QObject *parent) :
+ QObject(parent),
+ dptr(systecPrivate) { }
+
+ void customEvent(QEvent *event);
+
+private:
+ SystecCanBackendPrivate *dptr;
+};
+
+class SystecCanBackendPrivate
+{
+ Q_DECLARE_PUBLIC(SystecCanBackend)
+
+public:
+ SystecCanBackendPrivate(SystecCanBackend *q);
+
+ bool open();
+ void close();
+ void eventHandler(QEvent *event);
+ bool setConfigurationParameter(int key, const QVariant &value);
+ bool setupChannel(const QString &interfaceName);
+ void setupDefaultConfigurations();
+ QString systemErrorString(int errorCode);
+ void enableWriteNotification(bool enable);
+ void startWrite();
+ void readAllReceivedMessages();
+ bool verifyBitRate(int bitrate);
+
+ SystecCanBackend * const q_ptr;
+
+ tUcanHandle handle = 0;
+ quint8 device = 255;
+ quint8 channel = 255;
+
+ QTimer *outgoingEventNotifier = nullptr;
+ IncomingEventHandler *incomingEventHandler = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // SYSTECCANBACKEND_P_H
diff --git a/src/plugins/canbus/tinycan/main.cpp b/src/plugins/canbus/tinycan/main.cpp
index 108818b..c73d81e 100644
--- a/src/plugins/canbus/tinycan/main.cpp
+++ b/src/plugins/canbus/tinycan/main.cpp
@@ -43,14 +43,19 @@
QT_BEGIN_NAMESPACE
-class TinyCanBusPlugin : public QObject, public QCanBusFactory
+class TinyCanBusPlugin : public QObject, public QCanBusFactoryV2
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QCanBusFactory" FILE "plugin.json")
- Q_INTERFACES(QCanBusFactory)
-
+ Q_INTERFACES(QCanBusFactoryV2)
public:
+ QList<QCanBusDeviceInfo> availableDevices(QString *errorMessage) const override
+ {
+ Q_UNUSED(errorMessage);
+ return TinyCanBackend::interfaces();
+ }
+
QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
{
QString errorReason;
diff --git a/src/plugins/canbus/tinycan/tinycanbackend.cpp b/src/plugins/canbus/tinycan/tinycanbackend.cpp
index 86aa2b8..6405308 100644
--- a/src/plugins/canbus/tinycan/tinycanbackend.cpp
+++ b/src/plugins/canbus/tinycan/tinycanbackend.cpp
@@ -60,7 +60,7 @@ bool TinyCanBackend::canCreate(QString *errorReason)
return true;
#else
static bool symbolsResolved = resolveSymbols(mhstcanLibrary());
- if (!symbolsResolved) {
+ if (Q_UNLIKELY(!symbolsResolved)) {
*errorReason = mhstcanLibrary()->errorString();
return false;
}
@@ -68,6 +68,11 @@ bool TinyCanBackend::canCreate(QString *errorReason)
#endif
}
+QList<QCanBusDeviceInfo> TinyCanBackend::interfaces()
+{
+ return { createDeviceInfo(QStringLiteral("can0.0")), createDeviceInfo(QStringLiteral("can0.1")) };
+}
+
Q_GLOBAL_STATIC(QList<TinyCanBackendPrivate *>, qChannels)
static QMutex channelsGuard(QMutex::NonRecursive);
@@ -104,7 +109,7 @@ static void DRV_CALLBACK_TYPE canRxEventCallback(quint32 index, TCanMsg *frame,
Q_UNUSED(count);
QMutexLocker lock(&channelsGuard);
- foreach (TinyCanBackendPrivate *p, *qChannels()) {
+ for (TinyCanBackendPrivate *p : qAsConst(*qChannels())) {
if (p->channelIndex == int(index)) {
p->startRead();
return;
@@ -114,9 +119,6 @@ static void DRV_CALLBACK_TYPE canRxEventCallback(quint32 index, TCanMsg *frame,
TinyCanBackendPrivate::TinyCanBackendPrivate(TinyCanBackend *q)
: q_ptr(q)
- , isOpen(false)
- , channelIndex(INDEX_INVALID)
- , writeNotifier(nullptr)
{
startupDriver();
@@ -174,7 +176,7 @@ bool TinyCanBackendPrivate::open()
{
char options[] = "AutoConnect=1;AutoReopen=0";
const int ret = ::CanSetOptions(options);
- if (ret < 0) {
+ if (Q_UNLIKELY(ret < 0)) {
q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ConnectionError);
return false;
}
@@ -182,7 +184,7 @@ bool TinyCanBackendPrivate::open()
{
const int ret = ::CanDeviceOpen(channelIndex, nullptr);
- if (ret < 0) {
+ if (Q_UNLIKELY(ret < 0)) {
q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ConnectionError);
return false;
}
@@ -190,7 +192,7 @@ bool TinyCanBackendPrivate::open()
{
const int ret = ::CanSetMode(channelIndex, OP_CAN_START, CAN_CMD_ALL_CLEAR);
- if (ret < 0) {
+ if (Q_UNLIKELY(ret < 0)) {
q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ConnectionError);
::CanDeviceClose(channelIndex);
return false;
@@ -212,7 +214,7 @@ void TinyCanBackendPrivate::close()
writeNotifier = nullptr;
const int ret = ::CanDeviceClose(channelIndex);
- if (ret < 0)
+ if (Q_UNLIKELY(ret < 0))
q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ConnectionError);
isOpen = false;
@@ -341,8 +343,8 @@ void TinyCanBackendPrivate::startWrite()
TCanMsg message;
::memset(&message, 0, sizeof(message));
- if (payload.size() > int(sizeof(message.Data.Bytes))) {
- qWarning("Impossible to write the message with unacceptable data size %d, ignored", payload.size());
+ if (Q_UNLIKELY(payload.size() > int(sizeof(message.Data.Bytes)))) {
+ qWarning("Can not write frame with payload size %d, ignored", payload.size());
} else {
message.Id = frame.frameId();
message.Flags.Flag.Len = payload.size();
@@ -354,7 +356,7 @@ void TinyCanBackendPrivate::startWrite()
const qint32 messagesToWrite = 1;
::memcpy(message.Data.Bytes, payload.constData(), sizeof(message.Data.Bytes));
const int ret = ::CanTransmit(channelIndex, &message, messagesToWrite);
- if (ret < 0)
+ if (Q_UNLIKELY(ret < 0))
q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::WriteError);
else
emit q->framesWritten(messagesToWrite);
@@ -371,7 +373,7 @@ void TinyCanBackendPrivate::startRead()
QVector<QCanBusFrame> newFrames;
- forever {
+ for (;;) {
if (!::CanReceiveGetCount(channelIndex))
break;
@@ -380,7 +382,7 @@ void TinyCanBackendPrivate::startRead()
const int messagesToRead = 1;
const int ret = ::CanReceive(channelIndex, &message, messagesToRead);
- if (ret < 0) {
+ if (Q_UNLIKELY(ret < 0)) {
q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ReadError);
TDeviceStatus status;
@@ -423,7 +425,7 @@ void TinyCanBackendPrivate::startupDriver()
if (driverRefCount == 0) {
const int ret = ::CanInitDriver(nullptr);
- if (ret < 0) {
+ if (Q_UNLIKELY(ret < 0)) {
q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ConnectionError);
return;
}
@@ -431,7 +433,7 @@ void TinyCanBackendPrivate::startupDriver()
::CanSetRxEventCallback(&canRxEventCallback);
::CanSetEvents(EVENT_ENABLE_RX_MESSAGES);
- } else if (driverRefCount < 0) {
+ } else if (Q_UNLIKELY(driverRefCount < 0)) {
qCritical("Wrong reference counter: %d", driverRefCount);
return;
}
@@ -443,7 +445,7 @@ void TinyCanBackendPrivate::cleanupDriver()
{
--driverRefCount;
- if (driverRefCount < 0) {
+ if (Q_UNLIKELY(driverRefCount < 0)) {
qCritical("Wrong reference counter: %d", driverRefCount);
driverRefCount = 0;
} else if (driverRefCount == 0) {
@@ -457,7 +459,7 @@ bool TinyCanBackendPrivate::setBitRate(int bitrate)
Q_Q(TinyCanBackend);
const int bitrateCode = bitrateCodeFromBitrate(bitrate);
- if (bitrateCode == -1) {
+ if (Q_UNLIKELY(bitrateCode == -1)) {
q->setError(TinyCanBackend::tr("Unsupported bitrate value"),
QCanBusDevice::ConfigurationError);
return false;
@@ -465,7 +467,7 @@ bool TinyCanBackendPrivate::setBitRate(int bitrate)
if (isOpen) {
const int ret = ::CanSetSpeed(channelIndex, bitrateCode);
- if (ret < 0) {
+ if (Q_UNLIKELY(ret < 0)) {
q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ConfigurationError);
return false;
}
@@ -501,12 +503,13 @@ bool TinyCanBackend::open()
}
// apply all stored configurations
- foreach (int key, configurationKeys()) {
+ 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;
+ if (Q_UNLIKELY(!success)) {
+ qWarning("Cannot apply parameter: %d with value: %ls",
+ key, qUtf16Printable(param.toString()));
}
}
}
@@ -536,24 +539,24 @@ bool TinyCanBackend::writeFrame(const QCanBusFrame &newData)
{
Q_D(TinyCanBackend);
- if (state() != QCanBusDevice::ConnectedState)
+ if (Q_UNLIKELY(state() != QCanBusDevice::ConnectedState))
return false;
- if (!newData.isValid()) {
+ if (Q_UNLIKELY(!newData.isValid())) {
setError(tr("Cannot write invalid QCanBusFrame"), QCanBusDevice::WriteError);
return false;
}
- if (newData.frameType() != QCanBusFrame::DataFrame
+ if (Q_UNLIKELY(newData.frameType() != QCanBusFrame::DataFrame
&& newData.frameType() != QCanBusFrame::RemoteRequestFrame
- && newData.frameType() != QCanBusFrame::ErrorFrame) {
+ && newData.frameType() != QCanBusFrame::ErrorFrame)) {
setError(tr("Unable to write a frame with unacceptable type"),
QCanBusDevice::WriteError);
return false;
}
// CAN FD frame format not supported at this stage
- if (newData.payload().size() > 8) {
+ if (Q_UNLIKELY(newData.payload().size() > 8)) {
setError(tr("CAN FD frame format not supported."), QCanBusDevice::WriteError);
return false;
}
diff --git a/src/plugins/canbus/tinycan/tinycanbackend.h b/src/plugins/canbus/tinycan/tinycanbackend.h
index c263c22..5f504ca 100644
--- a/src/plugins/canbus/tinycan/tinycanbackend.h
+++ b/src/plugins/canbus/tinycan/tinycanbackend.h
@@ -40,6 +40,7 @@
#include <QtSerialBus/qcanbusframe.h>
#include <QtSerialBus/qcanbusdevice.h>
+#include <QtSerialBus/qcanbusdeviceinfo.h>
#include <QtCore/qvariant.h>
#include <QtCore/qvector.h>
@@ -68,6 +69,7 @@ public:
QString interpretErrorFrame(const QCanBusFrame &errorFrame) override;
static bool canCreate(QString *errorReason);
+ static QList<QCanBusDeviceInfo> interfaces();
private:
TinyCanBackendPrivate * const d_ptr;
diff --git a/src/plugins/canbus/tinycan/tinycanbackend_p.h b/src/plugins/canbus/tinycan/tinycanbackend_p.h
index c30b55d..905175c 100644
--- a/src/plugins/canbus/tinycan/tinycanbackend_p.h
+++ b/src/plugins/canbus/tinycan/tinycanbackend_p.h
@@ -39,6 +39,7 @@
#define TINYCANBACKEND_P_H
#include "tinycanbackend.h"
+#include "tinycan_symbols_p.h"
//
// W A R N I N G
@@ -78,9 +79,10 @@ public:
bool setBitRate(int bitrate);
TinyCanBackend * const q_ptr;
- bool isOpen;
- int channelIndex;
- QTimer *writeNotifier;
+
+ bool isOpen = false;
+ int channelIndex = INDEX_INVALID;
+ QTimer *writeNotifier = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/plugins/canbus/vectorcan/main.cpp b/src/plugins/canbus/vectorcan/main.cpp
index c7fada8..07c30bc 100644
--- a/src/plugins/canbus/vectorcan/main.cpp
+++ b/src/plugins/canbus/vectorcan/main.cpp
@@ -42,18 +42,26 @@
QT_BEGIN_NAMESPACE
-class VectorCanBusPlugin : public QObject, public QCanBusFactory
+class VectorCanBusPlugin : public QObject, public QCanBusFactoryV2
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QCanBusFactory" FILE "plugin.json")
- Q_INTERFACES(QCanBusFactory)
+ Q_INTERFACES(QCanBusFactoryV2)
public:
+ QList<QCanBusDeviceInfo> availableDevices(QString *errorMessage) const override
+ {
+ if (Q_UNLIKELY(!VectorCanBackend::canCreate(errorMessage)))
+ return QList<QCanBusDeviceInfo>();
+
+ return VectorCanBackend::interfaces();
+ }
+
QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
{
QString errorReason;
- if (!VectorCanBackend::canCreate(&errorReason)) {
- qWarning("%s", qPrintable(errorReason));
+ if (Q_UNLIKELY(!VectorCanBackend::canCreate(&errorReason))) {
+ qWarning("%ls", qUtf16Printable(errorReason));
if (errorMessage)
*errorMessage = errorReason;
return nullptr;
diff --git a/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h
index a267fa5..7bfb3c6 100644
--- a/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h
+++ b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h
@@ -362,6 +362,9 @@ typedef long XLportHandle, *pXLportHandle;
// defines for special DeviceStatus
#define XL_SPECIAL_DEVICE_STAT_FPGA_UPDATE_DONE 0x01 // automatic driver FPGA flashing done
+#define XL_HWTYPE_NONE 0
+#define XL_HWTYPE_VIRTUAL 1
+
typedef struct s_xl_channel_config {
char name[XL_MAX_LENGTH + 1];
quint8 hwType; // HWTYPE_xxxx (see above)
diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend.cpp b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp
index c91afad..47662a6 100644
--- a/src/plugins/canbus/vectorcan/vectorcanbackend.cpp
+++ b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp
@@ -59,7 +59,7 @@ bool VectorCanBackend::canCreate(QString *errorReason)
return true;
#else
static bool symbolsResolved = resolveSymbols(vectorcanLibrary());
- if (!symbolsResolved) {
+ if (Q_UNLIKELY(!symbolsResolved)) {
*errorReason = vectorcanLibrary()->errorString();
return false;
}
@@ -67,6 +67,32 @@ bool VectorCanBackend::canCreate(QString *errorReason)
#endif
}
+QList<QCanBusDeviceInfo> VectorCanBackend::interfaces()
+{
+ QList<QCanBusDeviceInfo> result;
+
+ if (Q_UNLIKELY(VectorCanBackendPrivate::loadDriver() != XL_SUCCESS))
+ return result;
+
+ XLdriverConfig config;
+ if (Q_UNLIKELY(::xlGetDriverConfig(&config) != XL_SUCCESS)) {
+ VectorCanBackendPrivate::cleanupDriver();
+ return result;
+ }
+
+ for (uint i = 0; i < config.channelCount; ++i) {
+ if (config.channel[i].hwType == XL_HWTYPE_NONE)
+ continue;
+
+ const bool isVirtual = config.channel[i].hwType == XL_HWTYPE_VIRTUAL;
+ const bool isFd = config.channel[i].channelCapabilities & XL_CHANNEL_FLAG_CANFD_SUPPORT;
+ result.append(createDeviceInfo(QStringLiteral("can") + QString::number(i), isVirtual, isFd));
+ }
+
+ VectorCanBackendPrivate::cleanupDriver();
+ return result;
+}
+
static int driverRefCount = 0;
class ReadNotifier : public QWinEventNotifier
@@ -122,7 +148,6 @@ private:
VectorCanBackendPrivate::VectorCanBackendPrivate(VectorCanBackend *q)
: q_ptr(q)
- , portHandle(XL_INVALID_PORTHANDLE)
{
startupDriver();
}
@@ -144,7 +169,7 @@ bool VectorCanBackendPrivate::open()
channelMask, &permissionMask, queueSize,
XL_INTERFACE_VERSION, XL_BUS_TYPE_CAN);
- if (status != XL_SUCCESS || portHandle == XL_INVALID_PORTHANDLE) {
+ if (Q_UNLIKELY(status != XL_SUCCESS || portHandle == XL_INVALID_PORTHANDLE)) {
q->setError(systemErrorString(status),
QCanBusDevice::ConnectionError);
return false;
@@ -154,7 +179,7 @@ bool VectorCanBackendPrivate::open()
{
const XLstatus status = ::xlActivateChannel(portHandle, channelMask,
XL_BUS_TYPE_CAN, XL_ACTIVATE_RESET_CLOCK);
- if (status != XL_SUCCESS) {
+ if (Q_UNLIKELY(status != XL_SUCCESS)) {
q->setError(systemErrorString(status),
QCanBusDevice::CanBusError::ConnectionError);
return false;
@@ -164,7 +189,7 @@ bool VectorCanBackendPrivate::open()
{
const int queueLevel = 1;
const XLstatus status = ::xlSetNotification(portHandle, &readHandle, queueLevel);
- if (status != XL_SUCCESS) {
+ if (Q_UNLIKELY(status != XL_SUCCESS)) {
q->setError(systemErrorString(status),
QCanBusDevice::ConnectionError);
return false;
@@ -191,7 +216,7 @@ void VectorCanBackendPrivate::close()
{
const XLstatus status = ::xlDeactivateChannel(portHandle, channelMask);
- if (status != XL_SUCCESS) {
+ if (Q_UNLIKELY(status != XL_SUCCESS)) {
q->setError(systemErrorString(status),
QCanBusDevice::CanBusError::ConfigurationError);
}
@@ -199,7 +224,7 @@ void VectorCanBackendPrivate::close()
{
const XLstatus status = ::xlClosePort(portHandle);
- if (status != XL_SUCCESS) {
+ if (Q_UNLIKELY(status != XL_SUCCESS)) {
q->setError(systemErrorString(status),
QCanBusDevice::ConnectionError);
}
@@ -215,6 +240,9 @@ bool VectorCanBackendPrivate::setConfigurationParameter(int key, const QVariant
switch (key) {
case QCanBusDevice::BitRateKey:
return setBitRate(value.toUInt());
+ case QCanBusDevice::ReceiveOwnKey:
+ transmitEcho = value.toBool();
+ return true;
default:
q->setError(VectorCanBackend::tr("Unsupported configuration key"),
QCanBusDevice::ConfigurationError);
@@ -224,7 +252,7 @@ bool VectorCanBackendPrivate::setConfigurationParameter(int key, const QVariant
void VectorCanBackendPrivate::setupChannel(const QString &interfaceName)
{
- if (interfaceName.startsWith(QStringLiteral("can"))) {
+ if (Q_LIKELY(interfaceName.startsWith(QStringLiteral("can")))) {
const QStringRef ref = interfaceName.midRef(3);
bool ok = false;
const int channelIndex = ref.toInt(&ok);
@@ -234,7 +262,7 @@ void VectorCanBackendPrivate::setupChannel(const QString &interfaceName)
}
}
- qCritical() << "Unable to parse the channel from an interface";
+ qCritical("Unable to parse the channel %ls", qUtf16Printable(interfaceName));
}
void VectorCanBackendPrivate::setupDefaultConfigurations()
@@ -246,7 +274,8 @@ void VectorCanBackendPrivate::setupDefaultConfigurations()
QString VectorCanBackendPrivate::systemErrorString(int errorCode) const
{
- if (const char *string = ::xlGetErrorString(errorCode))
+ const char *string = ::xlGetErrorString(errorCode);
+ if (Q_LIKELY(string))
return QString::fromUtf8(string);
return VectorCanBackend::tr("Unable to retrieve an error string");
}
@@ -286,7 +315,7 @@ void VectorCanBackendPrivate::startWrite()
quint32 eventCount = 1;
const XLstatus status = ::xlCanTransmit(portHandle, channelMask,
&eventCount, &event);
- if (status != XL_SUCCESS) {
+ if (Q_UNLIKELY(status != XL_SUCCESS)) {
q->setError(systemErrorString(status),
QCanBusDevice::WriteError);
} else {
@@ -309,7 +338,7 @@ void VectorCanBackendPrivate::startRead()
::memset(&event, 0, sizeof(event));
const XLstatus status = ::xlReceive(portHandle, &eventCount, &event);
- if (status != XL_SUCCESS) {
+ if (Q_UNLIKELY(status != XL_SUCCESS)) {
if (status != XL_ERR_QUEUE_IS_EMPTY) {
q->setError(systemErrorString(status),
QCanBusDevice::ReadError);
@@ -322,6 +351,9 @@ void VectorCanBackendPrivate::startRead()
const s_xl_can_msg &msg = event.tagData.msg;
+ if ((msg.flags & XL_CAN_MSG_FLAG_TX_COMPLETED) && !transmitEcho)
+ continue;
+
QCanBusFrame frame(msg.id & ~XL_CAN_EXT_MSG_ID,
QByteArray(reinterpret_cast<const char *>(msg.data), int(msg.dlc)));
frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(event.timeStamp / 1000));
@@ -338,30 +370,38 @@ void VectorCanBackendPrivate::startRead()
q->enqueueReceivedFrames(newFrames);
}
-void VectorCanBackendPrivate::startupDriver()
+XLstatus VectorCanBackendPrivate::loadDriver()
{
- 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) {
+ if (Q_UNLIKELY(status != XL_SUCCESS))
+ return status;
+
+ } else if (Q_UNLIKELY(driverRefCount < 0)) {
qCritical("Wrong reference counter: %d", driverRefCount);
- return;
+ return XL_ERR_CANNOT_OPEN_DRIVER;
}
++driverRefCount;
+ return XL_SUCCESS;
+}
+
+void VectorCanBackendPrivate::startupDriver()
+{
+ Q_Q(VectorCanBackend);
+
+ const XLstatus status = loadDriver();
+ if (Q_UNLIKELY(status != XL_SUCCESS)) {
+ q->setError(systemErrorString(status),
+ QCanBusDevice::CanBusError::ConnectionError);
+ }
}
void VectorCanBackendPrivate::cleanupDriver()
{
--driverRefCount;
- if (driverRefCount < 0) {
+ if (Q_UNLIKELY(driverRefCount < 0)) {
qCritical("Wrong reference counter: %d", driverRefCount);
driverRefCount = 0;
} else if (driverRefCount == 0) {
@@ -375,7 +415,7 @@ bool VectorCanBackendPrivate::setBitRate(quint32 bitrate)
if (q->state() != QCanBusDevice::UnconnectedState) {
const XLstatus status = ::xlCanSetChannelBitrate(portHandle, channelMask, bitrate);
- if (status != XL_SUCCESS) {
+ if (Q_UNLIKELY(status != XL_SUCCESS)) {
q->setError(systemErrorString(status),
QCanBusDevice::CanBusError::ConfigurationError);
return false;
@@ -417,8 +457,8 @@ bool VectorCanBackend::open()
const QVariant param = configurationParameter(key);
const bool success = d->setConfigurationParameter(key, param);
if (!success) {
- qWarning() << "Cannot apply parameter:" << key
- << "with value:" << param;
+ qWarning("Cannot apply parameter: %d with value: %ls",
+ key, qUtf16Printable(param.toString()));
}
}
@@ -450,22 +490,22 @@ bool VectorCanBackend::writeFrame(const QCanBusFrame &newData)
if (state() != QCanBusDevice::ConnectedState)
return false;
- if (!newData.isValid()) {
+ if (Q_UNLIKELY(!newData.isValid())) {
setError(tr("Cannot write invalid QCanBusFrame"),
QCanBusDevice::WriteError);
return false;
}
- if (newData.frameType() != QCanBusFrame::DataFrame
+ if (Q_UNLIKELY(newData.frameType() != QCanBusFrame::DataFrame
&& newData.frameType() != QCanBusFrame::RemoteRequestFrame
- && newData.frameType() != QCanBusFrame::ErrorFrame) {
+ && 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) {
+ if (Q_UNLIKELY(newData.payload().size() > MAX_MSG_LEN)) {
setError(tr("CAN FD frame format not supported."),
QCanBusDevice::WriteError);
return false;
diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend.h b/src/plugins/canbus/vectorcan/vectorcanbackend.h
index e6f6f43..165d369 100644
--- a/src/plugins/canbus/vectorcan/vectorcanbackend.h
+++ b/src/plugins/canbus/vectorcan/vectorcanbackend.h
@@ -39,6 +39,7 @@
#include <QtSerialBus/qcanbusframe.h>
#include <QtSerialBus/qcanbusdevice.h>
+#include <QtSerialBus/qcanbusdeviceinfo.h>
#include <QtCore/qvariant.h>
#include <QtCore/qvector.h>
@@ -67,6 +68,7 @@ public:
QString interpretErrorFrame(const QCanBusFrame &errorFrame) override;
static bool canCreate(QString *errorReason);
+ static QList<QCanBusDeviceInfo> interfaces();
private:
VectorCanBackendPrivate * const d_ptr;
diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend_p.h b/src/plugins/canbus/vectorcan/vectorcanbackend_p.h
index ede7223..3eafb0c 100644
--- a/src/plugins/canbus/vectorcan/vectorcanbackend_p.h
+++ b/src/plugins/canbus/vectorcan/vectorcanbackend_p.h
@@ -37,6 +37,7 @@
#ifndef VECTORCANBACKEND_P_H
#define VECTORCANBACKEND_P_H
+#include "vectorcan_symbols_p.h"
#include "vectorcanbackend.h"
#if defined(Q_OS_WIN32)
@@ -76,13 +77,15 @@ public:
QString systemErrorString(int errorCode) const;
void startWrite();
void startRead();
+ static XLstatus loadDriver();
void startupDriver();
- void cleanupDriver();
+ static void cleanupDriver();
bool setBitRate(quint32 bitrate);
VectorCanBackend * const q_ptr;
- long portHandle;
+ bool transmitEcho = false;
+ long portHandle = XL_INVALID_PORTHANDLE;
quint64 channelMask = 0;
HANDLE readHandle = INVALID_HANDLE_VALUE;
QTimer *writeNotifier = nullptr;
diff --git a/src/serialbus/doc/src/qtcanbus-backends.qdoc b/src/serialbus/doc/src/qtcanbus-backends.qdoc
index 239ccc3..c04cdd1 100644
--- a/src/serialbus/doc/src/qtcanbus-backends.qdoc
+++ b/src/serialbus/doc/src/qtcanbus-backends.qdoc
@@ -61,6 +61,10 @@
\li \l {Using SocketCAN Plugin}{SocketCAN} (\c socketcan)
\li CAN bus plugin using Linux sockets and open source drivers.
\row
+ \li SYS TEC electronic
+ \li \l {Using SystecCAN Backend}{SystecCAN} (\c systeccan)
+ \li CAN bus backend using the SYS TEC CAN adapters.
+ \row
\li PEAK-System
\li \l {Using PeakCAN Plugin}{PeakCAN} (\c peakcan)
\li CAN bus plugin using the PEAK CAN adapters.
@@ -91,7 +95,7 @@
\endcode
This key must be passed to \l {QCanBus::createDevice()} together with the interface name of
- the CAN bus adapter. QCanBus loads and instantiates the plugin using the QCanBusFactory
+ the CAN bus adapter. QCanBus loads and instantiates the plugin using the QCanBusFactoryV2
interface which each plugin must implement as central entry point. The interface acts as
a factory and its sole purpose is to return a \l QCanBusDevice instance. The above mentioned
interface name is passed on via the factory's \l QCanBusFactory::createDevice() method.
diff --git a/src/serialbus/doc/src/socketcan.qdoc b/src/serialbus/doc/src/socketcan.qdoc
index 87bbe79..74a8dab 100644
--- a/src/serialbus/doc/src/socketcan.qdoc
+++ b/src/serialbus/doc/src/socketcan.qdoc
@@ -47,10 +47,25 @@
This section assumes, that the device driver is already loaded
(most likely automatically when connecting the CAN hardware).
+ \section3 Default settings
+
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
+ sudo ip link set up can0 type can bitrate 250000
+ \endcode
+
+ To automatically recover from "bus off" errors after 100 milliseconds,
+ the following command can be used:
+ \code
+ sudo ip link set up can0 type can bitrate 250000 restart-ms 100
+ \endcode
+
+ \section3 CAN FD settings
+
+ To set the device can0 to an arbitration bitrate of 500 kBit/s and a data
+ bitrate of 4 MBit/s (for frames with bitrate switch flag):
+ \code
+ sudo ip link set can0 up type can bitrate 500000 dbitrate 4000000 fd on
\endcode
\section2 Setting up a virtual CAN bus
@@ -61,18 +76,17 @@
\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
+ sudo ip link set up vcan0 mtu 72
\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
+ # Display received CAN messages with absolute timestamps and flags
+ candump -ta -x vcan0
- # Send a CAN message
+ # Send a CAN FD message with flags BRS and EFI set
cansend vcan0 123##3112233445566778899aabbccddeeff
# Generate random CAN messages
@@ -159,6 +173,11 @@
\li This configuration option determines whether CANFD frames may be sent or received.
By default, this option is disabled. It controls the CAN_RAW_FD_FRAMES
option of the CAN socket.
+ \row
+ \li QCanBusDevice::DataBitRateKey
+ \li This configuration is not supported by the socketcan plugin. However
+ it is possible to set the data rate when configuring the CAN network interface
+ using the \c {ip link} command.
\endtable
For example:
diff --git a/src/serialbus/doc/src/systeccan.qdoc b/src/serialbus/doc/src/systeccan.qdoc
new file mode 100644
index 0000000..08ecfce
--- /dev/null
+++ b/src/serialbus/doc/src/systeccan.qdoc
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Andre Hartmann <aha_1980@gmx.de>
+** 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-SystecCAN-overview.html
+ \title Using SystecCAN Backend
+
+ \brief Overview of how to use the SystecCAN backend.
+
+ The SystecCAN backend encapsulates the low-level API to work with the
+ \l{http://www.systec-electronic.com/}{SYS TEC} CAN adapters.
+
+ \section1 Creating CAN Bus Devices
+
+ At first it is necessary to check that QCanBus provides the desired backend:
+
+ \code
+ if (QCanBus::instance()->plugins().contains(QStringLiteral("systeccan"))) {
+ // backend available
+ }
+ \endcode
+
+ Where \e systeccan is the backend name.
+
+ Next, a connection to a specific interface can be established:
+
+ \code
+ QCanBusDevice *device = QCanBus::instance()->createDevice(
+ QStringLiteral("systeccan"), QStringLiteral("can0.0"));
+ device->connectDevice();
+ \endcode
+
+ Where, \e can0.0 is the active CAN interface name (interface 0, channel 0).
+ The SystecCAN backend supports 64 USB interfaces (so called modules) from
+ \e can0.0 to \e can63.1. Each module can have one or two channels, they can
+ be accessed by the index canX.0 or canX.1.
+
+ \note SYS TEC also provides 8 or 16 channel CAN interfaces. These units
+ consist of an USB hub and multiple two-channel modules internally.
+
+ 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
+
+ SystecCAN 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. The following bit rates
+ are supported: 10000, 20000, 50000, 100000, 125000, 250000, 500000, 800000,
+ and 1000000. Note that this configuration parameter can only be adjusted
+ while the QCanBusDevice is not connected.
+ \row
+ \li QCanBusDevice::ReceiveOwnKey
+ \li The reception of CAN frames on the same channel that was sending the CAN frame
+ is disabled by default.
+ \endtable
+*/
diff --git a/src/serialbus/doc/src/vectorcan.qdoc b/src/serialbus/doc/src/vectorcan.qdoc
index 8163207..f95f956 100644
--- a/src/serialbus/doc/src/vectorcan.qdoc
+++ b/src/serialbus/doc/src/vectorcan.qdoc
@@ -88,5 +88,11 @@
\row
\li QCanBusDevice::BitRateKey
\li Determines the bit rate of the CAN bus connection.
+ \row
+ \li QCanBusDevice::ReceiveOwnKey
+ \li The reception of the CAN frames on the same device that was sending the CAN frame
+ 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.
\endtable
*/
diff --git a/src/serialbus/qcanbus.cpp b/src/serialbus/qcanbus.cpp
index f143779..6b74bd7 100644
--- a/src/serialbus/qcanbus.cpp
+++ b/src/serialbus/qcanbus.cpp
@@ -37,12 +37,11 @@
#include "qcanbus.h"
#include "qcanbusfactory.h"
-#include <QtCore/qobject.h>
-#include <QtCore/qpluginloader.h>
-#include <QtCore/qdebug.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qglobalstatic.h>
#include <QtCore/qlist.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qpluginloader.h>
#include <private/qfactoryloader_p.h>
@@ -50,14 +49,15 @@
QT_BEGIN_NAMESPACE
-struct QCanBusPrivate
+class QCanBusPrivate
{
public:
- QCanBusPrivate() : factory(nullptr), index(-1) { }
+ QCanBusPrivate() { }
+ QCanBusPrivate(int index, const QJsonObject &meta) : meta(meta), index(index) {}
QJsonObject meta;
- QCanBusFactory *factory;
- int index;
+ QObject *factory = nullptr;
+ int index = -1;
};
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qFactoryLoader,
@@ -72,14 +72,11 @@ static void loadPlugins()
{
const QList<QJsonObject> meta = qFactoryLoader()->metaData();
for (int i = 0; i < meta.count(); i++) {
- const QJsonObject obj = meta.at(i).value(QStringLiteral("MetaData")).toObject();
+ const QJsonObject obj = meta.at(i).value(QLatin1String("MetaData")).toObject();
if (obj.isEmpty())
continue;
- QCanBusPrivate d;
- d.index = i;
- d.meta = obj;
- qCanBusPlugins()->insert(obj.value(QStringLiteral("Key")).toString(), d);
+ qCanBusPlugins()->insert(obj.value(QLatin1String("Key")).toString(), {i, obj});
}
}
@@ -121,6 +118,57 @@ static void setErrorMessage(QString *result, const QString &message)
*result = message;
}
+static QObject *canBusFactory(const QString &plugin, QString *errorMessage)
+{
+ if (Q_UNLIKELY(!qCanBusPlugins()->contains(plugin))) {
+ setErrorMessage(errorMessage, QCanBus::tr("No such plugin: '%1'").arg(plugin));
+ return nullptr;
+ }
+
+ QCanBusPrivate d = qCanBusPlugins()->value(plugin);
+ if (!d.factory) {
+ d.factory = qFactoryLoader->instance(d.index);
+
+ if (d.factory)
+ qCanBusPlugins()->insert(plugin, d);
+ }
+
+ if (Q_UNLIKELY(!d.factory))
+ setErrorMessage(errorMessage, QCanBus::tr("No factory for plugin: '%1'").arg(plugin));
+
+ return d.factory;
+}
+
+/*!
+ \since 5.9
+
+ Returns the available interfaces for \a plugin. In case of failure, the optional
+ parameter \a errorMessage returns a textual error description.
+
+ \note Some plugins might not or only partially support this function.
+
+ \sa createDevice()
+*/
+QList<QCanBusDeviceInfo> QCanBus::availableDevices(const QString &plugin, QString *errorMessage) const
+{
+ const QObject *obj = canBusFactory(plugin, errorMessage);
+ if (Q_UNLIKELY(!obj))
+ return QList<QCanBusDeviceInfo>();
+
+ const QCanBusFactoryV2 *factoryV2 = qobject_cast<QCanBusFactoryV2 *>(obj);
+ if (Q_UNLIKELY(!factoryV2)) {
+ setErrorMessage(errorMessage,
+ tr("The plugin '%1' does not provide this function.").arg(plugin));
+ return QList<QCanBusDeviceInfo>();
+ }
+
+ QString errorString;
+ QList<QCanBusDeviceInfo> result = factoryV2->availableDevices(&errorString);
+
+ setErrorMessage(errorMessage, errorString);
+ return result;
+}
+
/*!
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. In case of failure, the optional
@@ -138,30 +186,29 @@ static void setErrorMessage(QString *result, const QString &message)
\endcode
\note The \a interfaceName is plugin-dependent. See the corresponding plugin documentation
- for more information: \l {CAN Bus Plugins}.
+ for more information: \l {CAN Bus Plugins}. To get a list of available interfaces,
+ \l availableDevices() can be used.
+
+ \sa availableDevices()
*/
QCanBusDevice *QCanBus::createDevice(const QString &plugin, const QString &interfaceName,
QString *errorMessage) const
{
- if (!qCanBusPlugins()->contains(plugin)) {
- setErrorMessage(errorMessage, tr("No such plugin: '%1'").arg(plugin));
+ const QObject *obj = canBusFactory(plugin, errorMessage);
+ if (Q_UNLIKELY(!obj))
return nullptr;
- }
- QCanBusPrivate d = qCanBusPlugins()->value(plugin);
- if (!d.factory) {
- d.factory
- = qobject_cast<QCanBusFactory *>(qFactoryLoader->instance(d.index));
+ const QCanBusFactoryV2 *factoryV2 = qobject_cast<QCanBusFactoryV2 *>(obj);
+ if (Q_LIKELY(factoryV2))
+ return factoryV2->createDevice(interfaceName, errorMessage);
- if (d.factory)
- qCanBusPlugins()->insert(plugin, d);
- }
- if (!d.factory) {
- setErrorMessage(errorMessage, tr("No factory for plugin: '%1'").arg(plugin));
- return nullptr;
- }
+ const QCanBusFactory *factory = qobject_cast<QCanBusFactory *>(obj);
+ if (factory)
+ return factory->createDevice(interfaceName, errorMessage);
- return d.factory->createDevice(interfaceName, errorMessage);
+ setErrorMessage(errorMessage,
+ tr("The plugin '%1' does not provide this function.").arg(plugin));
+ return nullptr;
}
QCanBus::QCanBus(QObject *parent) :
diff --git a/src/serialbus/qcanbus.h b/src/serialbus/qcanbus.h
index 273b9c1..695d312 100644
--- a/src/serialbus/qcanbus.h
+++ b/src/serialbus/qcanbus.h
@@ -40,11 +40,10 @@
#include <QtCore/qobject.h>
#include <QtSerialBus/qserialbusglobal.h>
#include <QtSerialBus/qcanbusdevice.h>
+#include <QtSerialBus/qcanbusdeviceinfo.h>
QT_BEGIN_NAMESPACE
-class QSerialBusBackendFactory;
-
class Q_SERIALBUS_EXPORT QCanBus : public QObject
{
Q_OBJECT
@@ -53,6 +52,8 @@ public:
static QCanBus *instance();
QStringList plugins() const;
+ QList<QCanBusDeviceInfo> availableDevices(const QString &plugin, QString *errorMessage = nullptr) const;
+
QCanBusDevice *createDevice(const QString &plugin,
const QString &interfaceName,
QString *errorMessage = nullptr) const;
diff --git a/src/serialbus/qcanbusdevice.cpp b/src/serialbus/qcanbusdevice.cpp
index 77b9914..70778a7 100644
--- a/src/serialbus/qcanbusdevice.cpp
+++ b/src/serialbus/qcanbusdevice.cpp
@@ -36,6 +36,7 @@
#include "qcanbusdevice.h"
#include "qcanbusdevice_p.h"
+#include "qcanbusdeviceinfo_p.h"
#include "qcanbusframe.h"
@@ -101,9 +102,18 @@ QT_BEGIN_NAMESPACE
\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 BitRateKey This key defines the CAN bitrate in bits per second. With CAN FD,
+ the payload can be transmitted at a higher data bitrate,
+ if \l QCanBusFrame::hasBitrateSwitch() is set. In this case,
+ \c QCanBusDevice::BitRateKey is only used for the CAN ID arbitration
+ phase. See also \c QCanBusDevice::DataBitRateKey
\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 DataBitRateKey This key defines the CAN FD payload bitrate in bits per second.
+ CAN FD allows to transmit the payload of frames with
+ \l QCanBusFrame::hasBitrateSwitch() flag at a higher data bitrate,
+ after the arbitration phase at the nominal bitrate is finished.
+ See also \c QCanBusDevice::BitRateKey
\value UserKey This key defines the range where custom keys start. Its most
common purpose is to permit platform-specific configuration
options.
@@ -241,7 +251,7 @@ void QCanBusDevice::enqueueReceivedFrames(const QVector<QCanBusFrame> &newFrames
{
Q_D(QCanBusDevice);
- if (newFrames.isEmpty())
+ if (Q_UNLIKELY(newFrames.isEmpty()))
return;
d->incomingFramesGuard.lock();
@@ -272,7 +282,7 @@ QCanBusFrame QCanBusDevice::dequeueOutgoingFrame()
{
Q_D(QCanBusDevice);
- if (d->outgoingFrames.isEmpty())
+ if (Q_UNLIKELY(d->outgoingFrames.isEmpty()))
return QCanBusFrame(QCanBusFrame::InvalidFrame);
return d->outgoingFrames.takeFirst();
}
@@ -335,7 +345,7 @@ QVariant QCanBusDevice::configurationParameter(int key) const
{
Q_D(const QCanBusDevice);
- foreach (const ConfigEntry &e, d->configOptions) {
+ for (const ConfigEntry &e : d->configOptions) {
if (e.first == key)
return e.second;
}
@@ -355,7 +365,7 @@ QVector<int> QCanBusDevice::configurationKeys() const
Q_D(const QCanBusDevice);
QVector<int> result;
- foreach (const ConfigEntry &e, d->configOptions)
+ for (const ConfigEntry &e : d->configOptions)
result.append(e.first);
return result;
@@ -434,7 +444,7 @@ qint64 QCanBusDevice::framesToWrite() const
bool QCanBusDevice::waitForFramesWritten(int msecs)
{
// do not enter this function recursively
- if (d_func()->waitForWrittenEntered) {
+ if (Q_UNLIKELY(d_func()->waitForWrittenEntered)) {
qWarning("QCanBusDevice::waitForFramesWritten() must not be called "
"recursively. Check that no slot containing waitForFramesReceived() "
"is called in response to framesWritten(qint64) or errorOccurred(CanBusError)"
@@ -487,7 +497,7 @@ bool QCanBusDevice::waitForFramesWritten(int msecs)
bool QCanBusDevice::waitForFramesReceived(int msecs)
{
// do not enter this function recursively
- if (d_func()->waitForReceivedEntered) {
+ if (Q_UNLIKELY(d_func()->waitForReceivedEntered)) {
qWarning("QCanBusDevice::waitForFramesReceived() must not be called "
"recursively. Check that no slot containing waitForFramesReceived() "
"is called in response to framesReceived() or errorOccurred(CanBusError) "
@@ -566,12 +576,12 @@ QCanBusFrame QCanBusDevice::readFrame()
{
Q_D(QCanBusDevice);
- if (d->state != ConnectedState)
+ if (Q_UNLIKELY(d->state != ConnectedState))
return QCanBusFrame(QCanBusFrame::InvalidFrame);
QMutexLocker locker(&d->incomingFramesGuard);
- if (d->incomingFrames.isEmpty())
+ if (Q_UNLIKELY(d->incomingFrames.isEmpty()))
return QCanBusFrame(QCanBusFrame::InvalidFrame);
return d->incomingFrames.takeFirst();
@@ -626,7 +636,7 @@ bool QCanBusDevice::connectDevice()
{
Q_D(QCanBusDevice);
- if (d->state != QCanBusDevice::UnconnectedState) {
+ if (Q_UNLIKELY(d->state != QCanBusDevice::UnconnectedState)) {
setError(tr("Can not connect an already connected device"),
QCanBusDevice::ConnectionError);
return false;
@@ -653,8 +663,8 @@ void QCanBusDevice::disconnectDevice()
{
Q_D(QCanBusDevice);
- if (d->state == QCanBusDevice::UnconnectedState
- || d->state == QCanBusDevice::ClosingState) {
+ if (Q_UNLIKELY(d->state == QCanBusDevice::UnconnectedState
+ || d->state == QCanBusDevice::ClosingState)) {
qWarning("Can not disconnect an unconnected device");
return;
}
@@ -699,4 +709,19 @@ void QCanBusDevice::setState(QCanBusDevice::CanBusDeviceState newState)
emit stateChanged(newState);
}
+/*!
+ * Returns a QCanBusDeviceInfo created from the given parameters \a name,
+ * \a isVirtual, and \a isFlexibleDataRateCapable.
+ * \internal
+ */
+QCanBusDeviceInfo QCanBusDevice::createDeviceInfo(const QString &name, bool isVirtual,
+ bool isFlexibleDataRateCapable)
+{
+ QCanBusDeviceInfoPrivate info;
+ info.name = name;
+ info.isVirtual = isVirtual;
+ info.hasFlexibleDataRate = isFlexibleDataRateCapable;
+ return QCanBusDeviceInfo(info);
+}
+
QT_END_NAMESPACE
diff --git a/src/serialbus/qcanbusdevice.h b/src/serialbus/qcanbusdevice.h
index 22966ef..3a644c6 100644
--- a/src/serialbus/qcanbusdevice.h
+++ b/src/serialbus/qcanbusdevice.h
@@ -39,6 +39,7 @@
#include <QtCore/qobject.h>
#include <QtSerialBus/qcanbusframe.h>
+#include <QtSerialBus/qcanbusdeviceinfo.h>
QT_BEGIN_NAMESPACE
@@ -76,6 +77,7 @@ public:
ReceiveOwnKey,
BitRateKey,
CanFdKey,
+ DataBitRateKey,
UserKey = 30
};
Q_ENUM(ConfigurationKey)
@@ -140,6 +142,10 @@ protected:
// Can be folded into one call to connectDevice() & disconnectDevice()
virtual bool open() = 0;
virtual void close() = 0;
+
+ static QCanBusDeviceInfo createDeviceInfo(const QString &name,
+ bool isVirtual = false,
+ bool isFlexibleDataRateCapable = false);
};
Q_DECLARE_TYPEINFO(QCanBusDevice::CanBusError, Q_PRIMITIVE_TYPE);
diff --git a/src/serialbus/qcanbusdevice_p.h b/src/serialbus/qcanbusdevice_p.h
index c6580c4..faeae65 100644
--- a/src/serialbus/qcanbusdevice_p.h
+++ b/src/serialbus/qcanbusdevice_p.h
@@ -61,14 +61,10 @@ class QCanBusDevicePrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QCanBusDevice)
public:
- QCanBusDevicePrivate()
- : lastError(QCanBusDevice::CanBusError::NoError),
- state(QCanBusDevice::UnconnectedState)
- {
- }
+ QCanBusDevicePrivate() {}
- QCanBusDevice::CanBusError lastError;
- QCanBusDevice::CanBusDeviceState state;
+ QCanBusDevice::CanBusError lastError = QCanBusDevice::CanBusError::NoError;
+ QCanBusDevice::CanBusDeviceState state = QCanBusDevice::UnconnectedState;
QString errorText;
QVector<QCanBusFrame> incomingFrames;
diff --git a/src/serialbus/qcanbusdeviceinfo.cpp b/src/serialbus/qcanbusdeviceinfo.cpp
new file mode 100644
index 0000000..fac1e9f
--- /dev/null
+++ b/src/serialbus/qcanbusdeviceinfo.cpp
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcanbusdeviceinfo.h"
+#include "qcanbusdeviceinfo_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QCanBusDeviceInfo
+ \inmodule QtSerialBus
+ \since 5.9
+
+ \brief The QCanBusDeviceInfo provides information about CAN bus interfaces.
+
+ Each plugin may support one or more interfaces with different
+ capabilities. This class provides information about available functions.
+*/
+
+/*!
+ Constructs an empty QCanBusDeviceInfo.
+*/
+QCanBusDeviceInfo::QCanBusDeviceInfo() :
+ d_ptr(new QCanBusDeviceInfoPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QCanBusDeviceInfo::QCanBusDeviceInfo(const QCanBusDeviceInfo &other) :
+ d_ptr(other.d_ptr)
+{
+}
+
+/*!
+ Constructs a CAN bus device info from QCanBusDeviceInfoPrivate \a dd.
+ \internal
+*/
+QCanBusDeviceInfo::QCanBusDeviceInfo(QCanBusDeviceInfoPrivate &dd) :
+ d_ptr(new QCanBusDeviceInfoPrivate(dd))
+{
+}
+
+/*!
+ Destroys the CAN bus device info.
+*/
+QCanBusDeviceInfo::~QCanBusDeviceInfo()
+{
+}
+
+/*!
+ \fn void QCanBusDeviceInfo::swap(QCanBusDeviceInfo &other)
+ Swaps this CAN bus device info with \a other. This operation is very fast
+ and never fails.
+*/
+
+
+/*!
+ Assigns \a other to this CAN bus device info and returns a reference to this
+ CAN bus device info.
+*/
+QCanBusDeviceInfo &QCanBusDeviceInfo::operator=(const QCanBusDeviceInfo &other)
+{
+ QCanBusDeviceInfo(other).swap(*this);
+ return *this;
+}
+
+/*!
+ Returns the interface name of this CAN bus interface, e.g. can0.
+*/
+QString QCanBusDeviceInfo::name() const
+{
+ return d_ptr->name;
+}
+
+/*!
+ Returns true, if the CAN bus interface is CAN FD (flexible data rate) capable.
+
+ If this information is not available, false is returned.
+*/
+bool QCanBusDeviceInfo::hasFlexibleDataRate() const
+{
+ return d_ptr->hasFlexibleDataRate;
+}
+
+/*!
+ Returns true, if the CAN bus interface is virtual (i.e. not connected to real
+ CAN hardware).
+
+ If this information is not available, false is returned.
+*/
+bool QCanBusDeviceInfo::isVirtual() const
+{
+ return d_ptr->isVirtual;
+}
+
+QT_END_NAMESPACE
diff --git a/src/serialbus/qcanbusdeviceinfo.h b/src/serialbus/qcanbusdeviceinfo.h
new file mode 100644
index 0000000..b21a4b3
--- /dev/null
+++ b/src/serialbus/qcanbusdeviceinfo.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCANBUSDEVICEINFO_H
+#define QCANBUSDEVICEINFO_H
+
+#include <QtCore/qshareddata.h>
+#include <QtCore/qstring.h>
+#include <QtSerialBus/qserialbusglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCanBusDeviceInfoPrivate;
+class QCanBusDeviceInfoPrivateDeleter;
+
+class Q_SERIALBUS_EXPORT QCanBusDeviceInfo
+{
+public:
+ QCanBusDeviceInfo();
+ QCanBusDeviceInfo(const QCanBusDeviceInfo &other);
+ ~QCanBusDeviceInfo();
+
+ void swap(QCanBusDeviceInfo &other) Q_DECL_NOTHROW
+ {
+ qSwap(d_ptr, other.d_ptr);
+ }
+
+ QCanBusDeviceInfo &operator=(const QCanBusDeviceInfo &other);
+ QCanBusDeviceInfo &operator=(QCanBusDeviceInfo &&other) Q_DECL_NOTHROW
+ {
+ swap(other);
+ return *this;
+ }
+
+ QString name() const;
+
+ bool hasFlexibleDataRate() const;
+ bool isVirtual() const;
+
+private:
+ friend class QCanBusDevice;
+
+ explicit QCanBusDeviceInfo(QCanBusDeviceInfoPrivate &dd);
+
+ QSharedDataPointer<QCanBusDeviceInfoPrivate> d_ptr;
+};
+
+Q_DECLARE_SHARED(QCanBusDeviceInfo)
+
+QT_END_NAMESPACE
+
+#endif // QCANBUSDEVICEINFO_H
diff --git a/src/serialbus/qcanbusdeviceinfo_p.h b/src/serialbus/qcanbusdeviceinfo_p.h
new file mode 100644
index 0000000..ae6d71d
--- /dev/null
+++ b/src/serialbus/qcanbusdeviceinfo_p.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCANBUSDEVICEINFO_P_H
+#define QCANBUSDEVICEINFO_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.
+//
+
+#include <QtCore/qshareddata.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCanBusDeviceInfoPrivate : public QSharedData {
+public:
+ QCanBusDeviceInfoPrivate() { }
+
+ ~QCanBusDeviceInfoPrivate()
+ {
+ }
+
+ QString name;
+ QString serialNumber;
+ bool hasFlexibleDataRate = false;
+ bool isVirtual = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCANBUSDEVICEINFO_P_H
diff --git a/src/serialbus/qcanbusfactory.cpp b/src/serialbus/qcanbusfactory.cpp
index 632d3c4..508741b 100644
--- a/src/serialbus/qcanbusfactory.cpp
+++ b/src/serialbus/qcanbusfactory.cpp
@@ -42,11 +42,16 @@ QT_BEGIN_NAMESPACE
\class QCanBusFactory
\inmodule QtSerialBus
\since 5.8
+ \deprecated
\brief The QCanBusFactory class is a factory class used as the
plugin interface for CAN bus plugins.
All plugins must implement the functions provided by this factory class.
+
+ This class is deprecated, you should use QCanBusFactoryV2 instead.
+
+ \sa QCanBusFactoryV2
*/
/*!
@@ -55,7 +60,7 @@ QT_BEGIN_NAMESPACE
Creates a new QCanBusDevice. The caller must take ownership of the returned pointer.
- \a interfaceName is the network/CAN interface name.
+ \a interfaceName is the CAN interface name and
\a errorMessage contains an error description in case of failure.
If the factory cannot create a plugin, it returns \c nullptr.
@@ -67,5 +72,42 @@ QT_BEGIN_NAMESPACE
\fn QCanBusFactory::~QCanBusFactory()
*/
+/*!
+ \class QCanBusFactoryV2
+ \inmodule QtSerialBus
+ \since 5.9
+
+ \brief The QCanBusFactoryV2 class is a factory class used as the
+ plugin interface for CAN bus plugins.
+
+ All plugins must implement the functions provided by this factory class.
+*/
+
+/*!
+ \fn QCanBusDevice *QCanBusFactoryV2::createDevice(const QString &interfaceName,
+ QString *errorMessage) const
+
+ Creates a new QCanBusDevice. The caller must take ownership of the returned pointer.
+
+ \a interfaceName is the CAN interface name and
+ \a errorMessage contains an error description in case of failure.
+
+ If the factory cannot create a plugin, it returns \c nullptr.
+*/
+
+/*!
+ \fn QList<QCanBusDeviceInfo> QCanBusFactoryV2::availableDevices(QString *errorMessage) const
+
+ Returns the list of available devices and their capabilities for the QCanBusDevice.
+
+ \a errorMessage contains an error description in case of failure.
+*/
+
+/*!
+ * \internal
+ */
+QCanBusFactoryV2::~QCanBusFactoryV2()
+{
+}
QT_END_NAMESPACE
diff --git a/src/serialbus/qcanbusfactory.h b/src/serialbus/qcanbusfactory.h
index 1d23168..d74903c 100644
--- a/src/serialbus/qcanbusfactory.h
+++ b/src/serialbus/qcanbusfactory.h
@@ -40,6 +40,7 @@
#include <QtCore/qstringlist.h>
#include <QtSerialBus/qserialbusglobal.h>
#include <QtSerialBus/qcanbusdevice.h>
+#include <QtSerialBus/qcanbusdeviceinfo.h>
QT_BEGIN_NAMESPACE
@@ -54,6 +55,19 @@ protected:
Q_DECLARE_INTERFACE(QCanBusFactory, "org.qt-project.Qt.QCanBusFactory")
+class Q_SERIALBUS_EXPORT QCanBusFactoryV2 : public QCanBusFactory
+{
+public:
+ virtual QCanBusDevice *createDevice(const QString &interfaceName,
+ QString *errorMessage) const = 0;
+ virtual QList<QCanBusDeviceInfo> availableDevices(QString *errorMessage) const = 0;
+
+protected:
+ virtual ~QCanBusFactoryV2();
+};
+
+Q_DECLARE_INTERFACE(QCanBusFactoryV2, "org.qt-project.Qt.QCanBusFactoryV2")
+
QT_END_NAMESPACE
#endif // QCANBUSFACTORY_H
diff --git a/src/serialbus/qcanbusframe.cpp b/src/serialbus/qcanbusframe.cpp
index 5527029..6ef0f11 100644
--- a/src/serialbus/qcanbusframe.cpp
+++ b/src/serialbus/qcanbusframe.cpp
@@ -148,6 +148,21 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \enum QCanBusFrame::Version
+ \internal
+
+ This enum describes the version of the QCanBusFrame.
+
+ With newer Qt versions, new features may be added to QCanBusFrame. To support serializing and
+ deserializing of frames with different features, the version needs to be incremented every
+ time a new feature appears. This enum describes, at which Qt version a specific QCanBusFrame
+ version appeared.
+
+ \value Qt_5_8 This frame is the initial version introduced in Qt 5.8
+ \value Qt_5_9 This frame version was introduced in Qt 5.9
+*/
+
+/*!
\enum QCanBusFrame::FrameType
This enum describes the type of the CAN frame.
@@ -253,6 +268,50 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \fn QCanBusFrame::hasBitrateSwitch() const
+ \since 5.9
+
+ Returns \c true if the CAN uses \e {Flexible Data-Rate} with \e {Bitrate Switch},
+ to transfer the payload data at a higher data bitrate.
+
+ \sa setBitrateSwitch() QCanBusDevice::DataBitRateKey
+*/
+
+/*!
+ \fn void QCanBusFrame::setBitrateSwitch(bool bitrateSwitch)
+ \since 5.9
+
+ Set the \e {Flexible Data-Rate} flag \e {Bitrate Switch} flag to \a bitrateSwitch.
+ The data field of frames with this flag is transferred at a higher data bitrate.
+
+ \sa hasBitrateSwitch() QCanBusDevice::DataBitRateKey
+*/
+
+/*!
+ \fn QCanBusFrame::hasErrorStateIndicator() const
+ \since 5.9
+
+ Returns \c true if the CAN uses \e {Flexible Data-Rate} with \e {Error State Indicator} set.
+
+ This flag is set by the transmitter's CAN FD hardware to indicate the transmitter's error state.
+
+ \sa setErrorStateIndicator()
+*/
+
+/*!
+ \fn void QCanBusFrame::setErrorStateIndicator(bool errorStateIndicator)
+ \since 5.9
+
+ Set the \e {Flexible Data-Rate} flag \e {Error State Indicator} flag to \a errorStateIndicator.
+
+ When sending CAN FD frames, this flag is automatically set by the CAN FD hardware.
+ \c QCanBusFrame::setErrorStateIndicator() should only be used for application testing,
+ e.g. on virtual CAN FD busses.
+
+ \sa hasErrorStateIndicator()
+*/
+
+/*!
\class QCanBusFrame::TimeStamp
\inmodule QtSerialBus
\since 5.8
@@ -334,19 +393,10 @@ QString QCanBusFrame::toString() const
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));
- }
- }
+ } else if (!payload().isEmpty()) {
+ const QByteArray data = payload().toHex(' ').toUpper();
+ result.append(QLatin1String(" "));
+ result.append(QLatin1String(data));
}
return result;
@@ -370,6 +420,8 @@ QDataStream &operator<<(QDataStream &out, const QCanBusFrame &frame)
const QCanBusFrame::TimeStamp stamp = frame.timeStamp();
out << stamp.seconds();
out << stamp.microSeconds();
+ if (frame.version >= QCanBusFrame::Version::Qt_5_9)
+ out << frame.hasBitrateSwitch() << frame.hasErrorStateIndicator();
return out;
}
@@ -385,6 +437,8 @@ QDataStream &operator>>(QDataStream &in, QCanBusFrame &frame)
quint8 version;
bool extendedFrameFormat;
bool flexibleDataRate;
+ bool bitrateSwitch = false;
+ bool errorStateIndicator = false;
QByteArray payload;
qint64 seconds;
qint64 microSeconds;
@@ -392,12 +446,17 @@ QDataStream &operator>>(QDataStream &in, QCanBusFrame &frame)
in >> frameId >> frameType >> version >> extendedFrameFormat >> flexibleDataRate
>> payload >> seconds >> microSeconds;
+ if (version >= QCanBusFrame::Version::Qt_5_9)
+ in >> bitrateSwitch >> errorStateIndicator;
+
frame.setFrameId(frameId);
frame.version = version;
frame.setFrameType(static_cast<QCanBusFrame::FrameType>(frameType));
frame.setExtendedFrameFormat(extendedFrameFormat);
frame.setFlexibleDataRateFormat(flexibleDataRate);
+ frame.setBitrateSwitch(bitrateSwitch);
+ frame.setErrorStateIndicator(errorStateIndicator);
frame.setPayload(payload);
frame.setTimeStamp(QCanBusFrame::TimeStamp(seconds, microSeconds));
diff --git a/src/serialbus/qcanbusframe.h b/src/serialbus/qcanbusframe.h
index b7ffbee..5e285c1 100644
--- a/src/serialbus/qcanbusframe.h
+++ b/src/serialbus/qcanbusframe.h
@@ -74,9 +74,13 @@ public:
explicit QCanBusFrame(FrameType type = DataFrame) Q_DECL_NOTHROW :
isExtendedFrame(0x0),
- version(0x0),
- isFlexibleDataRate(0x0)
+ version(Qt_5_9),
+ isFlexibleDataRate(0x0),
+ isBitrateSwitch(0x0),
+ isErrorStateIndicator(0x0),
+ reserved0(0x0)
{
+ Q_UNUSED(reserved0);
memset(reserved, 0, sizeof(reserved));
setFrameId(0x0);
setFrameType(type);
@@ -103,8 +107,11 @@ public:
explicit QCanBusFrame(quint32 identifier, const QByteArray &data) :
format(DataFrame),
isExtendedFrame(0x0),
- version(0x0),
+ version(Qt_5_9),
isFlexibleDataRate(data.length() > 8 ? 0x1 : 0x0),
+ isBitrateSwitch(0x0),
+ isErrorStateIndicator(0x0),
+ reserved0(0x0),
load(data)
{
memset(reserved, 0, sizeof(reserved));
@@ -174,13 +181,13 @@ public:
quint32 frameId() const Q_DECL_NOTHROW
{
- if (format == ErrorFrame)
+ if (Q_UNLIKELY(format == ErrorFrame))
return 0;
return (canId & 0x1FFFFFFFU);
}
void setFrameId(quint32 newFrameId)
{
- if (newFrameId < 0x20000000U) {
+ if (Q_LIKELY(newFrameId < 0x20000000U)) {
isValidFrameId = true;
canId = newFrameId;
setExtendedFrameFormat(isExtendedFrame || (newFrameId & 0x1FFFF800U));
@@ -221,6 +228,26 @@ public:
void setFlexibleDataRateFormat(bool isFlexibleData) Q_DECL_NOTHROW
{
isFlexibleDataRate = (isFlexibleData & 0x1);
+ if (!isFlexibleData) {
+ isBitrateSwitch = 0x0;
+ isErrorStateIndicator = 0x0;
+ }
+ }
+
+ bool hasBitrateSwitch() const Q_DECL_NOTHROW { return (isBitrateSwitch & 0x1); }
+ void setBitrateSwitch(bool bitrateSwitch) Q_DECL_NOTHROW
+ {
+ isBitrateSwitch = (bitrateSwitch & 0x1);
+ if (bitrateSwitch)
+ isFlexibleDataRate = 0x1;
+ }
+
+ bool hasErrorStateIndicator() const Q_DECL_NOTHROW { return (isErrorStateIndicator & 0x1); }
+ void setErrorStateIndicator(bool errorStateIndicator) Q_DECL_NOTHROW
+ {
+ isErrorStateIndicator = (errorStateIndicator & 0x1);
+ if (errorStateIndicator)
+ isFlexibleDataRate = 0x1;
}
#ifndef QT_NO_DATASTREAM
@@ -229,6 +256,11 @@ public:
#endif
private:
+ enum Version {
+ Qt_5_8 = 0x0,
+ Qt_5_9 = 0x1
+ };
+
quint32 canId:29; // acts as container for error codes too
quint8 format:3; // max of 8 frame types
@@ -237,8 +269,12 @@ private:
quint8 isValidFrameId:1;
quint8 isFlexibleDataRate:1;
+ quint8 isBitrateSwitch:1;
+ quint8 isErrorStateIndicator:1;
+ quint8 reserved0:6;
+
// reserved for future use
- quint8 reserved[3];
+ quint8 reserved[2];
QByteArray load;
TimeStamp stamp;
diff --git a/src/serialbus/qmodbusserver.cpp b/src/serialbus/qmodbusserver.cpp
index 71d59bc..815cb0c 100644
--- a/src/serialbus/qmodbusserver.cpp
+++ b/src/serialbus/qmodbusserver.cpp
@@ -994,7 +994,7 @@ QModbusResponse QModbusServerPrivate::processWriteMultipleCoilsRequest(const QMo
// range is numberOfCoils and therefore index too.
quint16 coil = numberOfCoils;
qint32 currentBit = 8 - ((byteCount * 8) - numberOfCoils);
- foreach (const auto &currentByte, bytes) {
+ for (const auto &currentByte : qAsConst(bytes)) {
for (currentBit -= 1; currentBit >= 0; --currentBit)
coils.setValue(--coil, currentByte[currentBit]);
currentBit = 8;
@@ -1243,7 +1243,7 @@ QModbusResponse QModbusServerPrivate::processEncapsulatedInterfaceTransportReque
payload[5] = quint8(0x00); // number of objects
const QList<int> objectIds = objectPool.objectIds();
- foreach (int id, objectIds) {
+ for (int id : objectIds) {
if (id < objectId)
continue;
if (id > lastObjectId)
diff --git a/src/serialbus/qmodbustcpclient_p.h b/src/serialbus/qmodbustcpclient_p.h
index 0336d21..e4dfa9e 100644
--- a/src/serialbus/qmodbustcpclient_p.h
+++ b/src/serialbus/qmodbustcpclient_p.h
@@ -242,14 +242,13 @@ public:
qCDebug(QT_MODBUS) << "(TCP client) Cleanup of pending requests";
- foreach (auto tid, m_transactionStore.keys()) {
- const QueueElement elem = m_transactionStore.take(tid);
+ for (const auto &elem : qAsConst(m_transactionStore)) {
if (elem.reply.isNull())
continue;
-
elem.reply->setError(QModbusDevice::ReplyAbortedError,
QModbusClient::tr("Reply aborted due to connection closure."));
}
+ m_transactionStore.clear();
}
// This doesn't overflow, it rather "wraps around". Expected.
diff --git a/src/serialbus/qmodbustcpserver.cpp b/src/serialbus/qmodbustcpserver.cpp
index 26e534d..39aeda8 100644
--- a/src/serialbus/qmodbustcpserver.cpp
+++ b/src/serialbus/qmodbustcpserver.cpp
@@ -131,7 +131,7 @@ void QModbusTcpServer::close()
if (d->m_tcpServer->isListening())
d->m_tcpServer->close();
- foreach (auto socket, d->connections)
+ for (auto socket : qAsConst(d->connections))
socket->disconnectFromHost();
setState(QModbusDevice::UnconnectedState);
diff --git a/src/serialbus/serialbus.pro b/src/serialbus/serialbus.pro
index 02348da..14583bc 100644
--- a/src/serialbus/serialbus.pro
+++ b/src/serialbus/serialbus.pro
@@ -8,6 +8,7 @@ QMAKE_DOCS = $$PWD/doc/qtserialbus.qdocconf
PUBLIC_HEADERS += \
qcanbusdevice.h \
+ qcanbusdeviceinfo.h \
qcanbusfactory.h \
qcanbusframe.h \
qcanbus.h \
@@ -26,6 +27,7 @@ PUBLIC_HEADERS += \
PRIVATE_HEADERS += \
qcanbusdevice_p.h \
+ qcanbusdeviceinfo_p.h \
qmodbusserver_p.h \
qmodbusclient_p.h \
qmodbusdevice_p.h \
@@ -39,6 +41,7 @@ PRIVATE_HEADERS += \
SOURCES += \
qcanbusdevice.cpp \
+ qcanbusdeviceinfo.cpp \
qcanbus.cpp \
qcanbusfactory.cpp \
qcanbusframe.cpp \
diff --git a/src/tools/canbusutil/canbusutil.cpp b/src/tools/canbusutil/canbusutil.cpp
index 693794c..0a0a6df 100644
--- a/src/tools/canbusutil/canbusutil.cpp
+++ b/src/tools/canbusutil/canbusutil.cpp
@@ -50,6 +50,11 @@ void CanBusUtil::setShowTimeStamp(bool showTimeStamp)
m_readTask->setShowTimeStamp(showTimeStamp);
}
+void CanBusUtil::setShowFdFlags(bool showFdFlags)
+{
+ m_readTask->setShowFdFlags(showFdFlags);
+}
+
bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, const QString &data)
{
if (!m_canBus) {
@@ -66,6 +71,8 @@ bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, con
return false;
if (m_listening) {
+ if (m_readTask->isShowFdFlags())
+ m_canDevice->setConfigurationParameter(QCanBusDevice::CanFdKey, true);
connect(m_canDevice.data(), &QCanBusDevice::framesReceived, m_readTask, &ReadTask::checkMessages);
} else {
if (!sendData())
@@ -83,6 +90,20 @@ void CanBusUtil::printPlugins()
m_output << plugins.at(i) << endl;
}
+int CanBusUtil::printDevices(const QString &pluginName)
+{
+ QString errorMessage;
+ const QList<QCanBusDeviceInfo> devices = m_canBus->availableDevices(pluginName, &errorMessage);
+ if (!errorMessage.isEmpty()) {
+ m_output << "Error: " << errorMessage << endl;
+ return 1;
+ }
+
+ for (const QCanBusDeviceInfo &info : devices)
+ m_output << info.name() << endl;
+ return 0;
+}
+
bool CanBusUtil::parseDataField(qint32 &id, QString &payload)
{
int hashMarkPos = m_data.indexOf('#');
@@ -128,17 +149,25 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame)
payload = payload.mid(1);
}
- if (payload.size() % 2 != 0) {
- m_output << "Data field invalid: Size is not multiple of two." << endl;
- return false;
- }
-
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;
}
+ if (payload.size() % 2 != 0) {
+ if (frame->hasFlexibleDataRateFormat()) {
+ enum { BitrateSwitchFlag = 1, ErrorStateIndicatorFlag = 2 };
+ const int flags = payload.left(1).toInt(nullptr, 16);
+ frame->setBitrateSwitch(flags & BitrateSwitchFlag);
+ frame->setErrorStateIndicator(flags & ErrorStateIndicatorFlag);
+ payload.remove(0, 1);
+ } else {
+ m_output << "Data field invalid: Size is not multiple of two." << endl;
+ return false;
+ }
+ }
+
QByteArray bytes = QByteArray::fromHex(payload.toLatin1());
const int maxSize = frame->hasFlexibleDataRateFormat() ? 64 : 8;
diff --git a/src/tools/canbusutil/canbusutil.h b/src/tools/canbusutil/canbusutil.h
index 2468710..af1d3af 100644
--- a/src/tools/canbusutil/canbusutil.h
+++ b/src/tools/canbusutil/canbusutil.h
@@ -57,8 +57,10 @@ public:
explicit CanBusUtil(QTextStream &output, QCoreApplication &app, QObject *parent = nullptr);
void setShowTimeStamp(bool showTimeStamp);
+ void setShowFdFlags(bool showFdFlags);
bool start(const QString &pluginName, const QString &deviceName, const QString &data = QString());
void printPlugins();
+ int printDevices(const QString &pluginName);
private:
bool parseDataField(qint32 &id, QString &payload);
diff --git a/src/tools/canbusutil/main.cpp b/src/tools/canbusutil/main.cpp
index 4249bb3..1304f22 100644
--- a/src/tools/canbusutil/main.cpp
+++ b/src/tools/canbusutil/main.cpp
@@ -63,7 +63,7 @@ int main(int argc, char *argv[])
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."));
+ "If the -l option is set, all received CAN bus frames are dumped."));
parser.addHelpOption();
parser.addVersionOption();
@@ -76,10 +76,12 @@ int main(int argc, char *argv[])
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"
+ "\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>##[flags]{payload} (CAN FD data frames),\n"
"where {payload} has 0..8 (0..64 CAN FD) ASCII hex-value pairs, "
+ "and flags is one optional ASCII hex char for CAN FD flags: "
+ "1 = Bitrate Switch, 2 = Error State Indicator\n"
"e.g. 1#1a2b3c\n"), QStringLiteral("[data]"));
const QCommandLineOption listeningOption({"l", "listen"},
@@ -91,9 +93,17 @@ int main(int argc, char *argv[])
parser.addOption(listOption);
const QCommandLineOption showTimeStampOption({"t", "timestamp"},
- CanBusUtil::tr("Show timestamp for each received message."));
+ CanBusUtil::tr("Show timestamp for each received CAN bus frame."));
parser.addOption(showTimeStampOption);
+ const QCommandLineOption showFdFlagsOption({"i", "info"},
+ CanBusUtil::tr("Show extra info (CAN FD flags) for each received CAN bus frame."));
+ parser.addOption(showFdFlagsOption);
+
+ const QCommandLineOption listDevicesOption({"d", "devices"},
+ CanBusUtil::tr("Show available CAN bus devices for the given plugin."));
+ parser.addOption(listDevicesOption);
+
parser.process(app);
if (parser.isSet(listOption)) {
@@ -103,15 +113,19 @@ int main(int argc, char *argv[])
QString data;
const QStringList args = parser.positionalArguments();
- if (!parser.isSet(listeningOption) && args.size() == 3) {
+ if (parser.isSet(listeningOption)) {
+ util.setShowTimeStamp(parser.isSet(showTimeStampOption));
+ util.setShowFdFlags(parser.isSet(showFdFlagsOption));
+ } else if (args.size() == 3) {
data = args[2];
- } else if (!parser.isSet(listeningOption) || args.size() != 2) {
+ } else if (args.size() == 1 && parser.isSet(listDevicesOption)) {
+ return util.printDevices(args[0]);
+ } else if (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;
diff --git a/src/tools/canbusutil/readtask.cpp b/src/tools/canbusutil/readtask.cpp
index b93820f..fb159b7 100644
--- a/src/tools/canbusutil/readtask.cpp
+++ b/src/tools/canbusutil/readtask.cpp
@@ -45,10 +45,20 @@ void ReadTask::setShowTimeStamp(bool showTimeStamp)
m_showTimeStamp = showTimeStamp;
}
+bool ReadTask::isShowFdFlags() const
+{
+ return m_showFdFlags;
+}
+
+void ReadTask::setShowFdFlags(bool showFlags)
+{
+ m_showFdFlags = showFlags;
+}
+
void ReadTask::checkMessages() {
auto canDevice = qobject_cast<QCanBusDevice *>(QObject::sender());
if (canDevice == nullptr) {
- qWarning() << "ReadTask::checkMessages: Unknown sender";
+ qWarning("ReadTask::checkMessages: Unknown sender");
return;
}
@@ -63,6 +73,15 @@ void ReadTask::checkMessages() {
.arg(frame.timeStamp().microSeconds() / 100, 4, 10, QLatin1Char('0'));
}
+ if (m_showFdFlags) {
+ QString flags = QLatin1String("- - ");
+ if (frame.hasBitrateSwitch())
+ flags[0] = QLatin1Char('B');
+ if (frame.hasErrorStateIndicator())
+ flags[2] = QLatin1Char('E');
+ view += flags;
+ }
+
if (frame.frameType() == QCanBusFrame::ErrorFrame)
view += canDevice->interpretErrorFrame(frame);
else
@@ -75,7 +94,7 @@ void ReadTask::checkMessages() {
void ReadTask::receiveError(QCanBusDevice::CanBusError /*error*/) {
auto canDevice = qobject_cast<QCanBusDevice *>(QObject::sender());
if (canDevice == nullptr) {
- qWarning() << "ReadTask::receiveError: Unknown sender";
+ qWarning("ReadTask::receiveError: Unknown sender");
return;
}
diff --git a/src/tools/canbusutil/readtask.h b/src/tools/canbusutil/readtask.h
index cab2b76..19bc7ad 100644
--- a/src/tools/canbusutil/readtask.h
+++ b/src/tools/canbusutil/readtask.h
@@ -47,6 +47,8 @@ class ReadTask : public QObject
public:
explicit ReadTask(QTextStream &output, QObject *parent = nullptr);
void setShowTimeStamp(bool showStamp);
+ bool isShowFdFlags() const;
+ void setShowFdFlags(bool isShowFdFlags);
signals:
void sigTermSignal();
@@ -58,6 +60,7 @@ public slots:
private:
QTextStream &output;
bool m_showTimeStamp = false;
+ bool m_showFdFlags = false;
};
#endif // READTASK_H
diff --git a/tests/auto/plugins/genericcanbus/dummybackend.cpp b/tests/auto/plugins/genericcanbus/dummybackend.cpp
index 82cdb86..4c3173a 100644
--- a/tests/auto/plugins/genericcanbus/dummybackend.cpp
+++ b/tests/auto/plugins/genericcanbus/dummybackend.cpp
@@ -84,4 +84,9 @@ QString DummyBackend::interpretErrorFrame(const QCanBusFrame &/*errorFrame*/)
return QString();
}
+QList<QCanBusDeviceInfo> DummyBackend::interfaces()
+{
+ return {createDeviceInfo(QStringLiteral("can0"), true, true)};
+}
+
QT_END_NAMESPACE
diff --git a/tests/auto/plugins/genericcanbus/dummybackend.h b/tests/auto/plugins/genericcanbus/dummybackend.h
index 965177b..22bbc80 100644
--- a/tests/auto/plugins/genericcanbus/dummybackend.h
+++ b/tests/auto/plugins/genericcanbus/dummybackend.h
@@ -56,6 +56,8 @@ public:
QString interpretErrorFrame(const QCanBusFrame &) override;
+ static QList<QCanBusDeviceInfo> interfaces();
+
public Q_SLOTS:
void sendMessage();
diff --git a/tests/auto/plugins/genericcanbus/main.cpp b/tests/auto/plugins/genericcanbus/main.cpp
index 838715a..8e14fee 100644
--- a/tests/auto/plugins/genericcanbus/main.cpp
+++ b/tests/auto/plugins/genericcanbus/main.cpp
@@ -38,18 +38,24 @@
#include <QtSerialBus/qcanbus.h>
#include <QtSerialBus/qcanbusfactory.h>
-
-#include <QtCore/qfile.h>
+#include "../../../../src/serialbus/qcanbusdeviceinfo_p.h"
QT_BEGIN_NAMESPACE
-class GenericBusPlugin : public QObject, public QCanBusFactory
+class GenericBusPlugin : public QObject, public QCanBusFactoryV2
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QCanBusFactory" FILE "plugin.json")
- Q_INTERFACES(QCanBusFactory)
+ Q_INTERFACES(QCanBusFactoryV2)
public:
+ QList<QCanBusDeviceInfo> availableDevices(QString *errorMessage) const override
+ {
+ Q_UNUSED(errorMessage);
+
+ return DummyBackend::interfaces();
+ }
+
QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
{
if (interfaceName == QStringLiteral("invalid")) {
diff --git a/tests/auto/plugins/genericcanbusv1/dummybackendv1.cpp b/tests/auto/plugins/genericcanbusv1/dummybackendv1.cpp
new file mode 100644
index 0000000..117aeec
--- /dev/null
+++ b/tests/auto/plugins/genericcanbusv1/dummybackendv1.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "dummybackendv1.h"
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+DummyBackendV1::DummyBackendV1() :
+ sendTimer(new QTimer(this))
+{
+ connect(sendTimer, &QTimer::timeout, [this]() {
+ const quint64 timeStamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
+ QCanBusFrame dummyFrame(11, "abc");
+ dummyFrame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(timeStamp * 1000));
+
+ enqueueReceivedFrames({dummyFrame});
+ });
+ sendTimer->start(1000);
+}
+
+bool DummyBackendV1::open()
+{
+ setState(QCanBusDevice::ConnectedState);
+ return true;
+}
+
+void DummyBackendV1::close()
+{
+ setState(QCanBusDevice::UnconnectedState);
+}
+
+bool DummyBackendV1::writeFrame(const QCanBusFrame &data)
+{
+ qDebug("DummyBackendV1::writeFrame: %ls", qUtf16Printable(data.toString()));
+ return true;
+}
+
+QString DummyBackendV1::interpretErrorFrame(const QCanBusFrame &/*errorFrame*/)
+{
+ return QString();
+}
+
+QT_END_NAMESPACE
diff --git a/tests/auto/plugins/genericcanbusv1/dummybackendv1.h b/tests/auto/plugins/genericcanbusv1/dummybackendv1.h
new file mode 100644
index 0000000..4c3fc64
--- /dev/null
+++ b/tests/auto/plugins/genericcanbusv1/dummybackendv1.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DUMMYBACKENDV1_H
+#define DUMMYBACKENDV1_H
+
+#include <QtSerialBus/qcanbusdevice.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTimer;
+
+class DummyBackendV1 : public QCanBusDevice
+{
+ Q_OBJECT
+public:
+ explicit DummyBackendV1();
+
+ bool open() override;
+ void close() override;
+
+ bool writeFrame(const QCanBusFrame &data) override;
+
+ QString interpretErrorFrame(const QCanBusFrame &) override;
+
+private:
+ QTimer *sendTimer;
+};
+
+QT_END_NAMESPACE
+
+#endif // DUMMYBACKENDV1_H
diff --git a/tests/auto/plugins/genericcanbusv1/genericcanbusv1.pro b/tests/auto/plugins/genericcanbusv1/genericcanbusv1.pro
new file mode 100644
index 0000000..57c4aa1
--- /dev/null
+++ b/tests/auto/plugins/genericcanbusv1/genericcanbusv1.pro
@@ -0,0 +1,17 @@
+TARGET = qtcanbustestgenericv1
+
+QT = core serialbus
+
+HEADERS += \
+ dummybackendv1.h
+
+SOURCES += \
+ main.cpp \
+ dummybackendv1.cpp
+
+DISTFILES = plugin.json
+
+PLUGIN_TYPE = canbus
+PLUGIN_EXTENDS = -
+PLUGIN_CLASS_NAME = GenericBusPluginV1
+load(qt_plugin)
diff --git a/tests/auto/plugins/genericcanbusv1/main.cpp b/tests/auto/plugins/genericcanbusv1/main.cpp
new file mode 100644
index 0000000..3ed41f9
--- /dev/null
+++ b/tests/auto/plugins/genericcanbusv1/main.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Andre Hartmann <aha_1980@gmx.de>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtSerialBus module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "dummybackendv1.h"
+
+#include <QtSerialBus/qcanbus.h>
+#include <QtSerialBus/qcanbusfactory.h>
+
+QT_BEGIN_NAMESPACE
+
+class GenericBusPluginV1 : 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
+ {
+ if (interfaceName == QStringLiteral("invalid")) {
+ if (errorMessage)
+ *errorMessage = tr("No such interface: '%1'").arg(interfaceName);
+
+ return nullptr;
+ }
+ auto device = new DummyBackendV1();
+ return device;
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/tests/auto/plugins/genericcanbusv1/plugin.json b/tests/auto/plugins/genericcanbusv1/plugin.json
new file mode 100644
index 0000000..e9f0f4c
--- /dev/null
+++ b/tests/auto/plugins/genericcanbusv1/plugin.json
@@ -0,0 +1,3 @@
+{
+ "Key": "genericv1"
+}
diff --git a/tests/auto/plugins/plugins.pro b/tests/auto/plugins/plugins.pro
index 58d4100..f4f4623 100644
--- a/tests/auto/plugins/plugins.pro
+++ b/tests/auto/plugins/plugins.pro
@@ -1,2 +1,2 @@
TEMPLATE = subdirs
-SUBDIRS += genericcanbus
+SUBDIRS += genericcanbus genericcanbusv1
diff --git a/tests/auto/qcanbus/tst_qcanbus.cpp b/tests/auto/qcanbus/tst_qcanbus.cpp
index 845b0c8..9e6a647 100644
--- a/tests/auto/qcanbus/tst_qcanbus.cpp
+++ b/tests/auto/qcanbus/tst_qcanbus.cpp
@@ -48,6 +48,7 @@ public:
private slots:
void initTestCase();
void plugins();
+ void interfaces();
void createDevice();
private:
@@ -82,11 +83,29 @@ void tst_QCanBus::plugins()
const QStringList pluginList = bus->plugins();
QVERIFY(!pluginList.isEmpty());
QVERIFY(pluginList.contains("generic"));
+ QVERIFY(pluginList.contains("genericv1"));
+}
+
+void tst_QCanBus::interfaces()
+{
+ // Plugins derived from QCanBusFactory(V1) don't have availableDevices()
+ const QList<QCanBusDeviceInfo> pluginListV1 = bus->availableDevices("genericV1");
+ QVERIFY(pluginListV1.isEmpty());
+ const QList<QCanBusDeviceInfo> pluginList = bus->availableDevices("generic");
+ QCOMPARE(1, pluginList.size());
+ QCOMPARE(QString("can0"), pluginList.at(0).name());
+ QVERIFY(pluginList.at(0).isVirtual());
+ QVERIFY(pluginList.at(0).hasFlexibleDataRate());
}
void tst_QCanBus::createDevice()
{
+ // Assure we can still create plugins derived from QCanBusFactory(V1)
+ QCanBusDevice *dummyV1 = bus->createDevice("genericv1", "unused");
+ QVERIFY(dummyV1);
+ delete dummyV1;
+
QString error, error2;
QCanBusDevice *dummy = bus->createDevice("generic", "unused");
QCanBusDevice *dummy2 = bus->createDevice("generic", "unused");
diff --git a/tests/auto/qcanbusframe/tst_qcanbusframe.cpp b/tests/auto/qcanbusframe/tst_qcanbusframe.cpp
index a6ab740..b67b6f8 100644
--- a/tests/auto/qcanbusframe/tst_qcanbusframe.cpp
+++ b/tests/auto/qcanbusframe/tst_qcanbusframe.cpp
@@ -48,6 +48,8 @@ private slots:
void id();
void payload();
void timeStamp();
+ void bitRateSwitch();
+ void errorStateIndicator();
void tst_isValid_data();
void tst_isValid();
@@ -180,6 +182,88 @@ void tst_QCanBusFrame::timeStamp()
QCOMPARE(timeStamp.microSeconds(), 1);
}
+void tst_QCanBusFrame::bitRateSwitch()
+{
+ QCanBusFrame frame(QCanBusFrame::DataFrame);
+ QVERIFY(!frame.hasBitrateSwitch());
+
+ // set CAN FD does not set BRS
+ frame.setFlexibleDataRateFormat(true);
+ QVERIFY(frame.hasFlexibleDataRateFormat());
+ QVERIFY(!frame.hasBitrateSwitch());
+
+ // set BRS keeps CAN FD
+ frame.setBitrateSwitch(true);
+ QVERIFY(frame.hasFlexibleDataRateFormat());
+ QVERIFY(frame.hasBitrateSwitch());
+
+ // clear BRS keeps CAN FD
+ frame.setBitrateSwitch(false);
+ QVERIFY(frame.hasFlexibleDataRateFormat());
+ QVERIFY(!frame.hasBitrateSwitch());
+
+ // clear CAN FD
+ frame.setFlexibleDataRateFormat(false);
+ QVERIFY(!frame.hasFlexibleDataRateFormat());
+ QVERIFY(!frame.hasBitrateSwitch());
+
+ // set BRS sets CAN FD
+ frame.setBitrateSwitch(true);
+ QVERIFY(frame.hasFlexibleDataRateFormat());
+ QVERIFY(frame.hasBitrateSwitch());
+
+ // clear CAN FD clears BRS
+ frame.setFlexibleDataRateFormat(false);
+ QVERIFY(!frame.hasFlexibleDataRateFormat());
+ QVERIFY(!frame.hasBitrateSwitch());
+
+ // default constructed CAN FD frame has no BRS
+ const QCanBusFrame frame2(0x123, QByteArray(10, 0x55));
+ QVERIFY(frame2.hasFlexibleDataRateFormat());
+ QVERIFY(!frame2.hasBitrateSwitch());
+}
+
+void tst_QCanBusFrame::errorStateIndicator()
+{
+ QCanBusFrame frame(QCanBusFrame::DataFrame);
+ QVERIFY(!frame.hasErrorStateIndicator());
+
+ // set CAN FD does not set ESI
+ frame.setFlexibleDataRateFormat(true);
+ QVERIFY(frame.hasFlexibleDataRateFormat());
+ QVERIFY(!frame.hasErrorStateIndicator());
+
+ // set ESI keeps CAN FD
+ frame.setErrorStateIndicator(true);
+ QVERIFY(frame.hasFlexibleDataRateFormat());
+ QVERIFY(frame.hasErrorStateIndicator());
+
+ // clear ESI keeps CAN FD
+ frame.setErrorStateIndicator(false);
+ QVERIFY(frame.hasFlexibleDataRateFormat());
+ QVERIFY(!frame.hasErrorStateIndicator());
+
+ // clear CAN FD
+ frame.setFlexibleDataRateFormat(false);
+ QVERIFY(!frame.hasFlexibleDataRateFormat());
+ QVERIFY(!frame.hasErrorStateIndicator());
+
+ // set ESI sets CAN FD
+ frame.setErrorStateIndicator(true);
+ QVERIFY(frame.hasFlexibleDataRateFormat());
+ QVERIFY(frame.hasErrorStateIndicator());
+
+ // clear CAN FD clears ESI
+ frame.setFlexibleDataRateFormat(false);
+ QVERIFY(!frame.hasFlexibleDataRateFormat());
+ QVERIFY(!frame.hasErrorStateIndicator());
+
+ // default constructed CAN FD frame has no ESI
+ const QCanBusFrame frame2(0x123, QByteArray(10, 0x55));
+ QVERIFY(frame2.hasFlexibleDataRateFormat());
+ QVERIFY(!frame2.hasErrorStateIndicator());
+}
+
void tst_QCanBusFrame::tst_isValid_data()
{
QTest::addColumn<QCanBusFrame::FrameType>("frameType");
@@ -346,33 +430,37 @@ void tst_QCanBusFrame::streaming_data()
QTest::addColumn<qint64>("microSeconds");
QTest::addColumn<bool>("isExtended");
QTest::addColumn<bool>("isFlexibleDataRate");
+ QTest::addColumn<bool>("isBitrateSwitch");
QTest::addColumn<QCanBusFrame::FrameType>("frameType");
QTest::newRow("emptyFrame") << quint32(0) << QByteArray()
<< qint64(0) << qint64(0)
- << false << false << QCanBusFrame::DataFrame;
+ << false << false << false << QCanBusFrame::DataFrame;
QTest::newRow("fullFrame1") << quint32(123) << QByteArray("abcde1")
<< qint64(456) << qint64(784)
- << true << false << QCanBusFrame::DataFrame;
+ << true << false << false << QCanBusFrame::DataFrame;
QTest::newRow("fullFrame2") << quint32(123) << QByteArray("abcde2")
<< qint64(457) << qint64(785)
- << false << false << QCanBusFrame::DataFrame;
+ << false << false << false << QCanBusFrame::DataFrame;
QTest::newRow("fullFrameFD") << quint32(123) << QByteArray("abcdfd")
<< qint64(457) << qint64(785)
- << false << true << QCanBusFrame::DataFrame;
+ << false << true << false << QCanBusFrame::DataFrame;
+ QTest::newRow("fullFrameBRS") << quint32(123) << QByteArray("abcdfd")
+ << qint64(457) << qint64(785)
+ << false << true << true << QCanBusFrame::DataFrame;
QTest::newRow("fullFrame3") << quint32(123) << QByteArray("abcde3")
<< qint64(458) << qint64(786)
- << true << false << QCanBusFrame::RemoteRequestFrame;
+ << true << false << false << QCanBusFrame::RemoteRequestFrame;
QTest::newRow("fullFrame4") << quint32(123) << QByteArray("abcde4")
<< qint64(459) << qint64(787)
- << false << false << QCanBusFrame::RemoteRequestFrame;
+ << false << false << false << QCanBusFrame::RemoteRequestFrame;
QTest::newRow("fullFrame5") << quint32(123) << QByteArray("abcde5")
<< qint64(460) << qint64(789)
- << true << false << QCanBusFrame::ErrorFrame;
+ << true << false << false << QCanBusFrame::ErrorFrame;
QTest::newRow("fullFrame6") << quint32(123) << QByteArray("abcde6")
<< qint64(453) << qint64(788)
- << false << false << QCanBusFrame::ErrorFrame;
+ << false << false << false << QCanBusFrame::ErrorFrame;
}
void tst_QCanBusFrame::streaming()
@@ -383,6 +471,7 @@ void tst_QCanBusFrame::streaming()
QFETCH(qint64, microSeconds);
QFETCH(bool, isExtended);
QFETCH(bool, isFlexibleDataRate);
+ QFETCH(bool, isBitrateSwitch);
QFETCH(QCanBusFrame::FrameType, frameType);
QCanBusFrame originalFrame(frameId, payload);
@@ -391,6 +480,7 @@ void tst_QCanBusFrame::streaming()
originalFrame.setExtendedFrameFormat(isExtended);
originalFrame.setFlexibleDataRateFormat(isFlexibleDataRate);
+ originalFrame.setBitrateSwitch(isBitrateSwitch);
originalFrame.setFrameType(frameType);
QByteArray buffer;
@@ -413,6 +503,8 @@ void tst_QCanBusFrame::streaming()
originalFrame.hasExtendedFrameFormat());
QCOMPARE(restoredFrame.hasFlexibleDataRateFormat(),
originalFrame.hasFlexibleDataRateFormat());
+ QCOMPARE(restoredFrame.hasBitrateSwitch(),
+ originalFrame.hasBitrateSwitch());
}
void tst_QCanBusFrame::tst_error()
diff --git a/tests/auto/qmodbusdataunit/tst_qmodbusdataunit.cpp b/tests/auto/qmodbusdataunit/tst_qmodbusdataunit.cpp
index 01502db..131411c 100644
--- a/tests/auto/qmodbusdataunit/tst_qmodbusdataunit.cpp
+++ b/tests/auto/qmodbusdataunit/tst_qmodbusdataunit.cpp
@@ -65,10 +65,12 @@ void tst_QModbusDataUnit::constructors()
QModbusDataUnit coils(QModbusDataUnit::Coils, 15, 20);
QCOMPARE(coils.registerType(), QModbusDataUnit::Coils);
QCOMPARE(coils.startAddress(), 15);
- QCOMPARE(coils.values().size(), 20);
- QCOMPARE(coils.values(), QVector<quint16>(20));
- foreach (auto val, coils.values())
+ const auto values = coils.values();
+ QCOMPARE(values.size(), 20);
+ QCOMPARE(values, QVector<quint16>(20));
+ for (auto val : values) {
QCOMPARE(val, quint16(0));
+ }
QCOMPARE(coils.valueCount(), 20u);
QModbusDataUnit unit2(QModbusDataUnit::HoldingRegisters, 3, QVector<quint16>() << 9);