summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.qmake.conf2
-rw-r--r--examples/serialbus/can/mainwindow.cpp18
-rw-r--r--qtserialbus.pro2
-rw-r--r--src/plugins/canbus/canbus.pro3
-rw-r--r--src/plugins/canbus/peakcan/peakcan_symbols_p.h353
-rw-r--r--src/plugins/canbus/peakcan/peakcanbackend.cpp293
-rw-r--r--src/plugins/canbus/peakcan/peakcanbackend_p.h1
-rw-r--r--src/plugins/canbus/virtualcan/main.cpp76
-rw-r--r--src/plugins/canbus/virtualcan/plugin.json3
-rw-r--r--src/plugins/canbus/virtualcan/virtualcan.pro17
-rw-r--r--src/plugins/canbus/virtualcan/virtualcanbackend.cpp362
-rw-r--r--src/plugins/canbus/virtualcan/virtualcanbackend.h106
-rw-r--r--src/serialbus/doc/src/peakcan.qdoc3
-rw-r--r--src/serialbus/doc/src/qtcanbus-backends.qdoc4
-rw-r--r--src/serialbus/doc/src/qtserialbus-module-cpp.qdoc4
-rw-r--r--src/serialbus/doc/src/virtualcan.qdoc119
-rw-r--r--src/serialbus/qcanbusdevice.cpp68
-rw-r--r--src/serialbus/qcanbusdevice.h10
-rw-r--r--src/tools/canbusutil/canbusutil.cpp11
-rw-r--r--src/tools/canbusutil/canbusutil.h3
-rw-r--r--src/tools/canbusutil/main.cpp38
-rw-r--r--tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp58
22 files changed, 1343 insertions, 211 deletions
diff --git a/.qmake.conf b/.qmake.conf
index 2714a07..81e7e4e 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -2,4 +2,4 @@ load(qt_build_config)
CONFIG += warning_clean
DEFINES += QT_NO_FOREACH
-MODULE_VERSION = 5.11.2
+MODULE_VERSION = 5.12.0
diff --git a/examples/serialbus/can/mainwindow.cpp b/examples/serialbus/can/mainwindow.cpp
index fd5861f..300bbde 100644
--- a/examples/serialbus/can/mainwindow.cpp
+++ b/examples/serialbus/can/mainwindow.cpp
@@ -151,11 +151,21 @@ void MainWindow::connectDevice()
m_ui->sendFrameBox->setEnabled(true);
- QVariant bitRate = m_canDevice->configurationParameter(QCanBusDevice::BitRateKey);
+ const QVariant bitRate = m_canDevice->configurationParameter(QCanBusDevice::BitRateKey);
if (bitRate.isValid()) {
- m_status->setText(tr("Plugin: %1, connected to %2 at %3 kBit/s")
- .arg(p.pluginName).arg(p.deviceInterfaceName)
- .arg(bitRate.toInt() / 1000));
+ const bool isCanFd =
+ m_canDevice->configurationParameter(QCanBusDevice::CanFdKey).toBool();
+ const QVariant dataBitRate =
+ m_canDevice->configurationParameter(QCanBusDevice::DataBitRateKey);
+ if (isCanFd && dataBitRate.isValid()) {
+ m_status->setText(tr("Plugin: %1, connected to %2 at %3 / %4 kBit/s")
+ .arg(p.pluginName).arg(p.deviceInterfaceName)
+ .arg(bitRate.toInt() / 1000).arg(dataBitRate.toInt() / 1000));
+ } else {
+ m_status->setText(tr("Plugin: %1, connected to %2 at %3 kBit/s")
+ .arg(p.pluginName).arg(p.deviceInterfaceName)
+ .arg(bitRate.toInt() / 1000));
+ }
} else {
m_status->setText(tr("Plugin: %1, connected to %2")
.arg(p.pluginName).arg(p.deviceInterfaceName));
diff --git a/qtserialbus.pro b/qtserialbus.pro
index 469da33..6261f9f 100644
--- a/qtserialbus.pro
+++ b/qtserialbus.pro
@@ -1,4 +1,4 @@
-requires(qtHaveModule(serialport))
+requires(qtHaveModule(serialport):!wasm)
lessThan(QT_MAJOR_VERSION, 5) {
message("Cannot build current QtSerialBus sources with Qt version $${QT_VERSION}.")
diff --git a/src/plugins/canbus/canbus.pro b/src/plugins/canbus/canbus.pro
index 0eddffc..4366027 100644
--- a/src/plugins/canbus/canbus.pro
+++ b/src/plugins/canbus/canbus.pro
@@ -2,6 +2,9 @@ TEMPLATE = subdirs
include($$OUT_PWD/../../serialbus/qtserialbus-config.pri)
QT_FOR_CONFIG += serialbus-private
+
+SUBDIRS += virtualcan
+
qtConfig(socketcan) {
SUBDIRS += socketcan
}
diff --git a/src/plugins/canbus/peakcan/peakcan_symbols_p.h b/src/plugins/canbus/peakcan/peakcan_symbols_p.h
index d94a7ec..9ab2e67 100644
--- a/src/plugins/canbus/peakcan/peakcan_symbols_p.h
+++ b/src/plugins/canbus/peakcan/peakcan_symbols_p.h
@@ -61,147 +61,178 @@
#endif
// Currently defined and supported PCAN channels
-#define PCAN_NONEBUS 0x00 // Undefined/default value for a PCAN bus
-
-#define PCAN_ISABUS1 0x21 // PCAN-ISA interface, channel 1
-#define PCAN_ISABUS2 0x22 // PCAN-ISA interface, channel 2
-#define PCAN_ISABUS3 0x23 // PCAN-ISA interface, channel 3
-#define PCAN_ISABUS4 0x24 // PCAN-ISA interface, channel 4
-#define PCAN_ISABUS5 0x25 // PCAN-ISA interface, channel 5
-#define PCAN_ISABUS6 0x26 // PCAN-ISA interface, channel 6
-#define PCAN_ISABUS7 0x27 // PCAN-ISA interface, channel 7
-#define PCAN_ISABUS8 0x28 // PCAN-ISA interface, channel 8
-
-#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_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
+#define PCAN_NONEBUS 0x00U // Undefined/default value for a PCAN bus
+
+#define PCAN_ISABUS1 0x21U // PCAN-ISA interface, channel 1
+#define PCAN_ISABUS2 0x22U // PCAN-ISA interface, channel 2
+#define PCAN_ISABUS3 0x23U // PCAN-ISA interface, channel 3
+#define PCAN_ISABUS4 0x24U // PCAN-ISA interface, channel 4
+#define PCAN_ISABUS5 0x25U // PCAN-ISA interface, channel 5
+#define PCAN_ISABUS6 0x26U // PCAN-ISA interface, channel 6
+#define PCAN_ISABUS7 0x27U // PCAN-ISA interface, channel 7
+#define PCAN_ISABUS8 0x28U // PCAN-ISA interface, channel 8
+
+#define PCAN_DNGBUS1 0x31U // PCAN-Dongle/LPT interface, channel 1
+
+#define PCAN_PCIBUS1 0x41U // PCAN-PCI interface, channel 1
+#define PCAN_PCIBUS2 0x42U // PCAN-PCI interface, channel 2
+#define PCAN_PCIBUS3 0x43U // PCAN-PCI interface, channel 3
+#define PCAN_PCIBUS4 0x44U // PCAN-PCI interface, channel 4
+#define PCAN_PCIBUS5 0x45U // PCAN-PCI interface, channel 5
+#define PCAN_PCIBUS6 0x46U // PCAN-PCI interface, channel 6
+#define PCAN_PCIBUS7 0x47U // PCAN-PCI interface, channel 7
+#define PCAN_PCIBUS8 0x48U // PCAN-PCI interface, channel 8
+#define PCAN_PCIBUS9 0x409U // PCAN-PCI interface, channel 9
+#define PCAN_PCIBUS10 0x40AU // PCAN-PCI interface, channel 10
+#define PCAN_PCIBUS11 0x40BU // PCAN-PCI interface, channel 11
+#define PCAN_PCIBUS12 0x40CU // PCAN-PCI interface, channel 12
+#define PCAN_PCIBUS13 0x40DU // PCAN-PCI interface, channel 13
+#define PCAN_PCIBUS14 0x40EU // PCAN-PCI interface, channel 14
+#define PCAN_PCIBUS15 0x40FU // PCAN-PCI interface, channel 15
+#define PCAN_PCIBUS16 0x410U // PCAN-PCI interface, channel 16
+
+#define PCAN_USBBUS1 0x51U // PCAN-USB interface, channel 1
+#define PCAN_USBBUS2 0x52U // PCAN-USB interface, channel 2
+#define PCAN_USBBUS3 0x53U // PCAN-USB interface, channel 3
+#define PCAN_USBBUS4 0x54U // PCAN-USB interface, channel 4
+#define PCAN_USBBUS5 0x55U // PCAN-USB interface, channel 5
+#define PCAN_USBBUS6 0x56U // PCAN-USB interface, channel 6
+#define PCAN_USBBUS7 0x57U // PCAN-USB interface, channel 7
+#define PCAN_USBBUS8 0x58U // PCAN-USB interface, channel 8
+#define PCAN_USBBUS9 0x509U // PCAN-USB interface, channel 9
+#define PCAN_USBBUS10 0x50AU // PCAN-USB interface, channel 10
+#define PCAN_USBBUS11 0x50BU // PCAN-USB interface, channel 11
+#define PCAN_USBBUS12 0x50CU // PCAN-USB interface, channel 12
+#define PCAN_USBBUS13 0x50DU // PCAN-USB interface, channel 13
+#define PCAN_USBBUS14 0x50EU // PCAN-USB interface, channel 14
+#define PCAN_USBBUS15 0x50FU // PCAN-USB interface, channel 15
+#define PCAN_USBBUS16 0x510U // PCAN-USB interface, channel 16
+
+#define PCAN_PCCBUS1 0x61U // PCAN-PC Card interface, channel 1
+#define PCAN_PCCBUS2 0x62U // PCAN-PC Card interface, channel 2
+
+#define PCAN_LANBUS1 0x801U // PCAN-LAN interface, channel 1
+#define PCAN_LANBUS2 0x802U // PCAN-LAN interface, channel 2
+#define PCAN_LANBUS3 0x803U // PCAN-LAN interface, channel 3
+#define PCAN_LANBUS4 0x804U // PCAN-LAN interface, channel 4
+#define PCAN_LANBUS5 0x805U // PCAN-LAN interface, channel 5
+#define PCAN_LANBUS6 0x806U // PCAN-LAN interface, channel 6
+#define PCAN_LANBUS7 0x807U // PCAN-LAN interface, channel 7
+#define PCAN_LANBUS8 0x808U // PCAN-LAN interface, channel 8
+#define PCAN_LANBUS9 0x809U // PCAN-LAN interface, channel 9
+#define PCAN_LANBUS10 0x80AU // PCAN-LAN interface, channel 10
+#define PCAN_LANBUS11 0x80BU // PCAN-LAN interface, channel 11
+#define PCAN_LANBUS12 0x80CU // PCAN-LAN interface, channel 12
+#define PCAN_LANBUS13 0x80DU // PCAN-LAN interface, channel 13
+#define PCAN_LANBUS14 0x80EU // PCAN-LAN interface, channel 14
+#define PCAN_LANBUS15 0x80FU // PCAN-LAN interface, channel 15
+#define PCAN_LANBUS16 0x810U // PCAN-LAN interface, channel 16
// Represent the PCAN error and status codes
-#define PCAN_ERROR_OK 0x00000 // No error
-#define PCAN_ERROR_XMTFULL 0x00001 // Transmit buffer in CAN controller is full
-#define PCAN_ERROR_OVERRUN 0x00002 // CAN controller was read too late
-#define PCAN_ERROR_BUSLIGHT 0x00004 // Bus error: an error counter reached the 'light' limit
-#define PCAN_ERROR_BUSHEAVY 0x00008 // Bus error: an error counter reached the 'heavy' limit
-#define PCAN_ERROR_BUSOFF 0x00010 // Bus error: the CAN controller is in bus-off state
-#define PCAN_ERROR_ANYBUSERR (PCAN_ERROR_BUSLIGHT | PCAN_ERROR_BUSHEAVY | PCAN_ERROR_BUSOFF) // Mask for all bus errors
-#define PCAN_ERROR_QRCVEMPTY 0x00020 // Receive queue is empty
-#define PCAN_ERROR_QOVERRUN 0x00040 // Receive queue was read too late
-#define PCAN_ERROR_QXMTFULL 0x00080 // Transmit queue is full
-#define PCAN_ERROR_REGTEST 0x00100 // Test of the CAN controller hardware registers failed (no hardware found)
-#define PCAN_ERROR_NODRIVER 0x00200 // Driver not loaded
-#define PCAN_ERROR_HWINUSE 0x00400 // Hardware already in use by a Net
-#define PCAN_ERROR_NETINUSE 0x00800 // A Client is already connected to the Net
-#define PCAN_ERROR_ILLHW 0x01400 // Hardware handle is invalid
-#define PCAN_ERROR_ILLNET 0x01800 // Net handle is invalid
-#define PCAN_ERROR_ILLCLIENT 0x01C00 // Client handle is invalid
+#define PCAN_ERROR_OK 0x00000U // No error
+#define PCAN_ERROR_XMTFULL 0x00001U // Transmit buffer in CAN controller is full
+#define PCAN_ERROR_OVERRUN 0x00002U // CAN controller was read too late
+#define PCAN_ERROR_BUSLIGHT 0x00004U // Bus error: an error counter reached the 'light' limit
+#define PCAN_ERROR_BUSHEAVY 0x00008U // Bus error: an error counter reached the 'heavy' limit
+#define PCAN_ERROR_BUSWARNING PCAN_ERROR_BUSHEAVY // Bus error: an error counter reached the 'warning' limit
+#define PCAN_ERROR_BUSPASSIVE 0x40000U // Bus error: the CAN controller is error passive
+#define PCAN_ERROR_BUSOFF 0x00010U // Bus error: the CAN controller is in bus-off state
+#define PCAN_ERROR_ANYBUSERR (PCAN_ERROR_BUSWARNING | PCAN_ERROR_BUSLIGHT | PCAN_ERROR_BUSHEAVY | PCAN_ERROR_BUSOFF | PCAN_ERROR_BUSPASSIVE) // Mask for all bus errors
+#define PCAN_ERROR_QRCVEMPTY 0x00020U // Receive queue is empty
+#define PCAN_ERROR_QOVERRUN 0x00040U // Receive queue was read too late
+#define PCAN_ERROR_QXMTFULL 0x00080U // Transmit queue is full
+#define PCAN_ERROR_REGTEST 0x00100U // Test of the CAN controller hardware registers failed (no hardware found)
+#define PCAN_ERROR_NODRIVER 0x00200U // Driver not loaded
+#define PCAN_ERROR_HWINUSE 0x00400U // Hardware already in use by a Net
+#define PCAN_ERROR_NETINUSE 0x00800U // A Client is already connected to the Net
+#define PCAN_ERROR_ILLHW 0x01400U // Hardware handle is invalid
+#define PCAN_ERROR_ILLNET 0x01800U // Net handle is invalid
+#define PCAN_ERROR_ILLCLIENT 0x01C00U // Client handle is invalid
#define PCAN_ERROR_ILLHANDLE (PCAN_ERROR_ILLHW | PCAN_ERROR_ILLNET | PCAN_ERROR_ILLCLIENT) // Mask for all handle errors
-#define PCAN_ERROR_RESOURCE 0x02000 // Resource (FIFO, Client, timeout) cannot be created
-#define PCAN_ERROR_ILLPARAMTYPE 0x04000 // Invalid parameter
-#define PCAN_ERROR_ILLPARAMVAL 0x08000 // Invalid parameter value
-#define PCAN_ERROR_UNKNOWN 0x10000 // Unknow error
-#define PCAN_ERROR_ILLDATA 0x20000 // Invalid data, function, or action
-#define PCAN_ERROR_INITIALIZE 0x40000 // Channel is not initialized
-#define PCAN_ERROR_ILLOPERATION 0x80000 // Invalid operation
+#define PCAN_ERROR_RESOURCE 0x02000U // Resource (FIFO, Client, timeout) cannot be created
+#define PCAN_ERROR_ILLPARAMTYPE 0x04000U // Invalid parameter
+#define PCAN_ERROR_ILLPARAMVAL 0x08000U // Invalid parameter value
+#define PCAN_ERROR_UNKNOWN 0x10000U // Unknown error
+#define PCAN_ERROR_ILLDATA 0x20000U // Invalid data, function, or action
+#define PCAN_ERROR_CAUTION 0x2000000U // An operation was successfully carried out, however, irregularities were registered
+#define PCAN_ERROR_INITIALIZE 0x4000000U // Channel is not initialized [Value was changed from 0x40000 to 0x4000000]
+#define PCAN_ERROR_ILLOPERATION 0x8000000U // Invalid operation [Value was changed from 0x80000 to 0x8000000]
// PCAN devices
-#define PCAN_NONE 0x00 // Undefined, unknown or not selected PCAN device value
-#define PCAN_PEAKCAN 0x01 // PCAN Non-Plug&Play devices. NOT USED WITHIN PCAN-Basic API
-#define PCAN_ISA 0x02 // PCAN-ISA, PCAN-PC/104, and PCAN-PC/104-Plus
-#define PCAN_DNG 0x03 // PCAN-Dongle
-#define PCAN_PCI 0x04 // PCAN-PCI, PCAN-cPCI, PCAN-miniPCI, and PCAN-PCI Express
-#define PCAN_USB 0x05 // PCAN-USB and PCAN-USB Pro
-#define PCAN_PCC 0x06 // PCAN-PC Card
+#define PCAN_NONE 0x00U // Undefined, unknown or not selected PCAN device value
+#define PCAN_PEAKCAN 0x01U // PCAN Non-Plug&Play devices. NOT USED WITHIN PCAN-Basic API
+#define PCAN_ISA 0x02U // PCAN-ISA, PCAN-PC/104, and PCAN-PC/104-Plus
+#define PCAN_DNG 0x03U // PCAN-Dongle
+#define PCAN_PCI 0x04U // PCAN-PCI, PCAN-cPCI, PCAN-miniPCI, and PCAN-PCI Express
+#define PCAN_USB 0x05U // PCAN-USB and PCAN-USB Pro
+#define PCAN_PCC 0x06U // PCAN-PC Card
// PCAN parameters
-#define PCAN_DEVICE_NUMBER 0x01 // PCAN-USB device number parameter
-#define PCAN_5VOLTS_POWER 0x02 // PCAN-PC Card 5-Volt power parameter
-#define PCAN_RECEIVE_EVENT 0x03 // PCAN receive event handler parameter
-#define PCAN_MESSAGE_FILTER 0x04 // PCAN message filter parameter
-#define PCAN_API_VERSION 0x05 // PCAN-Basic API version parameter
-#define PCAN_CHANNEL_VERSION 0x06 // PCAN device channel version parameter
-#define PCAN_BUSOFF_AUTORESET 0x07 // PCAN Reset-On-Busoff parameter
-#define PCAN_LISTEN_ONLY 0x08 // PCAN Listen-Only parameter
-#define PCAN_LOG_LOCATION 0x09 // Directory path for log files
-#define PCAN_LOG_STATUS 0x0A // Debug-Log activation status
-#define PCAN_LOG_CONFIGURE 0x0B // Configuration of the debugged information (LOG_FUNCTION_***)
-#define PCAN_LOG_TEXT 0x0C // Custom insertion of text into the log file
-#define PCAN_CHANNEL_CONDITION 0x0D // Availability status of a PCAN-Channel
-#define PCAN_HARDWARE_NAME 0x0E // PCAN hardware name parameter
-#define PCAN_RECEIVE_STATUS 0x0F // Message reception status of a PCAN-Channel
-#define PCAN_CONTROLLER_NUMBER 0x10 // CAN-Controller number of a PCAN-Channel
-#define PCAN_TRACE_LOCATION 0x11 // Directory path for PCAN trace files
-#define PCAN_TRACE_STATUS 0x12 // CAN tracing activation status
-#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)
+#define PCAN_DEVICE_NUMBER 0x01U // PCAN-USB device number parameter
+#define PCAN_5VOLTS_POWER 0x02U // PCAN-PC Card 5-Volt power parameter
+#define PCAN_RECEIVE_EVENT 0x03U // PCAN receive event handler parameter
+#define PCAN_MESSAGE_FILTER 0x04U // PCAN message filter parameter
+#define PCAN_API_VERSION 0x05U // PCAN-Basic API version parameter
+#define PCAN_CHANNEL_VERSION 0x06U // PCAN device channel version parameter
+#define PCAN_BUSOFF_AUTORESET 0x07U // PCAN Reset-On-Busoff parameter
+#define PCAN_LISTEN_ONLY 0x08U // PCAN Listen-Only parameter
+#define PCAN_LOG_LOCATION 0x09U // Directory path for log files
+#define PCAN_LOG_STATUS 0x0AU // Debug-Log activation status
+#define PCAN_LOG_CONFIGURE 0x0BU // Configuration of the debugged information (LOG_FUNCTION_***)
+#define PCAN_LOG_TEXT 0x0CU // Custom insertion of text into the log file
+#define PCAN_CHANNEL_CONDITION 0x0DU // Availability status of a PCAN-Channel
+#define PCAN_HARDWARE_NAME 0x0EU // PCAN hardware name parameter
+#define PCAN_RECEIVE_STATUS 0x0FU // Message reception status of a PCAN-Channel
+#define PCAN_CONTROLLER_NUMBER 0x10U // CAN-Controller number of a PCAN-Channel
+#define PCAN_TRACE_LOCATION 0x11U // Directory path for PCAN trace files
+#define PCAN_TRACE_STATUS 0x12U // CAN tracing activation status
+#define PCAN_TRACE_SIZE 0x13U // Configuration of the maximum file size of a CAN trace
+#define PCAN_TRACE_CONFIGURE 0x14U // Configuration of the trace file storing mode (TRACE_FILE_***)
+#define PCAN_CHANNEL_IDENTIFYING 0x15U // Physical identification of a USB based PCAN-Channel by blinking its associated LED
+#define PCAN_CHANNEL_FEATURES 0x16U // Capabilities of a PCAN device (FEATURE_***)
+#define PCAN_BITRATE_ADAPTING 0x17U // Using of an existing bit rate (PCAN-View connected to a channel)
+#define PCAN_BITRATE_INFO 0x18U // Configured bit rate as Btr0Btr1 value
+#define PCAN_BITRATE_INFO_FD 0x19U // Configured bit rate as TPCANBitrateFD string
+#define PCAN_BUSSPEED_NOMINAL 0x1AU // Configured nominal CAN Bus speed as Bits per seconds
+#define PCAN_BUSSPEED_DATA 0x1BU // Configured CAN data speed as Bits per seconds
+#define PCAN_IP_ADDRESS 0x1CU // Remote address of a LAN channel as string in IPv4 format
+#define PCAN_LAN_SERVICE_STATUS 0x1DU // Status of the Virtual PCAN-Gateway Service
+
+#define FEATURE_FD_CAPABLE 0x01U // Device supports flexible data-rate (CAN-FD)
// PCAN parameter values
-#define PCAN_PARAMETER_OFF 0x00 // The PCAN parameter is not set (inactive)
-#define PCAN_PARAMETER_ON 0x01 // The PCAN parameter is set (active)
-#define PCAN_FILTER_CLOSE 0x00 // The PCAN filter is closed. No messages will be received
-#define PCAN_FILTER_OPEN 0x01 // The PCAN filter is fully opened. All messages will be received
-#define PCAN_FILTER_CUSTOM 0x02 // The PCAN filter is custom configured. Only registered messages will be received
-#define PCAN_CHANNEL_UNAVAILABLE 0x00 // The PCAN-Channel handle is illegal, or its associated hadware is not available
-#define PCAN_CHANNEL_AVAILABLE 0x01 // The PCAN-Channel handle is available to be connected (Plug&Play Hardware: it means furthermore that the hardware is plugged-in)
-#define PCAN_CHANNEL_OCCUPIED 0x02 // The PCAN-Channel handle is valid, and is already being used
-
-#define LOG_FUNCTION_DEFAULT 0x00 // Logs system exceptions / errors
-#define LOG_FUNCTION_ENTRY 0x01 // Logs the entries to the PCAN-Basic API functions
-#define LOG_FUNCTION_PARAMETERS 0x02 // Logs the parameters passed to the PCAN-Basic API functions
-#define LOG_FUNCTION_LEAVE 0x04 // Logs the exits from the PCAN-Basic API functions
-#define LOG_FUNCTION_WRITE 0x08 // Logs the CAN messages passed to the CAN_Write function
-#define LOG_FUNCTION_READ 0x10 // Logs the CAN messages received within the CAN_Read function
-#define LOG_FUNCTION_ALL 0xFFFF // Logs all possible information within the PCAN-Basic API functions
-
-#define TRACE_FILE_SINGLE 0x00 // A single file is written until it size reaches PAN_TRACE_SIZE
-#define TRACE_FILE_SEGMENTED 0x01 // Traced data is distributed in several files with size PAN_TRACE_SIZE
-#define TRACE_FILE_DATE 0x02 // Includes the date into the name of the trace file
-#define TRACE_FILE_TIME 0x04 // Includes the start time into the name of the trace file
-#define TRACE_FILE_OVERWRITE 0x80 // Causes the overwriting of available traces (same name)
+#define PCAN_PARAMETER_OFF 0x00U // The PCAN parameter is not set (inactive)
+#define PCAN_PARAMETER_ON 0x01U // The PCAN parameter is set (active)
+#define PCAN_FILTER_CLOSE 0x00U // The PCAN filter is closed. No messages will be received
+#define PCAN_FILTER_OPEN 0x01U // The PCAN filter is fully opened. All messages will be received
+#define PCAN_FILTER_CUSTOM 0x02U // The PCAN filter is custom configured. Only registered messages will be received
+#define PCAN_CHANNEL_UNAVAILABLE 0x00U // The PCAN-Channel handle is illegal, or its associated hardware is not available
+#define PCAN_CHANNEL_AVAILABLE 0x01U // The PCAN-Channel handle is available to be connected (Plug&Play Hardware: it means furthermore that the hardware is plugged-in)
+#define PCAN_CHANNEL_OCCUPIED 0x02U // The PCAN-Channel handle is valid, and is already being used
+#define PCAN_CHANNEL_PCANVIEW (PCAN_CHANNEL_AVAILABLE | PCAN_CHANNEL_OCCUPIED) // The PCAN-Channel handle is already being used by a PCAN-View application, but is available to connect
+
+#define LOG_FUNCTION_DEFAULT 0x00U // Logs system exceptions / errors
+#define LOG_FUNCTION_ENTRY 0x01U // Logs the entries to the PCAN-Basic API functions
+#define LOG_FUNCTION_PARAMETERS 0x02U // Logs the parameters passed to the PCAN-Basic API functions
+#define LOG_FUNCTION_LEAVE 0x04U // Logs the exits from the PCAN-Basic API functions
+#define LOG_FUNCTION_WRITE 0x08U // Logs the CAN messages passed to the CAN_Write function
+#define LOG_FUNCTION_READ 0x10U // Logs the CAN messages received within the CAN_Read function
+#define LOG_FUNCTION_ALL 0xFFFFU // Logs all possible information within the PCAN-Basic API functions
+
+#define TRACE_FILE_SINGLE 0x00U // A single file is written until it size reaches PAN_TRACE_SIZE
+#define TRACE_FILE_SEGMENTED 0x01U // Traced data is distributed in several files with size PAN_TRACE_SIZE
+#define TRACE_FILE_DATE 0x02U // Includes the date into the name of the trace file
+#define TRACE_FILE_TIME 0x04U // Includes the start time into the name of the trace file
+#define TRACE_FILE_OVERWRITE 0x80U // Causes the overwriting of available traces (same name)
// PCAN message types
-#define PCAN_MESSAGE_STANDARD 0x00 // The PCAN message is a CAN Standard Frame (11-bit identifier)
-#define PCAN_MESSAGE_RTR 0x01 // The PCAN message is a CAN Remote-Transfer-Request Frame
-#define PCAN_MESSAGE_EXTENDED 0x02 // The PCAN message is a CAN Extended Frame (29-bit identifier)
-#define PCAN_MESSAGE_STATUS 0x80 // The PCAN message represents a PCAN status message
+#define PCAN_MESSAGE_STANDARD 0x00U // The PCAN message is a CAN Standard Frame (11-bit identifier)
+#define PCAN_MESSAGE_RTR 0x01U // The PCAN message is a CAN Remote-Transfer-Request Frame
+#define PCAN_MESSAGE_EXTENDED 0x02U // The PCAN message is a CAN Extended Frame (29-bit identifier)
+#define PCAN_MESSAGE_FD 0x04U // The PCAN message represents a FD frame in terms of CiA Specs
+#define PCAN_MESSAGE_BRS 0x08U // The PCAN message represents a FD bit rate switch (CAN data at a higher bit rate)
+#define PCAN_MESSAGE_ESI 0x10U // The PCAN message represents a FD error state indicator(CAN FD transmitter was error active)
+#define PCAN_MESSAGE_STATUS 0x80U // The PCAN message represents a PCAN status message
// Frame Type / Initialization Mode
#define PCAN_MODE_STANDARD PCAN_MESSAGE_STANDARD
@@ -211,29 +242,29 @@
// You can define your own Baud rate with the BTROBTR1 register.
// Take a look at www.peak-system.com for our free software "BAUDTOOL"
// to calculate the BTROBTR1 register for every baudrate and sample point.
-#define PCAN_BAUD_1M 0x0014 // 1 MBit/s
-#define PCAN_BAUD_800K 0x0016 // 800 kBit/s
-#define PCAN_BAUD_500K 0x001C // 500 kBit/s
-#define PCAN_BAUD_250K 0x011C // 250 kBit/s
-#define PCAN_BAUD_125K 0x031C // 125 kBit/s
-#define PCAN_BAUD_100K 0x432F // 100 kBit/s
-#define PCAN_BAUD_95K 0xC34E // 95,238 kBit/s
-#define PCAN_BAUD_83K 0x852B // 83,333 kBit/s
-#define PCAN_BAUD_50K 0x472F // 50 kBit/s
-#define PCAN_BAUD_47K 0x1414 // 47,619 kBit/s
-#define PCAN_BAUD_33K 0x8B2F // 33,333 kBit/s
-#define PCAN_BAUD_20K 0x532F // 20 kBit/s
-#define PCAN_BAUD_10K 0x672F // 10 kBit/s
-#define PCAN_BAUD_5K 0x7F7F // 5 kBit/s
-#define PCAN_BAUD_INVALID 0xFFFF // unknown or invalid baudrate
-
-#define PCAN_TYPE_ISA 0x01 // PCAN-ISA 82C200
-#define PCAN_TYPE_ISA_SJA 0x09 // PCAN-ISA SJA1000
-#define PCAN_TYPE_ISA_PHYTEC 0x04 // PHYTEC ISA
-#define PCAN_TYPE_DNG 0x02 // PCAN-Dongle 82C200
-#define PCAN_TYPE_DNG_EPP 0x03 // PCAN-Dongle EPP 82C200
-#define PCAN_TYPE_DNG_SJA 0x05 // PCAN-Dongle SJA1000
-#define PCAN_TYPE_DNG_SJA_EPP 0x06 // PCAN-Dongle EPP SJA1000
+#define PCAN_BAUD_1M 0x0014U // 1 MBit/s
+#define PCAN_BAUD_800K 0x0016U // 800 kBit/s
+#define PCAN_BAUD_500K 0x001CU // 500 kBit/s
+#define PCAN_BAUD_250K 0x011CU // 250 kBit/s
+#define PCAN_BAUD_125K 0x031CU // 125 kBit/s
+#define PCAN_BAUD_100K 0x432FU // 100 kBit/s
+#define PCAN_BAUD_95K 0xC34EU // 95,238 kBit/s
+#define PCAN_BAUD_83K 0x852BU // 83,333 kBit/s
+#define PCAN_BAUD_50K 0x472FU // 50 kBit/s
+#define PCAN_BAUD_47K 0x1414U // 47,619 kBit/s
+#define PCAN_BAUD_33K 0x8B2FU // 33,333 kBit/s
+#define PCAN_BAUD_20K 0x532FU // 20 kBit/s
+#define PCAN_BAUD_10K 0x672FU // 10 kBit/s
+#define PCAN_BAUD_5K 0x7F7FU // 5 kBit/s
+#define PCAN_BAUD_INVALID 0xFFFFU // unknown or invalid baudrate
+
+#define PCAN_TYPE_ISA 0x01U // PCAN-ISA 82C200
+#define PCAN_TYPE_ISA_SJA 0x09U // PCAN-ISA SJA1000
+#define PCAN_TYPE_ISA_PHYTEC 0x04U // PHYTEC ISA
+#define PCAN_TYPE_DNG 0x02U // PCAN-Dongle 82C200
+#define PCAN_TYPE_DNG_EPP 0x03U // PCAN-Dongle EPP 82C200
+#define PCAN_TYPE_DNG_SJA 0x05U // PCAN-Dongle SJA1000
+#define PCAN_TYPE_DNG_SJA_EPP 0x06U // PCAN-Dongle EPP SJA1000
// Type definitions
#define TPCANHandle quint16 // Represents a PCAN hardware channel handle
@@ -244,6 +275,8 @@
#define TPCANType quint8 // Represents the type of PCAN hardware to be initialized
#define TPCANMode quint8 // Represents a PCAN filter mode
#define TPCANBaudrate quint16 // Represents a PCAN Baud rate register value
+#define TPCANBitrateFD char * // Represents a PCAN-FD bit rate string
+#define TPCANTimestampFD quint64 // Represents a timestamp of a received PCAN FD message
// Represents a PCAN message
typedef struct tagTPCANMsg
@@ -263,6 +296,14 @@ typedef struct tagTPCANTimestamp
quint16 micros; // Microseconds: 0..999
} TPCANTimestamp;
+// Represents a PCAN message from a FD capable hardware
+typedef struct tagTPCANMsgFD
+{
+ quint32 ID; // 11/29-bit message identifier
+ TPCANMessageType MSGTYPE; // Type of the message
+ quint8 DLC; // Data Length Code of the message (0..15)
+ quint8 DATA[64]; // Data of the message (DATA[0]..DATA[63])
+} TPCANMsgFD;
#define GENERATE_SYMBOL_VARIABLE(returnType, symbolName, ...) \
typedef returnType (DRV_CALLBACK_TYPE *fp_##symbolName)(__VA_ARGS__); \
@@ -274,11 +315,14 @@ typedef struct tagTPCANTimestamp
return false;
GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Initialize, TPCANHandle, TPCANBaudrate, TPCANType, quint32, quint16)
+GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_InitializeFD, TPCANHandle, TPCANBitrateFD)
GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Uninitialize, TPCANHandle)
GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Reset, TPCANHandle)
GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetStatus, TPCANHandle)
GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Read, TPCANHandle, TPCANMsg *, TPCANTimestamp *)
+GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_ReadFD, TPCANHandle, TPCANMsgFD *, TPCANTimestampFD *)
GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Write, TPCANHandle, TPCANMsg *)
+GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_WriteFD, TPCANHandle, TPCANMsgFD *)
GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_FilterMessages, TPCANHandle, quint32, quint32, TPCANMode)
GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetValue, TPCANHandle, TPCANParameter, void *, quint32)
GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_SetValue, TPCANHandle, TPCANParameter, void *, quint32)
@@ -293,11 +337,14 @@ inline bool resolveSymbols(QLibrary *pcanLibrary)
}
RESOLVE_SYMBOL(CAN_Initialize)
+ RESOLVE_SYMBOL(CAN_InitializeFD)
RESOLVE_SYMBOL(CAN_Uninitialize)
RESOLVE_SYMBOL(CAN_Reset)
RESOLVE_SYMBOL(CAN_GetStatus)
RESOLVE_SYMBOL(CAN_Read)
+ RESOLVE_SYMBOL(CAN_ReadFD)
RESOLVE_SYMBOL(CAN_Write)
+ RESOLVE_SYMBOL(CAN_WriteFD)
RESOLVE_SYMBOL(CAN_FilterMessages)
RESOLVE_SYMBOL(CAN_GetValue)
RESOLVE_SYMBOL(CAN_SetValue)
diff --git a/src/plugins/canbus/peakcan/peakcanbackend.cpp b/src/plugins/canbus/peakcan/peakcanbackend.cpp
index 7e6fe43..f8932e9 100644
--- a/src/plugins/canbus/peakcan/peakcanbackend.cpp
+++ b/src/plugins/canbus/peakcan/peakcanbackend.cpp
@@ -120,7 +120,7 @@ QList<QCanBusDeviceInfo> PeakCanBackend::interfaces()
QList<QCanBusDeviceInfo> result;
for (int i = 0; pcanChannels[i].index != PCAN_NONEBUS; ++i) {
- int value = 0;
+ uint value = 0;
const TPCANHandle index = pcanChannels[i].index;
const TPCANStatus stat = ::CAN_GetValue(index, PCAN_CHANNEL_CONDITION,
&value, sizeof(value));
@@ -270,14 +270,66 @@ static TPCANBaudrate bitrateCodeFromBitrate(int bitrate)
return where != endtable ? where->code : PCAN_BAUD_INVALID;
}
+static QByteArray nominalBitrateString(int nominalBitrate)
+{
+ switch (nominalBitrate) {
+ case 125000:
+ return "f_clock=80000000, nom_brp=40, nom_tseg1=12, nom_tseg2=3, nom_sjw=1";
+ case 250000:
+ return "f_clock=80000000, nom_brp=20, nom_tseg1=12, nom_tseg2=3, nom_sjw=1";
+ case 500000:
+ return "f_clock=80000000, nom_brp=10, nom_tseg1=12, nom_tseg2=3, nom_sjw=1";
+ case 1000000:
+ return "f_clock=80000000, nom_brp=10, nom_tseg1=5, nom_tseg2=2, nom_sjw=1";
+ default:
+ return QByteArray();
+ }
+}
+
+static QByteArray dataBitrateString(int dataBitrate)
+{
+ switch (dataBitrate) {
+ case 2000000:
+ return ", data_brp=4, data_tseg1=7, data_tseg2=2, data_sjw=1";
+ case 4000000:
+ return ", data_brp=2, data_tseg1=7, data_tseg2=2, data_sjw=1";
+ case 8000000:
+ return ", data_brp=1, data_tseg1=7, data_tseg2=2, data_sjw=1";
+ case 10000000:
+ return ", data_brp=1, data_tseg1=5, data_tseg2=2, data_sjw=1";
+ default:
+ return QByteArray();
+ }
+}
+
+static QByteArray bitrateStringFromBitrate(int nominalBitrate, int dataBitrate)
+{
+ QByteArray result = nominalBitrateString(nominalBitrate);
+
+ if (result.isEmpty())
+ return QByteArray();
+
+ result += dataBitrateString(dataBitrate);
+
+ return result;
+}
+
bool PeakCanBackendPrivate::open()
{
Q_Q(PeakCanBackend);
- const int bitrate = q->configurationParameter(QCanBusDevice::BitRateKey).toInt();
- const TPCANBaudrate bitrateCode = bitrateCodeFromBitrate(bitrate);
+ const int nominalBitrate = q->configurationParameter(QCanBusDevice::BitRateKey).toInt();
+ TPCANStatus st = PCAN_ERROR_OK;
+
+ if (isFlexibleDatarateEnabled) {
+ const int dataBitrate = q->configurationParameter(QCanBusDevice::DataBitRateKey).toInt();
+ const QByteArray bitrateStr = bitrateStringFromBitrate(nominalBitrate, dataBitrate);
+ st = ::CAN_InitializeFD(channelIndex, const_cast<char *>(bitrateStr.data()));
+ } else {
+ const TPCANBaudrate bitrateCode = bitrateCodeFromBitrate(nominalBitrate);
+ st = ::CAN_Initialize(channelIndex, bitrateCode, 0, 0, 0);
+ }
- const TPCANStatus st = ::CAN_Initialize(channelIndex, bitrateCode, 0, 0, 0);
if (Q_UNLIKELY(st != PCAN_ERROR_OK)) {
q->setError(systemErrorString(st), QCanBusDevice::ConnectionError);
return false;
@@ -355,6 +407,18 @@ bool PeakCanBackendPrivate::setConfigurationParameter(int key, const QVariant &v
switch (key) {
case QCanBusDevice::BitRateKey:
return verifyBitRate(value.toInt());
+ case QCanBusDevice::CanFdKey:
+ isFlexibleDatarateEnabled = value.toBool();
+ return true;
+ case QCanBusDevice::DataBitRateKey: {
+ const int dataBitrate = value.toInt();
+ if (Q_UNLIKELY(dataBitrateString(dataBitrate).isEmpty())) {
+ q->setError(PeakCanBackend::tr("Unsupported data bitrate value: %1.").arg(dataBitrate),
+ QCanBusDevice::ConfigurationError);
+ return false;
+ }
+ return true;
+ }
default:
q->setError(PeakCanBackend::tr("Unsupported configuration key: %1").arg(key),
QCanBusDevice::ConfigurationError);
@@ -386,6 +450,81 @@ QString PeakCanBackendPrivate::systemErrorString(TPCANStatus errorCode)
return QString::fromLatin1(buffer);
}
+enum CanFrameDlc {
+ Dlc00 = 0,
+ Dlc01 = 1,
+ Dlc02 = 2,
+ Dlc03 = 3,
+ Dlc04 = 4,
+ Dlc05 = 5,
+ Dlc06 = 6,
+ Dlc07 = 7,
+ Dlc08 = 8,
+ Dlc12 = 9,
+ Dlc16 = 10,
+ Dlc20 = 11,
+ Dlc24 = 12,
+ Dlc32 = 13,
+ Dlc48 = 14,
+ Dlc64 = 15
+};
+
+static CanFrameDlc sizeToDlc(int size)
+{
+ switch (size) {
+ case 12:
+ return Dlc12;
+ case 16:
+ return Dlc16;
+ case 20:
+ return Dlc20;
+ case 24:
+ return Dlc24;
+ case 32:
+ return Dlc32;
+ case 48:
+ return Dlc48;
+ case 64:
+ return Dlc64;
+ default:
+ if (size >= 0 && size <= 8)
+ return static_cast<CanFrameDlc>(size);
+
+ return Dlc00;
+ }
+}
+
+static int dlcToSize(CanFrameDlc dlc)
+{
+ switch (dlc) {
+ case Dlc00:
+ case Dlc01:
+ case Dlc02:
+ case Dlc03:
+ case Dlc04:
+ case Dlc05:
+ case Dlc06:
+ case Dlc07:
+ case Dlc08:
+ return static_cast<int>(dlc);
+ case Dlc12:
+ return 12;
+ case Dlc16:
+ return 16;
+ case Dlc20:
+ return 20;
+ case Dlc24:
+ return 24;
+ case Dlc32:
+ return 32;
+ case Dlc48:
+ return 48;
+ case Dlc64:
+ return 64;
+ }
+ return 0;
+}
+
void PeakCanBackendPrivate::startWrite()
{
Q_Q(PeakCanBackend);
@@ -397,20 +536,46 @@ void PeakCanBackendPrivate::startWrite()
const QCanBusFrame frame = q->dequeueOutgoingFrame();
const QByteArray payload = frame.payload();
+ TPCANStatus st = PCAN_ERROR_OK;
- TPCANMsg message;
- ::memset(&message, 0, sizeof(message));
+ if (isFlexibleDatarateEnabled) {
+ const int size = payload.size();
+ TPCANMsgFD message;
+ ::memset(&message, 0, sizeof(message));
+ message.ID = frame.frameId();
+ message.DLC = sizeToDlc(size);
+ message.MSGTYPE = frame.hasExtendedFrameFormat() ? PCAN_MESSAGE_EXTENDED
+ : PCAN_MESSAGE_STANDARD;
+
+ if (frame.hasFlexibleDataRateFormat())
+ message.MSGTYPE |= PCAN_MESSAGE_FD;
+ if (frame.hasBitrateSwitch())
+ message.MSGTYPE |= PCAN_MESSAGE_BRS;
+
+ if (frame.frameType() == QCanBusFrame::RemoteRequestFrame)
+ message.MSGTYPE |= PCAN_MESSAGE_RTR; // we do not care about the payload
+ else
+ ::memcpy(message.DATA, payload.constData(), sizeof(message.DATA));
+ st = ::CAN_WriteFD(channelIndex, &message);
+ } else if (frame.hasFlexibleDataRateFormat()) {
+ q->setError(PeakCanBackend::tr("Cannot send CAN FD frame format as CAN FD is not enabled."),
+ QCanBusDevice::WriteError);
+ } else {
+ TPCANMsg message;
+ ::memset(&message, 0, sizeof(message));
- message.ID = frame.frameId();
- message.LEN = static_cast<quint8>(payload.size());
- message.MSGTYPE = frame.hasExtendedFrameFormat() ? PCAN_MESSAGE_EXTENDED : PCAN_MESSAGE_STANDARD;
+ message.ID = frame.frameId();
+ message.LEN = static_cast<quint8>(payload.size());
+ message.MSGTYPE = frame.hasExtendedFrameFormat() ? PCAN_MESSAGE_EXTENDED
+ : PCAN_MESSAGE_STANDARD;
- if (frame.frameType() == QCanBusFrame::RemoteRequestFrame)
- message.MSGTYPE |= PCAN_MESSAGE_RTR; // we do not care about the payload
- else
- ::memcpy(message.DATA, payload.constData(), sizeof(message.DATA));
+ if (frame.frameType() == QCanBusFrame::RemoteRequestFrame)
+ message.MSGTYPE |= PCAN_MESSAGE_RTR; // we do not care about the payload
+ else
+ ::memcpy(message.DATA, payload.constData(), sizeof(message.DATA));
+ st = ::CAN_Write(channelIndex, &message);
+ }
- const TPCANStatus st = ::CAN_Write(channelIndex, &message);
if (Q_UNLIKELY(st != PCAN_ERROR_OK))
q->setError(systemErrorString(st), QCanBusDevice::WriteError);
else
@@ -427,31 +592,66 @@ void PeakCanBackendPrivate::startRead()
QVector<QCanBusFrame> newFrames;
for (;;) {
- TPCANMsg message;
- ::memset(&message, 0, sizeof(message));
- TPCANTimestamp timestamp;
- ::memset(&timestamp, 0, sizeof(timestamp));
-
- const TPCANStatus st = ::CAN_Read(channelIndex, &message, &timestamp);
- if (st != PCAN_ERROR_OK) {
- if (Q_UNLIKELY(st != PCAN_ERROR_QRCVEMPTY))
- q->setError(systemErrorString(st), QCanBusDevice::ReadError);
- break;
- }
+ if (isFlexibleDatarateEnabled) {
+ TPCANMsgFD message;
+ ::memset(&message, 0, sizeof(message));
+ TPCANTimestampFD timestamp;
+ ::memset(&timestamp, 0, sizeof(timestamp));
+
+ const TPCANStatus st = ::CAN_ReadFD(channelIndex, &message, &timestamp);
+ if (st != PCAN_ERROR_OK) {
+ if (Q_UNLIKELY(st != PCAN_ERROR_QRCVEMPTY))
+ q->setError(systemErrorString(st), QCanBusDevice::ReadError);
+ break;
+ }
- // Filter out PCAN status frames, to avoid turning them
- // into QCanBusFrame::DataFrames with random canId
- if (Q_UNLIKELY(message.MSGTYPE & PCAN_MESSAGE_STATUS))
- continue;
+ // Filter out PCAN status frames, to avoid turning them
+ // into QCanBusFrame::DataFrames with random canId
+ if (Q_UNLIKELY(message.MSGTYPE & PCAN_MESSAGE_STATUS))
+ continue;
- QCanBusFrame frame(message.ID, QByteArray(reinterpret_cast<const char *>(message.DATA), int(message.LEN)));
- const quint64 millis = timestamp.millis + Q_UINT64_C(0xFFFFFFFF) * timestamp.millis_overflow;
- const quint64 micros = Q_UINT64_C(1000) * millis + timestamp.micros;
- frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(static_cast<qint64>(micros)));
- frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED);
- frame.setFrameType((message.MSGTYPE & PCAN_MESSAGE_RTR) ? QCanBusFrame::RemoteRequestFrame : QCanBusFrame::DataFrame);
+ const int size = dlcToSize(static_cast<CanFrameDlc>(message.DLC));
+ QCanBusFrame frame(message.ID,
+ QByteArray(reinterpret_cast<const char *>(message.DATA), size));
+ frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(static_cast<qint64>(timestamp)));
+ frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED);
+ frame.setFrameType((message.MSGTYPE & PCAN_MESSAGE_RTR)
+ ? QCanBusFrame::RemoteRequestFrame : QCanBusFrame::DataFrame);
+ frame.setFlexibleDataRateFormat(message.MSGTYPE & PCAN_MESSAGE_FD);
+ frame.setBitrateSwitch(message.MSGTYPE & PCAN_MESSAGE_BRS);
+ frame.setErrorStateIndicator(message.MSGTYPE & PCAN_MESSAGE_ESI);
+
+ newFrames.append(std::move(frame));
+ } else {
+ TPCANMsg message;
+ ::memset(&message, 0, sizeof(message));
+ TPCANTimestamp timestamp;
+ ::memset(&timestamp, 0, sizeof(timestamp));
+
+ const TPCANStatus st = ::CAN_Read(channelIndex, &message, &timestamp);
+ if (st != PCAN_ERROR_OK) {
+ if (Q_UNLIKELY(st != PCAN_ERROR_QRCVEMPTY))
+ q->setError(systemErrorString(st), QCanBusDevice::ReadError);
+ break;
+ }
- newFrames.append(std::move(frame));
+ // Filter out PCAN status frames, to avoid turning them
+ // into QCanBusFrame::DataFrames with random canId
+ if (Q_UNLIKELY(message.MSGTYPE & PCAN_MESSAGE_STATUS))
+ continue;
+
+ const int size = static_cast<int>(message.LEN);
+ QCanBusFrame frame(message.ID,
+ QByteArray(reinterpret_cast<const char *>(message.DATA), size));
+ const quint64 millis = timestamp.millis + Q_UINT64_C(0xFFFFFFFF) * timestamp.millis_overflow;
+ const quint64 micros = Q_UINT64_C(1000) * millis + timestamp.micros;
+ frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(static_cast<qint64>(micros)));
+ frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED);
+ frame.setFrameType((message.MSGTYPE & PCAN_MESSAGE_RTR)
+ ? QCanBusFrame::RemoteRequestFrame : QCanBusFrame::DataFrame);
+
+ newFrames.append(std::move(frame));
+ }
}
q->enqueueReceivedFrames(newFrames);
@@ -462,18 +662,23 @@ bool PeakCanBackendPrivate::verifyBitRate(int bitrate)
Q_Q(PeakCanBackend);
if (Q_UNLIKELY(isOpen)) {
- q->setError(PeakCanBackend::tr("Impossible to reconfigure bitrate for the opened device"),
+ q->setError(PeakCanBackend::tr("Cannot change bitrate for already opened device."),
QCanBusDevice::ConfigurationError);
return false;
}
- if (Q_UNLIKELY(bitrateCodeFromBitrate(bitrate) == PCAN_BAUD_INVALID)) {
- q->setError(PeakCanBackend::tr("Unsupported bitrate value"),
+ bool isValidBitrate = false;
+ if (q->configurationParameter(QCanBusDevice::CanFdKey).toBool())
+ isValidBitrate = !nominalBitrateString(bitrate).isEmpty();
+ else
+ isValidBitrate = bitrateCodeFromBitrate(bitrate) != PCAN_BAUD_INVALID;
+
+ if (Q_UNLIKELY(!isValidBitrate)) {
+ q->setError(PeakCanBackend::tr("Unsupported bitrate value: %1.").arg(bitrate),
QCanBusDevice::ConfigurationError);
- return false;
}
- return true;
+ return isValidBitrate;
}
PeakCanBackend::PeakCanBackend(const QString &name, QObject *parent)
@@ -559,12 +764,6 @@ bool PeakCanBackend::writeFrame(const QCanBusFrame &newData)
return false;
}
- // CAN FD frame format not implemented at this stage
- if (Q_UNLIKELY(newData.hasFlexibleDataRateFormat())) {
- setError(tr("CAN FD frame format not supported."), QCanBusDevice::WriteError);
- return false;
- }
-
enqueueOutgoingFrame(newData);
if (!d->writeNotifier->isActive())
diff --git a/src/plugins/canbus/peakcan/peakcanbackend_p.h b/src/plugins/canbus/peakcan/peakcanbackend_p.h
index 2dc8197..96d7ad3 100644
--- a/src/plugins/canbus/peakcan/peakcanbackend_p.h
+++ b/src/plugins/canbus/peakcan/peakcanbackend_p.h
@@ -82,6 +82,7 @@ public:
PeakCanBackend * const q_ptr;
+ bool isFlexibleDatarateEnabled = false;
bool isOpen = false;
TPCANHandle channelIndex = PCAN_NONEBUS;
QTimer *writeNotifier = nullptr;
diff --git a/src/plugins/canbus/virtualcan/main.cpp b/src/plugins/canbus/virtualcan/main.cpp
new file mode 100644
index 0000000..287b95a
--- /dev/null
+++ b/src/plugins/canbus/virtualcan/main.cpp
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "virtualcanbackend.h"
+
+#include <QtSerialBus/qcanbus.h>
+#include <QtSerialBus/qcanbusdevice.h>
+#include <QtSerialBus/qcanbusfactory.h>
+
+#include <QtCore/qloggingcategory.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_VIRTUALCAN, "qt.canbus.plugins.virtualcan")
+
+class VirtualCanBusPlugin : 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 (errorMessage != nullptr)
+ errorMessage->clear();
+
+ return VirtualCanBackend::interfaces();
+ }
+
+ QCanBusDevice *createDevice(const QString &interfaceName, QString *errorMessage) const override
+ {
+ if (errorMessage)
+ errorMessage->clear();
+
+ auto device = new VirtualCanBackend(interfaceName);
+ return device;
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "main.moc"
diff --git a/src/plugins/canbus/virtualcan/plugin.json b/src/plugins/canbus/virtualcan/plugin.json
new file mode 100644
index 0000000..4b13f6e
--- /dev/null
+++ b/src/plugins/canbus/virtualcan/plugin.json
@@ -0,0 +1,3 @@
+{
+ "Key": "virtualcan"
+}
diff --git a/src/plugins/canbus/virtualcan/virtualcan.pro b/src/plugins/canbus/virtualcan/virtualcan.pro
new file mode 100644
index 0000000..7b5504d
--- /dev/null
+++ b/src/plugins/canbus/virtualcan/virtualcan.pro
@@ -0,0 +1,17 @@
+TARGET = qtvirtualcanbus
+
+QT = core network serialbus
+
+HEADERS += \
+ virtualcanbackend.h
+
+SOURCES += \
+ main.cpp \
+ virtualcanbackend.cpp
+
+DISTFILES = plugin.json
+
+PLUGIN_TYPE = canbus
+PLUGIN_EXTENDS = serialbus
+PLUGIN_CLASS_NAME = VirtualCanBusPlugin
+load(qt_plugin)
diff --git a/src/plugins/canbus/virtualcan/virtualcanbackend.cpp b/src/plugins/canbus/virtualcan/virtualcanbackend.cpp
new file mode 100644
index 0000000..2ba13ce
--- /dev/null
+++ b/src/plugins/canbus/virtualcan/virtualcanbackend.cpp
@@ -0,0 +1,362 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "virtualcanbackend.h"
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qregularexpression.h>
+
+#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qtcpsocket.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_VIRTUALCAN)
+
+enum {
+ ServerDefaultTcpPort = 35468,
+ VirtualChannels = 2
+};
+
+static const char RemoteRequestFlag = 'R';
+static const char ExtendedFormatFlag = 'X';
+static const char FlexibleDataRateFlag = 'F';
+static const char BitRateSwitchFlag = 'B';
+static const char ErrorStateFlag = 'E';
+static const char LocalEchoFlag = 'L';
+
+VirtualCanServer::VirtualCanServer(QObject *parent)
+ : QObject(parent)
+{
+ qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN, "Server [%p]: constructed.", this);
+}
+
+VirtualCanServer::~VirtualCanServer()
+{
+ qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN, "Server [%p]: destructed.", this);
+}
+
+void VirtualCanServer::start(quint16 port)
+{
+ // If there is already a server object, return immediately
+ if (m_server) {
+ qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN, "Server [%p] is already running.", this);
+ return;
+ }
+
+ // Otherwise try to start a new server. If there is already
+ // another server listen on the specified port, give up.
+ m_server = new QTcpServer(this);
+ if (!m_server->listen(QHostAddress::LocalHost, port)) {
+ qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN,
+ "Server [%p] could not be started, port %d is already in use.", this, port);
+ m_server->deleteLater();
+ m_server = nullptr;
+ return;
+ }
+
+ // Server successfully started
+ connect(m_server, &QTcpServer::newConnection, this, &VirtualCanServer::connected);
+ qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN,
+ "Server [%p] started and listening on port %d.", this, port);
+ return;
+}
+
+void VirtualCanServer::connected()
+{
+ while (m_server->hasPendingConnections()) {
+ qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN, "Server [%p]: client connected.", this);
+ QTcpSocket *next = m_server->nextPendingConnection();
+ m_serverSockets.append(next);
+ connect(next, &QIODevice::readyRead, this, &VirtualCanServer::readyRead);
+ connect(next, &QTcpSocket::disconnected, this, &VirtualCanServer::disconnected);
+ }
+}
+
+void VirtualCanServer::disconnected()
+{
+ qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN, "Server [%p]: client disconnected.", this);
+
+ auto socket = qobject_cast<QTcpSocket *>(sender());
+ Q_ASSERT(socket);
+
+ m_serverSockets.removeOne(socket);
+ socket->deleteLater();
+}
+
+void VirtualCanServer::readyRead()
+{
+ auto readSocket = qobject_cast<QTcpSocket *>(sender());
+ Q_ASSERT(readSocket);
+
+ while (readSocket->canReadLine()) {
+ const QByteArray command = readSocket->readLine().trimmed();
+ qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN,
+ "Server [%p] received: '%s'.", this, command.constData());
+
+ if (command.startsWith("connect:")) {
+ const QVariant interfaces = readSocket->property("interfaces");
+ QStringList list = interfaces.toStringList();
+ list.append(command.mid(int(strlen("connect:"))));
+ readSocket->setProperty("interfaces", list);
+
+ } else if (command.startsWith("disconnect:")) {
+ const QVariant interfaces = readSocket->property("interfaces");
+ QStringList list = interfaces.toStringList();
+ list.removeAll(command.mid(int(strlen("disconnect:"))));
+ readSocket->setProperty("interfaces", list);
+
+ } else {
+ const QByteArrayList commandList = command.split(':');
+ Q_ASSERT(commandList.size() == 2);
+
+ for (QTcpSocket *writeSocket : qAsConst(m_serverSockets)) {
+ // Don't send the frame back to its origin
+ if (writeSocket == readSocket)
+ continue;
+
+ // Send frame to all clients registered to the same interface as sender
+ const QVariant property = writeSocket->property("interfaces");
+ if (!property.isValid())
+ continue;
+
+ const QStringList propertyList = property.toStringList();
+ if (propertyList.contains(commandList.first()))
+ writeSocket->write(commandList.last() + '\n');
+ }
+ }
+ }
+}
+
+Q_GLOBAL_STATIC(VirtualCanServer, g_server)
+
+VirtualCanBackend::VirtualCanBackend(const QString &interface, QObject *parent)
+ : QCanBusDevice(parent)
+{
+ m_url = QUrl(interface);
+ const QString canDevice = m_url.fileName();
+
+ const QRegularExpression re(QStringLiteral("can(\\d)"));
+ const QRegularExpressionMatch match = re.match(canDevice);
+
+ if (Q_UNLIKELY(!match.hasMatch())) {
+ qCWarning(QT_CANBUS_PLUGINS_VIRTUALCAN,
+ "Invalid interface '%ls'.", qUtf16Printable(interface));
+ setError(tr("Invalid interface '%1'.").arg(interface), QCanBusDevice::ConnectionError);
+ return;
+ }
+
+ const uint channel = match.captured(1).toUInt();
+ if (Q_UNLIKELY(channel >= VirtualChannels)) {
+ qCWarning(QT_CANBUS_PLUGINS_VIRTUALCAN,
+ "Invalid interface '%ls'.", qUtf16Printable(interface));
+ setError(tr("Invalid interface '%1'.").arg(interface), QCanBusDevice::ConnectionError);
+ return;
+ }
+
+ m_channel = channel;
+}
+
+VirtualCanBackend::~VirtualCanBackend()
+{
+ qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN, "Client [%p] socket destructed.", this);
+}
+
+bool VirtualCanBackend::open()
+{
+ setState(QCanBusDevice::ConnectingState);
+
+ const QString host = m_url.host();
+ const QHostAddress address = host.isEmpty() ? QHostAddress::LocalHost : QHostAddress(host);
+ const quint16 port = static_cast<quint16>(m_url.port(ServerDefaultTcpPort));
+
+ if (address.isLoopback())
+ g_server->start(port);
+
+ m_clientSocket = new QTcpSocket(this);
+ m_clientSocket->connectToHost(address, port, QIODevice::ReadWrite);
+ connect(m_clientSocket, &QAbstractSocket::connected, this, &VirtualCanBackend::clientConnected);
+ connect(m_clientSocket, &QAbstractSocket::disconnected, this, &VirtualCanBackend::clientDisconnected);
+ connect(m_clientSocket, &QIODevice::readyRead, this, &VirtualCanBackend::clientReadyRead);
+ qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN, "Client [%p] socket created.", this);
+ return true;
+}
+
+void VirtualCanBackend::close()
+{
+ setState(ClosingState);
+
+ m_clientSocket->write("disconnect:can" + QByteArray::number(m_channel) + '\n');
+}
+
+void VirtualCanBackend::setConfigurationParameter(int key, const QVariant &value)
+{
+ if (key == QCanBusDevice::ReceiveOwnKey || key == QCanBusDevice::CanFdKey)
+ QCanBusDevice::setConfigurationParameter(key, value);
+}
+
+/*
+ Protocol format: All data is in ASCII, one CAN message per line,
+ each line ends with line feed '\n'.
+
+ Format: "<CAN-Channel>:<Flags>#<CAN-ID>#<Data-Bytes>\n"
+ Example: "can0:XF#123#123456\n"
+
+ The first part is the destination CAN channel, "can0" or "can1",
+ followed by the flags list:
+
+ * R - Remote Request
+ * X - Extended Frame Format
+ * F - Flexible Data Rate Format
+ * B - Bitrate Switch
+ * E - Error State Indicator
+ * L - Local Echo
+
+ Afterwards the CAN-ID and the data follows, both separated by '#'.
+*/
+
+bool VirtualCanBackend::writeFrame(const QCanBusFrame &frame)
+{
+ if (Q_UNLIKELY(state() != ConnectedState)) {
+ qCWarning(QT_CANBUS_PLUGINS_VIRTUALCAN, "Error: Cannot write frame as client is not connected!");
+ return false;
+ }
+
+ bool canFdEnabled = configurationParameter(QCanBusDevice::CanFdKey).toBool();
+ if (Q_UNLIKELY(frame.hasFlexibleDataRateFormat() && !canFdEnabled)) {
+ qCWarning(QT_CANBUS_PLUGINS_VIRTUALCAN,
+ "Error: Cannot write CAN FD frame as CAN FD is not enabled!");
+ return false;
+ }
+
+ QByteArray flags;
+ if (frame.frameType() == QCanBusFrame::RemoteRequestFrame)
+ flags.append(RemoteRequestFlag);
+ if (frame.hasExtendedFrameFormat())
+ flags.append(ExtendedFormatFlag);
+ if (frame.hasFlexibleDataRateFormat())
+ flags.append(FlexibleDataRateFlag);
+ if (frame.hasBitrateSwitch())
+ flags.append(BitRateSwitchFlag);
+ if (frame.hasErrorStateIndicator())
+ flags.append(ErrorStateFlag);
+ if (frame.hasLocalEcho())
+ flags.append(LocalEchoFlag);
+ const QByteArray frameId = QByteArray::number(frame.frameId());
+ const QByteArray command = "can" + QByteArray::number(m_channel)
+ + ':' + frameId + '#' + flags + '#' + frame.payload().toHex() + '\n';
+ m_clientSocket->write(command);
+
+ if (configurationParameter(QCanBusDevice::ReceiveOwnKey).toBool()) {
+ const qint64 timeStamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
+ QCanBusFrame echoFrame = frame;
+ echoFrame.setLocalEcho(true);
+ echoFrame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(timeStamp * 1000));
+ enqueueReceivedFrames({echoFrame});
+ }
+
+ return true;
+}
+
+QString VirtualCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame)
+{
+ Q_UNUSED(errorFrame);
+ return QString();
+}
+
+QList<QCanBusDeviceInfo> VirtualCanBackend::interfaces()
+{
+ QList<QCanBusDeviceInfo> result;
+
+ for (int channel = 0; channel < VirtualChannels; ++channel) {
+ result.append(std::move(createDeviceInfo(
+ QStringLiteral("can%1").arg(channel), QString(),
+ QStringLiteral("Qt Virtual CAN bus"), channel,
+ true, true)));
+ }
+
+ return result;
+}
+
+void VirtualCanBackend::clientConnected()
+{
+ qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN, "Client [%p] socket connected.", this);
+ m_clientSocket->write("connect:can" + QByteArray::number(m_channel) + '\n');
+
+ setState(QCanBusDevice::ConnectedState);
+}
+
+void VirtualCanBackend::clientDisconnected()
+{
+ qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN, "Client [%p] socket disconnected.", this);
+
+ setState(UnconnectedState);
+}
+
+void VirtualCanBackend::clientReadyRead()
+{
+ while (m_clientSocket->canReadLine()) {
+ const QByteArray answer = m_clientSocket->readLine().trimmed();
+ qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN, "Client [%p] received: '%s'.",
+ this, answer.constData());
+
+ if (answer.startsWith("disconnect:can" + QByteArray::number(m_channel))) {
+ m_clientSocket->disconnectFromHost();
+ continue;
+ }
+
+ const QByteArrayList list = answer.split('#');
+ Q_ASSERT(list.size() == 3);
+
+ const quint32 id = list.at(0).toUInt();
+ const QByteArray flags = list.at(1);
+ const QByteArray data = QByteArray::fromHex(list.at(2));
+ const qint64 timeStamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
+ QCanBusFrame frame(id, data);
+ frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(timeStamp * 1000));
+ if (flags.contains(RemoteRequestFlag))
+ frame.setFrameType(QCanBusFrame::RemoteRequestFrame);
+ frame.setExtendedFrameFormat(flags.contains(ExtendedFormatFlag));
+ frame.setFlexibleDataRateFormat(flags.contains(FlexibleDataRateFlag));
+ frame.setBitrateSwitch(flags.contains(BitRateSwitchFlag));
+ frame.setErrorStateIndicator(flags.contains(ErrorStateFlag));
+ frame.setLocalEcho(flags.contains(LocalEchoFlag));
+ enqueueReceivedFrames({frame});
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/canbus/virtualcan/virtualcanbackend.h b/src/plugins/canbus/virtualcan/virtualcanbackend.h
new file mode 100644
index 0000000..c83b568
--- /dev/null
+++ b/src/plugins/canbus/virtualcan/virtualcanbackend.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 VIRTUALCANBACKEND_H
+#define VIRTUALCANBACKEND_H
+
+#include <QtSerialBus/qcanbusframe.h>
+#include <QtSerialBus/qcanbusdevice.h>
+#include <QtSerialBus/qcanbusdeviceinfo.h>
+
+#include <QtCore/qlist.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTcpServer;
+class QTcpSocket;
+
+class VirtualCanServer : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(VirtualCanServer)
+
+public:
+ explicit VirtualCanServer(QObject *parent = nullptr);
+ ~VirtualCanServer() override;
+
+ void start(quint16 port);
+
+private:
+ void connected();
+ void disconnected();
+ void readyRead();
+
+ QTcpServer *m_server = nullptr;
+ QList<QTcpSocket *> m_serverSockets;
+};
+
+class VirtualCanBackend : public QCanBusDevice
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(VirtualCanBackend)
+
+public:
+ explicit VirtualCanBackend(const QString &interface, QObject *parent = nullptr);
+ ~VirtualCanBackend() override;
+
+ bool open() override;
+ void close() override;
+
+ void setConfigurationParameter(int key, const QVariant &value) override;
+
+ bool writeFrame(const QCanBusFrame &frame) override;
+
+ QString interpretErrorFrame(const QCanBusFrame &errorFrame) override;
+
+ static QList<QCanBusDeviceInfo> interfaces();
+
+private:
+ void clientConnected();
+ void clientDisconnected();
+ void clientReadyRead();
+
+ QUrl m_url;
+ uint m_channel = 0;
+ QTcpSocket *m_clientSocket = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // VIRTUALCANBACKEND_H
diff --git a/src/serialbus/doc/src/peakcan.qdoc b/src/serialbus/doc/src/peakcan.qdoc
index 68d52fd..c8f52c9 100644
--- a/src/serialbus/doc/src/peakcan.qdoc
+++ b/src/serialbus/doc/src/peakcan.qdoc
@@ -34,7 +34,8 @@
\l{http://www.peak-system.com/}{PEAK-System} CAN adapters.
This plugin requires the PCAN device drivers and the PCAN-Basic library
- (pcanbasic.dll under Windows, libpcanbasic.so under Linux).
+ version 4.0.0 or higher. Supported platforms are Windows (pcanbasic.dll)
+ and Linux (libpcanbasic.so).
\section1 Creating CAN Bus Devices
diff --git a/src/serialbus/doc/src/qtcanbus-backends.qdoc b/src/serialbus/doc/src/qtcanbus-backends.qdoc
index 7942ace..e6b150f 100644
--- a/src/serialbus/doc/src/qtcanbus-backends.qdoc
+++ b/src/serialbus/doc/src/qtcanbus-backends.qdoc
@@ -81,6 +81,10 @@
\li Vector Informatik
\li \l {Using VectorCAN Plugin}{VectorCAN} (\c vectorcan)
\li CAN bus plugin using the Vector CAN adapters.
+ \row
+ \li Virtual CAN interface
+ \li \l {Using VirtualCAN Plugin}{VirtualCAN} (\c virtualcan)
+ \li CAN bus plugin using a virtual TCP/IP connection.
\endtable
\section1 Implementing a Custom CAN Plugin
diff --git a/src/serialbus/doc/src/qtserialbus-module-cpp.qdoc b/src/serialbus/doc/src/qtserialbus-module-cpp.qdoc
index 6280f6f..74123fc 100644
--- a/src/serialbus/doc/src/qtserialbus-module-cpp.qdoc
+++ b/src/serialbus/doc/src/qtserialbus-module-cpp.qdoc
@@ -40,7 +40,9 @@
For C++ projects include the header appropriate for the current use case,
for example applications using the CAN bus device may use
- \code #include <QCanBusDevice> \endcode
+ \code
+ #include <QCanBusDevice>
+ \endcode
The .pro file should have the \e serialbus keyword added
diff --git a/src/serialbus/doc/src/virtualcan.qdoc b/src/serialbus/doc/src/virtualcan.qdoc
new file mode 100644
index 0000000..3dc9f76
--- /dev/null
+++ b/src/serialbus/doc/src/virtualcan.qdoc
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Andre Hartmann <aha_1980@gmx.de>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+/*!
+ \page qtserialbus-virtualcan-overview.html
+ \title Using VirtualCAN Plugin
+
+ \brief Overview of how to use the VirtualCAN plugin.
+
+ The VirtualCAN plugin allows testing of CAN applications with a local
+ TCP/IP connection without CAN hardware. The TCP server is created,
+ when the first client calls createDevice(). The default TCP port is
+ 35468, which can be changed by giving the fully qualified URL to
+ createDevice(). Once the server is running, no further server is
+ started on the same system.
+
+ Afterwards, all clients send their CAN frames to the server, which
+ distributes them to the other clients.
+
+ \section1 Creating CAN Bus Devices
+
+ At first it is necessary to check that QCanBus provides the desired plugin:
+
+ \code
+ if (QCanBus::instance()->plugins().contains(QStringLiteral("virtualcan"))) {
+ // plugin available
+ }
+ \endcode
+
+ Where \e virtualcan is the plugin name.
+
+ Next, a connection to a specific interface can be established:
+
+ \code
+ QCanBusDevice *device = QCanBus::instance()->createDevice(
+ QStringLiteral("virtualcan"), QStringLiteral("can0"));
+ device->connectDevice();
+ \endcode
+
+ Where \e can0 is the active CAN channel name. The VirtualCAN plugin
+ provides two channels "can0" and "can1". Both can be used as CAN 2.0
+ or CAN FD channels. All applications connected to one of these channels
+ receive all messages that are sent to this channel.
+
+ To connect to a remote server, use the following fully qualified URL
+ as interface name:
+
+ \code
+ tcp://server:port/canX
+ \endcode
+
+ for example:
+
+ \code
+ tcp://192.168.1.2:35468/can0
+ \endcode
+
+ 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
+
+ VirtualCAN supports the following configurations that can be controlled through
+ \l {QCanBusDevice::}{setConfigurationParameter()}:
+
+ \table
+ \header
+ \li Configuration parameter key
+ \li Description
+ \row
+ \li QCanBusDevice::CanFdKey
+ \li Determines whether the virtual CAN bus operates in CAN FD mode or not.
+ This option is disabled by default.
+ \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. If this
+ option is enabled, the therefore received frames are marked with
+ QCanBusFrame::hasLocalEcho()
+ \endtable
+*/
diff --git a/src/serialbus/qcanbusdevice.cpp b/src/serialbus/qcanbusdevice.cpp
index 3d7e4f8..6dc8abd 100644
--- a/src/serialbus/qcanbusdevice.cpp
+++ b/src/serialbus/qcanbusdevice.cpp
@@ -405,7 +405,7 @@ QString QCanBusDevice::errorString() const
Returns the number of available frames. If no frames are available,
this function returns 0.
- \sa readFrame()
+ \sa clear(), readFrame(), readAllFrames()
*/
qint64 QCanBusDevice::framesAvailable() const
{
@@ -420,7 +420,7 @@ qint64 QCanBusDevice::framesAvailable() const
Therefore, if this function returns zero, that does not mean all CAN frames are
already written to the CAN bus.
- \sa writeFrame()
+ \sa clear(), writeFrame()
*/
qint64 QCanBusDevice::framesToWrite() const
{
@@ -428,6 +428,45 @@ qint64 QCanBusDevice::framesToWrite() const
}
/*!
+ \since 5.12
+ \enum QCanBusDevice::Direction
+
+ This enum describes possible data transmission directions.
+
+ \value Input Input direction.
+ \value Output Output direction.
+ \value AllDirections Both directions, input and output.
+*/
+
+/*!
+ \since 5.12
+ Clears the devices input or output buffers, depending on \a direction.
+
+ This function only operates on QCanBusDevice buffers. Frames that are
+ already written to the CAN driver or CAN hardware layer, or that are
+ not yet read from these layers, are not cleared by this function.
+
+ \note Clearing the output buffers is only possible for buffered devices.
+
+ \sa framesAvailable(), readFrame(), framesToWrite(), writeFrame(),
+*/
+void QCanBusDevice::clear(QCanBusDevice::Directions direction)
+{
+ Q_D(QCanBusDevice);
+
+ if (Q_UNLIKELY(d->state != ConnectedState))
+ return;
+
+ if (direction & Direction::Input) {
+ QMutexLocker(&d->incomingFramesGuard);
+ d->incomingFrames.clear();
+ }
+
+ if (direction & Direction::Output)
+ d->outgoingFrames.clear();
+}
+
+/*!
For buffered devices, this function waits until all buffered frames
have been written to the device and the \l framesWritten() signal has been emitted,
or until \a msecs milliseconds have passed. If \a msecs is -1,
@@ -574,7 +613,7 @@ bool QCanBusDevice::waitForFramesReceived(int msecs)
The queue operates according to the FIFO principle.
- \sa framesAvailable()
+ \sa clear(), framesAvailable(), readAllFrames()
*/
QCanBusFrame QCanBusDevice::readFrame()
{
@@ -592,6 +631,29 @@ QCanBusFrame QCanBusDevice::readFrame()
}
/*!
+ \since 5.12
+ Returns all \l{QCanBusFrame}s from the queue; otherwise returns
+ an empty QVector. The returned frames are removed from the queue.
+
+ The queue operates according to the FIFO principle.
+
+ \sa clear(), framesAvailable(), readFrame()
+*/
+QVector<QCanBusFrame> QCanBusDevice::readAllFrames()
+{
+ Q_D(QCanBusDevice);
+
+ if (Q_UNLIKELY(d->state != ConnectedState))
+ return QVector<QCanBusFrame>();
+
+ QMutexLocker locker(&d->incomingFramesGuard);
+
+ QVector<QCanBusFrame> result;
+ result.swap(d->incomingFrames);
+ return result;
+}
+
+/*!
\fn void QCanBusDevice::framesWritten(qint64 framesCount)
This signal is emitted every time a payload of frames has been
diff --git a/src/serialbus/qcanbusdevice.h b/src/serialbus/qcanbusdevice.h
index 482e9d0..a47fac4 100644
--- a/src/serialbus/qcanbusdevice.h
+++ b/src/serialbus/qcanbusdevice.h
@@ -105,9 +105,18 @@ public:
virtual bool writeFrame(const QCanBusFrame &frame) = 0;
QCanBusFrame readFrame();
+ QVector<QCanBusFrame> readAllFrames();
qint64 framesAvailable() const;
qint64 framesToWrite() const;
+ enum Direction {
+ Input = 1,
+ Output = 2,
+ AllDirections = Input | Output
+ };
+ Q_DECLARE_FLAGS(Directions, Direction)
+ void clear(Directions direction = Direction::AllDirections);
+
virtual bool waitForFramesWritten(int msecs);
virtual bool waitForFramesReceived(int msecs);
@@ -158,6 +167,7 @@ Q_DECLARE_TYPEINFO(QCanBusDevice::Filter, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(QCanBusDevice::Filter::FormatFilter, Q_PRIMITIVE_TYPE);
Q_DECLARE_OPERATORS_FOR_FLAGS(QCanBusDevice::Filter::FormatFilters)
+Q_DECLARE_OPERATORS_FOR_FLAGS(QCanBusDevice::Directions)
QT_END_NAMESPACE
diff --git a/src/tools/canbusutil/canbusutil.cpp b/src/tools/canbusutil/canbusutil.cpp
index 38433bb..a4e1a58 100644
--- a/src/tools/canbusutil/canbusutil.cpp
+++ b/src/tools/canbusutil/canbusutil.cpp
@@ -58,6 +58,12 @@ void CanBusUtil::setShowFdFlags(bool showFdFlags)
m_readTask->setShowFdFlags(showFdFlags);
}
+void CanBusUtil::setConfigurationParameter(QCanBusDevice::ConfigurationKey key,
+ const QVariant &value)
+{
+ m_configurationParameter[key] = value;
+}
+
bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, const QString &data)
{
if (!m_canBus) {
@@ -201,6 +207,11 @@ bool CanBusUtil::connectCanDevice()
m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << endl;
return false;
}
+
+ const auto constEnd = m_configurationParameter.constEnd();
+ for (auto i = m_configurationParameter.constBegin(); i != constEnd; ++i)
+ m_canDevice->setConfigurationParameter(i.key(), i.value());
+
connect(m_canDevice.data(), &QCanBusDevice::errorOccurred, m_readTask, &ReadTask::handleError);
if (!m_canDevice->connectDevice()) {
m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << endl;
diff --git a/src/tools/canbusutil/canbusutil.h b/src/tools/canbusutil/canbusutil.h
index e6432a3..19b7868 100644
--- a/src/tools/canbusutil/canbusutil.h
+++ b/src/tools/canbusutil/canbusutil.h
@@ -58,6 +58,7 @@ public:
void setShowTimeStamp(bool showTimeStamp);
void setShowFdFlags(bool showFdFlags);
+ void setConfigurationParameter(QCanBusDevice::ConfigurationKey key, const QVariant &value);
bool start(const QString &pluginName, const QString &deviceName, const QString &data = QString());
int printPlugins();
int printDevices(const QString &pluginName);
@@ -78,6 +79,8 @@ private:
QString m_data;
QScopedPointer<QCanBusDevice> m_canDevice;
ReadTask *m_readTask = nullptr;
+ using ConfigurationParameter = QHash<QCanBusDevice::ConfigurationKey, QVariant>;
+ ConfigurationParameter m_configurationParameter;
};
#endif // CANBUSUTIL_H
diff --git a/src/tools/canbusutil/main.cpp b/src/tools/canbusutil/main.cpp
index 6744f19..3a71192 100644
--- a/src/tools/canbusutil/main.cpp
+++ b/src/tools/canbusutil/main.cpp
@@ -102,6 +102,28 @@ int main(int argc, char *argv[])
CanBusUtil::tr("Show available CAN bus devices for the given plugin."));
parser.addOption(listDevicesOption);
+ const QCommandLineOption canFdOption({"f", "can-fd"},
+ CanBusUtil::tr("Enable CAN FD functionality when listening."));
+ parser.addOption(canFdOption);
+
+ const QCommandLineOption loopbackOption({"c", "local-loopback"},
+ CanBusUtil::tr("Transmits all sent frames to other local applications."));
+ parser.addOption(loopbackOption);
+
+ const QCommandLineOption receiveOwnOption({"o", "receive-own"},
+ CanBusUtil::tr("Receive each sent frame on successful transmission."));
+ parser.addOption(receiveOwnOption);
+
+ const QCommandLineOption bitrateOption({"b", "bitrate"},
+ CanBusUtil::tr("Set the CAN bus bitrate to the given value."),
+ QStringLiteral("bitrate"));
+ parser.addOption(bitrateOption);
+
+ const QCommandLineOption dataBitrateOption({"a", "data-bitrate"},
+ CanBusUtil::tr("Set the CAN FD data bitrate to the given value."),
+ QStringLiteral("bitrate"));
+ parser.addOption(dataBitrateOption);
+
parser.process(app);
if (parser.isSet(listOption))
@@ -109,6 +131,22 @@ int main(int argc, char *argv[])
QString data;
const QStringList args = parser.positionalArguments();
+
+ if (parser.isSet(canFdOption))
+ util.setConfigurationParameter(QCanBusDevice::CanFdKey, true);
+ if (parser.isSet(loopbackOption))
+ util.setConfigurationParameter(QCanBusDevice::LoopbackKey, true);
+ if (parser.isSet(receiveOwnOption))
+ util.setConfigurationParameter(QCanBusDevice::ReceiveOwnKey, true);
+ if (!parser.value(bitrateOption).isEmpty()) {
+ util.setConfigurationParameter(QCanBusDevice::BitRateKey,
+ parser.value(bitrateOption).toInt());
+ }
+ if (!parser.value(dataBitrateOption).isEmpty()) {
+ util.setConfigurationParameter(QCanBusDevice::DataBitRateKey,
+ parser.value(dataBitrateOption).toInt());
+ }
+
if (parser.isSet(listeningOption)) {
util.setShowTimeStamp(parser.isSet(showTimeStampOption));
util.setShowFdFlags(parser.isSet(showFdFlagsOption));
diff --git a/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp b/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp
index d0cf082..85d445f 100644
--- a/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp
+++ b/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp
@@ -143,6 +143,9 @@ private slots:
void conf();
void write();
void read();
+ void readAll();
+ void clearInputBuffer();
+ void clearOutputBuffer();
void error();
void cleanupTestCase();
void tst_filtering();
@@ -271,6 +274,61 @@ void tst_QCanBusDevice::read()
QVERIFY(frame2.isValid());
}
+void tst_QCanBusDevice::readAll()
+{
+ enum { FrameNumber = 10 };
+ device->disconnectDevice();
+ QVERIFY(device->connectDevice());
+ QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000);
+
+ for (int i = 0; i < FrameNumber; ++i)
+ device->triggerNewFrame();
+
+ const QVector<QCanBusFrame> frames = device->readAllFrames();
+ QCOMPARE(FrameNumber, frames.size());
+ QVERIFY(!device->framesAvailable());
+}
+
+void tst_QCanBusDevice::clearInputBuffer()
+{
+ if (device->state() != QCanBusDevice::ConnectedState) {
+ QVERIFY(device->connectDevice());
+ QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000);
+ }
+
+ for (int i = 0; i < 10; ++i)
+ device->triggerNewFrame();
+
+ device->clear(QCanBusDevice::Input);
+
+ QVERIFY(!device->framesAvailable());
+}
+
+void tst_QCanBusDevice::clearOutputBuffer()
+{
+ // this test requires buffered writing
+ device->setWriteBuffered(true);
+
+ if (device->state() != QCanBusDevice::ConnectedState) {
+ QVERIFY(device->connectDevice());
+ QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000);
+ }
+
+ // first test buffered writing, frames will be written after some delay
+ QSignalSpy spy(device.data(), &QCanBusDevice::framesWritten);
+ for (int i = 0; i < 10; ++i)
+ device->writeFrame(QCanBusFrame(0x123, "output"));
+ QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 10, 5000);
+
+ // now test clearing the buffer before the frames are actually written
+ spy.clear();
+ for (int i = 0; i < 10; ++i)
+ device->writeFrame(QCanBusFrame(0x123, "output"));
+
+ device->clear(QCanBusDevice::Output);
+ QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 0, 5000);
+}
+
void tst_QCanBusDevice::error()
{
QSignalSpy spy(device.data(), &QCanBusDevice::errorOccurred);