diff options
61 files changed, 1490 insertions, 337 deletions
diff --git a/.qmake.conf b/.qmake.conf index c29327d..a8a7769 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.13.1 +MODULE_VERSION = 5.14.0 diff --git a/examples/serialbus/can/mainwindow.cpp b/examples/serialbus/can/mainwindow.cpp index 300bbde..29c5f1f 100644 --- a/examples/serialbus/can/mainwindow.cpp +++ b/examples/serialbus/can/mainwindow.cpp @@ -60,7 +60,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), - m_ui(new Ui::MainWindow) + m_ui(new Ui::MainWindow), + m_busStatusTimer(new QTimer(this)) { m_ui->setupUi(this); @@ -93,6 +94,9 @@ void MainWindow::initActionsConnections() connect(m_ui->actionConnect, &QAction::triggered, m_connectDialog, &ConnectDialog::show); connect(m_connectDialog, &QDialog::accepted, this, &MainWindow::connectDevice); connect(m_ui->actionDisconnect, &QAction::triggered, this, &MainWindow::disconnectDevice); + connect(m_ui->actionResetController, &QAction::triggered, this, [this]() { + m_canDevice->resetController(); + }); connect(m_ui->actionQuit, &QAction::triggered, this, &QWidget::close); connect(m_ui->actionAboutQt, &QAction::triggered, qApp, &QApplication::aboutQt); connect(m_ui->actionClearLog, &QAction::triggered, m_ui->receivedMessagesEdit, &QTextEdit::clear); @@ -171,6 +175,31 @@ void MainWindow::connectDevice() .arg(p.pluginName).arg(p.deviceInterfaceName)); } } + + connect(m_busStatusTimer, &QTimer::timeout, this, [this]() { + switch (m_canDevice->busStatus()) { + case QCanBusDevice::CanBusStatus::Good: + m_ui->busStatus->setText("CAN bus status: Good."); + break; + case QCanBusDevice::CanBusStatus::Warning: + m_ui->busStatus->setText("CAN bus status: Warning."); + break; + case QCanBusDevice::CanBusStatus::Error: + m_ui->busStatus->setText("CAN bus status: Error."); + break; + case QCanBusDevice::CanBusStatus::BusOff: + m_ui->busStatus->setText("CAN bus status: Bus Off."); + break; + default: + m_ui->busStatus->setText("CAN bus status: Unknown."); + break; + } + }); + + if (m_canDevice->hasBusStatus()) + m_busStatusTimer->start(2000); + else + m_ui->busStatus->setText(tr("No CAN bus status available.")); } void MainWindow::disconnectDevice() @@ -178,6 +207,8 @@ void MainWindow::disconnectDevice() if (!m_canDevice) return; + m_busStatusTimer->stop(); + m_canDevice->disconnectDevice(); delete m_canDevice; m_canDevice = nullptr; diff --git a/examples/serialbus/can/mainwindow.h b/examples/serialbus/can/mainwindow.h index 40b2e4a..673e87a 100644 --- a/examples/serialbus/can/mainwindow.h +++ b/examples/serialbus/can/mainwindow.h @@ -61,6 +61,7 @@ QT_BEGIN_NAMESPACE class QCanBusFrame; class QLabel; +class QTimer; namespace Ui { class MainWindow; @@ -96,6 +97,7 @@ private: QLabel *m_written = nullptr; ConnectDialog *m_connectDialog = nullptr; QCanBusDevice *m_canDevice = nullptr; + QTimer *m_busStatusTimer = nullptr; }; #endif // MAINWINDOW_H diff --git a/examples/serialbus/can/mainwindow.ui b/examples/serialbus/can/mainwindow.ui index 1d5a657..f773ffb 100644 --- a/examples/serialbus/can/mainwindow.ui +++ b/examples/serialbus/can/mainwindow.ui @@ -66,6 +66,13 @@ </property> </widget> </item> + <item> + <widget class="QLabel" name="busStatus"> + <property name="text"> + <string/> + </property> + </widget> + </item> </layout> </widget> </item> @@ -77,7 +84,7 @@ <x>0</x> <y>0</y> <width>551</width> - <height>19</height> + <height>22</height> </rect> </property> <widget class="QMenu" name="menuCalls"> @@ -87,6 +94,8 @@ <addaction name="actionConnect"/> <addaction name="actionDisconnect"/> <addaction name="separator"/> + <addaction name="actionResetController"/> + <addaction name="separator"/> <addaction name="actionClearLog"/> <addaction name="separator"/> <addaction name="actionQuit"/> @@ -163,6 +172,14 @@ <string>Open plugin documentation in Webbrowser</string> </property> </action> + <action name="actionResetController"> + <property name="text"> + <string>&Reset CAN Controller</string> + </property> + <property name="toolTip"> + <string>Reset CAN Controller</string> + </property> + </action> </widget> <layoutdefault spacing="6" margin="11"/> <customwidgets> diff --git a/examples/serialbus/modbus/adueditor/adueditor.pro b/examples/serialbus/modbus/adueditor/adueditor.pro index facba76..77a6158 100644 --- a/examples/serialbus/modbus/adueditor/adueditor.pro +++ b/examples/serialbus/modbus/adueditor/adueditor.pro @@ -7,9 +7,19 @@ QT += serialbus-private core-private requires(qtConfig(combobox)) requires(qtConfig(modbus-serialport)) -FORMS += interface.ui -SOURCES += main.cpp mainwindow.cpp modbustcpclient.cpp -HEADERS += mainwindow.h plaintextedit.h modbustcpclient.h modbustcpclient_p.h +SOURCES += \ + main.cpp \ + mainwindow.cpp \ + modbustcpclient.cpp + +HEADERS += \ + mainwindow.h \ + modbustcpclient.h \ + modbustcpclient_p.h \ + plaintextedit.h + +FORMS += \ + interface.ui target.path = $$[QT_INSTALL_EXAMPLES]/serialbus/modbus/adueditor INSTALLS += target diff --git a/examples/serialbus/modbus/adueditor/mainwindow.cpp b/examples/serialbus/modbus/adueditor/mainwindow.cpp index 862be6a..4a0931e 100644 --- a/examples/serialbus/modbus/adueditor/mainwindow.cpp +++ b/examples/serialbus/modbus/adueditor/mainwindow.cpp @@ -52,8 +52,8 @@ #include "modbustcpclient.h" #include <QLoggingCategory> -#include <QModbusRtuSerialMaster> #include <QModbusPdu> +#include <QModbusRtuSerialMaster> #include <QSerialPortInfo> #ifndef QT_STATIC @@ -84,7 +84,7 @@ MainWindow::MainWindow(QWidget *parent) for (const QSerialPortInfo &info : ports) serialPortCombo->addItem(info.portName(), false); serialPortCombo->insertSeparator(serialPortCombo->count()); - serialPortCombo->addItem(QStringLiteral("Add port..."), true); + serialPortCombo->addItem(tr("Add port..."), true); serialPortCombo->setInsertPolicy(QComboBox::InsertAtTop); connect(tcpRadio, &QRadioButton::toggled, this, [this](bool toggled) { @@ -119,14 +119,14 @@ void MainWindow::on_sendButton_clicked() return; } - const quint8 address = (isSerial ? addressSpin : ui1Spin)->value(); + const quint8 address = quint8((isSerial ? addressSpin : ui1Spin)->value()); if (isCustom) { qDebug() << "Send: Sending custom PDU."; reply = m_device->sendRawRequest(QModbusRequest(QModbusRequest::FunctionCode( pduData[0]), pduData.mid(1)), address); } else { qDebug() << "Send: Sending PDU with predefined function code."; - quint16 fc = (isSerial ? fcSerialDrop : fcTcpDrop)->currentText().left(4).toShort(0, 16); + quint16 fc = (isSerial ? fcSerialDrop : fcTcpDrop)->currentText().left(4).toUShort(0, 16); reply = m_device->sendRawRequest(QModbusRequest(QModbusRequest::FunctionCode(fc), pduData), address); } @@ -136,11 +136,11 @@ void MainWindow::on_sendButton_clicked() if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, [reply, this]() { sendButton->setEnabled(true); - qDebug() << "Receive: Asynchronous response PDU: " << reply->rawResult() << endl; + qDebug() << "Receive: Asynchronous response PDU: " << reply->rawResult() << Qt::endl; }); } else { sendButton->setEnabled(true); - qDebug() << "Receive: Synchronous response pdu: " << reply->rawResult() << endl; + qDebug() << "Receive: Synchronous response pdu: " << reply->rawResult() << Qt::endl; } } } @@ -149,24 +149,23 @@ void MainWindow::on_connectButton_clicked() { if (tcpRadio->isChecked()) { auto device = new ModbusTcpClient; - using signature = void (QSpinBox::*)(int); - connect(ti1Spin, static_cast<signature>(&QSpinBox::valueChanged), device, - &ModbusTcpClient::valueChanged); - connect(ti2Spin, static_cast<signature>(&QSpinBox::valueChanged), device, - &ModbusTcpClient::valueChanged); - - connect(pi1Spin, static_cast<signature>(&QSpinBox::valueChanged), device, - &ModbusTcpClient::valueChanged); - connect(pi2Spin, static_cast<signature>(&QSpinBox::valueChanged), device, - &ModbusTcpClient::valueChanged); - - connect(l1Spin, static_cast<signature>(&QSpinBox::valueChanged), device, - &ModbusTcpClient::valueChanged); - connect(l2Spin, static_cast<signature>(&QSpinBox::valueChanged), device, - &ModbusTcpClient::valueChanged); - - connect(ui1Spin, static_cast<signature>(&QSpinBox::valueChanged), device, - &ModbusTcpClient::valueChanged); + connect(ti1Spin, QOverload<int>::of(&QSpinBox::valueChanged), + device, &ModbusTcpClient::valueChanged); + connect(ti2Spin, QOverload<int>::of(&QSpinBox::valueChanged), + device, &ModbusTcpClient::valueChanged); + + connect(pi1Spin, QOverload<int>::of(&QSpinBox::valueChanged), + device, &ModbusTcpClient::valueChanged); + connect(pi2Spin, QOverload<int>::of(&QSpinBox::valueChanged), + device, &ModbusTcpClient::valueChanged); + + connect(l1Spin, QOverload<int>::of(&QSpinBox::valueChanged), + device, &ModbusTcpClient::valueChanged); + connect(l2Spin, QOverload<int>::of(&QSpinBox::valueChanged), + device, &ModbusTcpClient::valueChanged); + + connect(ui1Spin, QOverload<int>::of(&QSpinBox::valueChanged), + device, &ModbusTcpClient::valueChanged); m_device = device; device->valueChanged(0); // trigger update diff --git a/examples/serialbus/modbus/adueditor/modbustcpclient_p.h b/examples/serialbus/modbus/adueditor/modbustcpclient_p.h index c1b37a8..a9170f9 100644 --- a/examples/serialbus/modbus/adueditor/modbustcpclient_p.h +++ b/examples/serialbus/modbus/adueditor/modbustcpclient_p.h @@ -73,7 +73,7 @@ public: QDataStream output(&buffer, QIODevice::WriteOnly); output << m_tId << m_pId << m_length << m_uId << request; - int writtenBytes = m_socket->write(buffer); + qint64 writtenBytes = m_socket->write(buffer); if (writtenBytes == -1 || writtenBytes < buffer.size()) { Q_Q(ModbusTcpClient); qDebug() << "Cannot write request to socket."; @@ -82,7 +82,7 @@ public: return false; } qDebug() << "Sent TCP ADU:" << buffer.toHex(); - qDebug() << "Sent TCP PDU:" << request << "with tId:" << hex << m_tId; + qDebug() << "Sent TCP PDU:" << request << "with tId:" << Qt::hex << m_tId; return true; }; @@ -91,13 +91,12 @@ public: Q_Q(ModbusTcpClient); auto reply = new QModbusReply(type, m_uId, q); - const auto element = QueueElement{ reply, request, unit, m_numberOfRetries, - m_responseTimeoutDuration }; + const auto element = QueueElement{reply, request, unit, m_numberOfRetries, + m_responseTimeoutDuration}; m_transactionStore.insert(m_tId, element); - using TypeId = void (QTimer::*)(int); q->connect(q, &QModbusClient::timeoutChanged, - element.timer.data(), static_cast<TypeId>(&QTimer::setInterval)); + element.timer.data(), QOverload<int>::of(&QTimer::setInterval)); QObject::connect(element.timer.data(), &QTimer::timeout, q, [this, writeToSocket]() { if (!m_transactionStore.contains(m_tId)) return; @@ -112,21 +111,21 @@ public: return; m_transactionStore.insert(m_tId, elem); elem.timer->start(); - qDebug() << "Resend request with tId:" << hex << m_tId; + qDebug() << "Resend request with tId:" << Qt::hex << m_tId; } else { - qDebug() << "Timeout of request with tId:" << hex << m_tId; + qDebug() << "Timeout of request with tId:" << Qt::hex << m_tId; elem.reply->setError(QModbusDevice::TimeoutError, QModbusClient::tr("Request timeout.")); } }); element.timer->start(); - return reply; + return reply; } - quint16 m_tId; - quint16 m_pId; - quint16 m_length; - quint8 m_uId; + quint16 m_tId = 0; + quint16 m_pId = 0; + quint16 m_length = 0; + quint8 m_uId = 0; }; #endif // MODBUSTCPCLIENT_P_H diff --git a/examples/serialbus/modbus/adueditor/plaintextedit.h b/examples/serialbus/modbus/adueditor/plaintextedit.h index 3cbbaaf..cbc590c 100644 --- a/examples/serialbus/modbus/adueditor/plaintextedit.h +++ b/examples/serialbus/modbus/adueditor/plaintextedit.h @@ -81,12 +81,12 @@ public: void contextMenuEvent(QContextMenuEvent *event) { QMenu menu(this); - menu.addAction(QStringLiteral("Clear"), this, &QPlainTextEdit::clear); + menu.addAction(tr("Clear"), this, &QPlainTextEdit::clear); #ifndef QT_NO_CLIPBOARD - menu.addAction(QStringLiteral("Copy"), this, &QPlainTextEdit::copy, QKeySequence::Copy); + menu.addAction(tr("Copy"), this, &QPlainTextEdit::copy, QKeySequence::Copy); #endif menu.addSeparator(); - menu.addAction(QStringLiteral("Select All"), this, &QPlainTextEdit::selectAll, + menu.addAction(tr("Select All"), this, &QPlainTextEdit::selectAll, QKeySequence::SelectAll); menu.exec(event->globalPos()); } diff --git a/examples/serialbus/modbus/master/main.cpp b/examples/serialbus/modbus/master/main.cpp index 4f97a39..19a1d5b 100644 --- a/examples/serialbus/modbus/master/main.cpp +++ b/examples/serialbus/modbus/master/main.cpp @@ -55,9 +55,8 @@ int main(int argc, char *argv[]) { - // TODO uncomment this line before release - // right now we always need it - QLoggingCategory::setFilterRules(QStringLiteral("qt.modbus* = true")); + // Uncomment the following line to enable logging + // QLoggingCategory::setFilterRules(QStringLiteral("qt.modbus* = true")); QApplication a(argc, argv); MainWindow w; w.show(); diff --git a/examples/serialbus/modbus/master/mainwindow.cpp b/examples/serialbus/modbus/master/mainwindow.cpp index 6600af8..d88393b 100644 --- a/examples/serialbus/modbus/master/mainwindow.cpp +++ b/examples/serialbus/modbus/master/mainwindow.cpp @@ -67,8 +67,6 @@ enum ModbusConnection { MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) - , lastRequest(nullptr) - , modbusDevice(nullptr) { ui->setupUi(this); @@ -82,8 +80,8 @@ MainWindow::MainWindow(QWidget *parent) ui->writeValueTable->setModel(writeModel); ui->writeValueTable->hideColumn(2); - connect(writeModel, &WriteRegisterModel::updateViewport, ui->writeValueTable->viewport(), - static_cast<void (QWidget::*)()>(&QWidget::update)); + connect(writeModel, &WriteRegisterModel::updateViewport, + ui->writeValueTable->viewport(), QOverload<>::of(&QWidget::update)); ui->writeTable->addItem(tr("Coils"), QModbusDataUnit::Coils); ui->writeTable->addItem(tr("Discrete Inputs"), QModbusDataUnit::DiscreteInputs); @@ -92,11 +90,11 @@ MainWindow::MainWindow(QWidget *parent) #if QT_CONFIG(modbus_serialport) ui->connectType->setCurrentIndex(0); - on_connectType_currentIndexChanged(0); + onConnectTypeChanged(0); #else // lock out the serial port option ui->connectType->setCurrentIndex(1); - on_connectType_currentIndexChanged(1); + onConnectTypeChanged(1); ui->connectType->setEnabled(false); #endif @@ -105,10 +103,10 @@ MainWindow::MainWindow(QWidget *parent) model->setItem(i, new QStandardItem(QStringLiteral("%1").arg(i + 1))); ui->writeSize->setModel(model); ui->writeSize->setCurrentText("10"); - connect(ui->writeSize,&QComboBox::currentTextChanged, writeModel, - &WriteRegisterModel::setNumberOfValues); + connect(ui->writeSize, &QComboBox::currentTextChanged, + writeModel, &WriteRegisterModel::setNumberOfValues); - auto valueChanged = static_cast<void (QSpinBox::*)(int)> (&QSpinBox::valueChanged); + auto valueChanged = QOverload<int>::of(&QSpinBox::valueChanged); connect(ui->writeAddress, valueChanged, writeModel, &WriteRegisterModel::setStartAddress); connect(ui->writeAddress, valueChanged, this, [this, model](int i) { int lastPossibleIndex = 0; @@ -142,16 +140,28 @@ void MainWindow::initActions() ui->actionExit->setEnabled(true); ui->actionOptions->setEnabled(true); + connect(ui->connectButton, &QPushButton::clicked, + this, &MainWindow::onConnectButtonClicked); connect(ui->actionConnect, &QAction::triggered, - this, &MainWindow::on_connectButton_clicked); + this, &MainWindow::onConnectButtonClicked); connect(ui->actionDisconnect, &QAction::triggered, - this, &MainWindow::on_connectButton_clicked); + this, &MainWindow::onConnectButtonClicked); + connect(ui->readButton, &QPushButton::clicked, + this, &MainWindow::onReadButtonClicked); + connect(ui->writeButton, &QPushButton::clicked, + this, &MainWindow::onWriteButtonClicked); + connect(ui->readWriteButton, &QPushButton::clicked, + this, &MainWindow::onReadWriteButtonClicked); + connect(ui->connectType, QOverload<int>::of(&QComboBox::currentIndexChanged), + this, &MainWindow::onConnectTypeChanged); + connect(ui->writeTable, QOverload<int>::of(&QComboBox::currentIndexChanged), + this, &MainWindow::onWriteTableChanged); connect(ui->actionExit, &QAction::triggered, this, &QMainWindow::close); connect(ui->actionOptions, &QAction::triggered, m_settingsDialog, &QDialog::show); } -void MainWindow::on_connectType_currentIndexChanged(int index) +void MainWindow::onConnectTypeChanged(int index) { if (modbusDevice) { modbusDevice->disconnectDevice(); @@ -159,7 +169,7 @@ void MainWindow::on_connectType_currentIndexChanged(int index) modbusDevice = nullptr; } - auto type = static_cast<ModbusConnection> (index); + auto type = static_cast<ModbusConnection>(index); if (type == Serial) { #if QT_CONFIG(modbus_serialport) modbusDevice = new QModbusRtuSerialMaster(this); @@ -167,7 +177,7 @@ void MainWindow::on_connectType_currentIndexChanged(int index) } else if (type == Tcp) { modbusDevice = new QModbusTcpClient(this); if (ui->portEdit->text().isEmpty()) - ui->portEdit->setText(QLatin1Literal("127.0.0.1:502")); + ui->portEdit->setText(QLatin1String("127.0.0.1:502")); } connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) { @@ -182,18 +192,18 @@ void MainWindow::on_connectType_currentIndexChanged(int index) statusBar()->showMessage(tr("Could not create Modbus client."), 5000); } else { connect(modbusDevice, &QModbusClient::stateChanged, - this, &MainWindow::onStateChanged); + this, &MainWindow::onModbusStateChanged); } } -void MainWindow::on_connectButton_clicked() +void MainWindow::onConnectButtonClicked() { if (!modbusDevice) return; statusBar()->clearMessage(); if (modbusDevice->state() != QModbusDevice::ConnectedState) { - if (static_cast<ModbusConnection> (ui->connectType->currentIndex()) == Serial) { + if (static_cast<ModbusConnection>(ui->connectType->currentIndex()) == Serial) { modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, ui->portEdit->text()); #if QT_CONFIG(modbus_serialport) @@ -226,7 +236,7 @@ void MainWindow::on_connectButton_clicked() } } -void MainWindow::onStateChanged(int state) +void MainWindow::onModbusStateChanged(int state) { bool connected = (state != QModbusDevice::UnconnectedState); ui->actionConnect->setEnabled(!connected); @@ -238,7 +248,7 @@ void MainWindow::onStateChanged(int state) ui->connectButton->setText(tr("Disconnect")); } -void MainWindow::on_readButton_clicked() +void MainWindow::onReadButtonClicked() { if (!modbusDevice) return; @@ -247,7 +257,7 @@ void MainWindow::on_readButton_clicked() if (auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) { if (!reply->isFinished()) - connect(reply, &QModbusReply::finished, this, &MainWindow::readReady); + connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady); else delete reply; // broadcast replies return immediately } else { @@ -255,7 +265,7 @@ void MainWindow::on_readButton_clicked() } } -void MainWindow::readReady() +void MainWindow::onReadReady() { auto reply = qobject_cast<QModbusReply *>(sender()); if (!reply) @@ -263,7 +273,7 @@ void MainWindow::readReady() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); - for (uint i = 0; i < unit.valueCount(); i++) { + for (int i = 0, total = int(unit.valueCount()); i < total; ++i) { const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i) .arg(QString::number(unit.value(i), unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16)); @@ -282,7 +292,7 @@ void MainWindow::readReady() reply->deleteLater(); } -void MainWindow::on_writeButton_clicked() +void MainWindow::onWriteButtonClicked() { if (!modbusDevice) return; @@ -290,7 +300,7 @@ void MainWindow::on_writeButton_clicked() QModbusDataUnit writeUnit = writeRequest(); QModbusDataUnit::RegisterType table = writeUnit.registerType(); - for (uint i = 0; i < writeUnit.valueCount(); i++) { + for (int i = 0, total = int(writeUnit.valueCount()); i < total; ++i) { if (table == QModbusDataUnit::Coils) writeUnit.setValue(i, writeModel->m_coils[i + writeUnit.startAddress()]); else @@ -319,7 +329,7 @@ void MainWindow::on_writeButton_clicked() } } -void MainWindow::on_readWriteButton_clicked() +void MainWindow::onReadWriteButtonClicked() { if (!modbusDevice) return; @@ -328,7 +338,7 @@ void MainWindow::on_readWriteButton_clicked() QModbusDataUnit writeUnit = writeRequest(); QModbusDataUnit::RegisterType table = writeUnit.registerType(); - for (uint i = 0; i < writeUnit.valueCount(); i++) { + for (int i = 0, total = int(writeUnit.valueCount()); i < total; ++i) { if (table == QModbusDataUnit::Coils) writeUnit.setValue(i, writeModel->m_coils[i + writeUnit.startAddress()]); else @@ -338,7 +348,7 @@ void MainWindow::on_readWriteButton_clicked() if (auto *reply = modbusDevice->sendReadWriteRequest(readRequest(), writeUnit, ui->serverEdit->value())) { if (!reply->isFinished()) - connect(reply, &QModbusReply::finished, this, &MainWindow::readReady); + connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady); else delete reply; // broadcast replies return immediately } else { @@ -346,7 +356,7 @@ void MainWindow::on_readWriteButton_clicked() } } -void MainWindow::on_writeTable_currentIndexChanged(int index) +void MainWindow::onWriteTableChanged(int index) { const bool coilsOrHolding = index == 0 || index == 3; if (coilsOrHolding) { @@ -363,25 +373,25 @@ void MainWindow::on_writeTable_currentIndexChanged(int index) QModbusDataUnit MainWindow::readRequest() const { const auto table = - static_cast<QModbusDataUnit::RegisterType> (ui->writeTable->currentData().toInt()); + static_cast<QModbusDataUnit::RegisterType>(ui->writeTable->currentData().toInt()); int startAddress = ui->readAddress->value(); Q_ASSERT(startAddress >= 0 && startAddress < 10); // do not go beyond 10 entries - int numberOfEntries = qMin(ui->readSize->currentText().toInt(), 10 - startAddress); + quint16 numberOfEntries = qMin(ui->readSize->currentText().toUShort(), quint16(10 - startAddress)); return QModbusDataUnit(table, startAddress, numberOfEntries); } QModbusDataUnit MainWindow::writeRequest() const { const auto table = - static_cast<QModbusDataUnit::RegisterType> (ui->writeTable->currentData().toInt()); + static_cast<QModbusDataUnit::RegisterType>(ui->writeTable->currentData().toInt()); int startAddress = ui->writeAddress->value(); Q_ASSERT(startAddress >= 0 && startAddress < 10); // do not go beyond 10 entries - int numberOfEntries = qMin(ui->writeSize->currentText().toInt(), 10 - startAddress); + quint16 numberOfEntries = qMin(ui->writeSize->currentText().toUShort(), quint16(10 - startAddress)); return QModbusDataUnit(table, startAddress, numberOfEntries); } diff --git a/examples/serialbus/modbus/master/mainwindow.h b/examples/serialbus/modbus/master/mainwindow.h index d4d5036..186d004 100644 --- a/examples/serialbus/modbus/master/mainwindow.h +++ b/examples/serialbus/modbus/master/mainwindow.h @@ -83,24 +83,24 @@ private: QModbusDataUnit writeRequest() const; private slots: - void on_connectButton_clicked(); - void onStateChanged(int state); + void onConnectButtonClicked(); + void onModbusStateChanged(int state); - void on_readButton_clicked(); - void readReady(); + void onReadButtonClicked(); + void onReadReady(); - void on_writeButton_clicked(); - void on_readWriteButton_clicked(); + void onWriteButtonClicked(); + void onReadWriteButtonClicked(); - void on_connectType_currentIndexChanged(int); - void on_writeTable_currentIndexChanged(int); + void onConnectTypeChanged(int); + void onWriteTableChanged(int); private: - Ui::MainWindow *ui; - QModbusReply *lastRequest; - QModbusClient *modbusDevice; - SettingsDialog *m_settingsDialog; - WriteRegisterModel *writeModel; + Ui::MainWindow *ui = nullptr; + QModbusReply *lastRequest = nullptr; + QModbusClient *modbusDevice = nullptr; + SettingsDialog *m_settingsDialog = nullptr; + WriteRegisterModel *writeModel = nullptr; }; #endif // MAINWINDOW_H diff --git a/examples/serialbus/modbus/master/master.pro b/examples/serialbus/modbus/master/master.pro index 188fbc1..d7a4885 100644 --- a/examples/serialbus/modbus/master/master.pro +++ b/examples/serialbus/modbus/master/master.pro @@ -7,17 +7,20 @@ TARGET = modbusmaster TEMPLATE = app CONFIG += c++11 -SOURCES += main.cpp\ - mainwindow.cpp \ - settingsdialog.cpp \ - writeregistermodel.cpp +SOURCES += \ + main.cpp\ + mainwindow.cpp \ + settingsdialog.cpp \ + writeregistermodel.cpp -HEADERS += mainwindow.h \ - settingsdialog.h \ - writeregistermodel.h +HEADERS += \ + mainwindow.h \ + settingsdialog.h \ + writeregistermodel.h -FORMS += mainwindow.ui \ - settingsdialog.ui +FORMS += \ + mainwindow.ui \ + settingsdialog.ui RESOURCES += \ master.qrc diff --git a/examples/serialbus/modbus/master/settingsdialog.h b/examples/serialbus/modbus/master/settingsdialog.h index 98ba946..fbeb97a 100644 --- a/examples/serialbus/modbus/master/settingsdialog.h +++ b/examples/serialbus/modbus/master/settingsdialog.h @@ -88,7 +88,7 @@ public: private: Settings m_settings; - Ui::SettingsDialog *ui; + Ui::SettingsDialog *ui = nullptr; }; #endif // SETTINGSDIALOG_H diff --git a/examples/serialbus/modbus/master/writeregistermodel.cpp b/examples/serialbus/modbus/master/writeregistermodel.cpp index 4b61e99..441f643 100644 --- a/examples/serialbus/modbus/master/writeregistermodel.cpp +++ b/examples/serialbus/modbus/master/writeregistermodel.cpp @@ -82,7 +82,7 @@ QVariant WriteRegisterModel::data(const QModelIndex &index, int role) const if (index.column() == CoilsColumn && role == Qt::CheckStateRole) // coils return m_coils.at(index.row()) ? Qt::Checked : Qt::Unchecked; - else if (index.column() == HoldingColumn && role == Qt::DisplayRole) //holding registers + if (index.column() == HoldingColumn && role == Qt::DisplayRole) // holding registers return QString("0x%1").arg(QString::number(m_holdingRegisters.at(index.row()), 16)); return QVariant(); @@ -146,9 +146,9 @@ Qt::ItemFlags WriteRegisterModel::flags(const QModelIndex &index) const if ((index.row() < m_address) || (index.row() >= (m_address + m_number))) flags &= ~Qt::ItemIsEnabled; - if (index.column() == CoilsColumn) //coils + if (index.column() == CoilsColumn) // coils return flags | Qt::ItemIsUserCheckable; - if (index.column() == HoldingColumn) //holding registers + if (index.column() == HoldingColumn) // holding registers return flags | Qt::ItemIsEditable; return flags; diff --git a/examples/serialbus/modbus/master/writeregistermodel.h b/examples/serialbus/modbus/master/writeregistermodel.h index fe49591..4b0311e 100644 --- a/examples/serialbus/modbus/master/writeregistermodel.h +++ b/examples/serialbus/modbus/master/writeregistermodel.h @@ -79,8 +79,8 @@ signals: void updateViewport(); public: - int m_number; - int m_address; + int m_number = 0; + int m_address = 0; QBitArray m_coils; QVector<quint16> m_holdingRegisters; }; diff --git a/examples/serialbus/modbus/slave/main.cpp b/examples/serialbus/modbus/slave/main.cpp index 4f97a39..19a1d5b 100644 --- a/examples/serialbus/modbus/slave/main.cpp +++ b/examples/serialbus/modbus/slave/main.cpp @@ -55,9 +55,8 @@ int main(int argc, char *argv[]) { - // TODO uncomment this line before release - // right now we always need it - QLoggingCategory::setFilterRules(QStringLiteral("qt.modbus* = true")); + // Uncomment the following line to enable logging + // QLoggingCategory::setFilterRules(QStringLiteral("qt.modbus* = true")); QApplication a(argc, argv); MainWindow w; w.show(); diff --git a/examples/serialbus/modbus/slave/mainwindow.cpp b/examples/serialbus/modbus/slave/mainwindow.cpp index 4a4c194..213c034 100644 --- a/examples/serialbus/modbus/slave/mainwindow.cpp +++ b/examples/serialbus/modbus/slave/mainwindow.cpp @@ -67,18 +67,17 @@ enum ModbusConnection { MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) - , modbusDevice(nullptr) { ui->setupUi(this); setupWidgetContainers(); #if QT_CONFIG(modbus_serialport) ui->connectType->setCurrentIndex(0); - on_connectType_currentIndexChanged(0); + onCurrentConnectTypeChanged(0); #else // lock out the serial port option ui->connectType->setCurrentIndex(1); - on_connectType_currentIndexChanged(1); + onCurrentConnectTypeChanged(1); ui->connectType->setEnabled(false); #endif @@ -102,16 +101,20 @@ void MainWindow::initActions() ui->actionExit->setEnabled(true); ui->actionOptions->setEnabled(true); + connect(ui->connectButton, &QPushButton::clicked, + this, &MainWindow::onConnectButtonClicked); connect(ui->actionConnect, &QAction::triggered, - this, &MainWindow::on_connectButton_clicked); + this, &MainWindow::onConnectButtonClicked); connect(ui->actionDisconnect, &QAction::triggered, - this, &MainWindow::on_connectButton_clicked); + this, &MainWindow::onConnectButtonClicked); + connect(ui->connectType, QOverload<int>::of(&QComboBox::currentIndexChanged), + this, &MainWindow::onCurrentConnectTypeChanged); connect(ui->actionExit, &QAction::triggered, this, &QMainWindow::close); connect(ui->actionOptions, &QAction::triggered, m_settingsDialog, &QDialog::show); } -void MainWindow::on_connectType_currentIndexChanged(int index) +void MainWindow::onCurrentConnectTypeChanged(int index) { if (modbusDevice) { modbusDevice->disconnect(); @@ -119,7 +122,7 @@ void MainWindow::on_connectType_currentIndexChanged(int index) modbusDevice = nullptr; } - ModbusConnection type = static_cast<ModbusConnection> (index); + auto type = static_cast<ModbusConnection>(index); if (type == Serial) { #if QT_CONFIG(modbus_serialport) modbusDevice = new QModbusRtuSerialSlave(this); @@ -127,7 +130,7 @@ void MainWindow::on_connectType_currentIndexChanged(int index) } else if (type == Tcp) { modbusDevice = new QModbusTcpServer(this); if (ui->portEdit->text().isEmpty()) - ui->portEdit->setText(QLatin1Literal("127.0.0.1:502")); + ui->portEdit->setText(QLatin1String("127.0.0.1:502")); } ui->listenOnlyBox->setEnabled(type == Serial); @@ -176,14 +179,14 @@ void MainWindow::handleDeviceError(QModbusDevice::Error newError) statusBar()->showMessage(modbusDevice->errorString(), 5000); } -void MainWindow::on_connectButton_clicked() +void MainWindow::onConnectButtonClicked() { bool intendToConnect = (modbusDevice->state() == QModbusDevice::UnconnectedState); statusBar()->clearMessage(); if (intendToConnect) { - if (static_cast<ModbusConnection> (ui->connectType->currentIndex()) == Serial) { + if (static_cast<ModbusConnection>(ui->connectType->currentIndex()) == Serial) { modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, ui->portEdit->text()); #if QT_CONFIG(modbus_serialport) @@ -244,7 +247,7 @@ void MainWindow::bitChanged(int id, QModbusDataUnit::RegisterType table, bool va if (!modbusDevice) return; - if (!modbusDevice->setData(table, id, value)) + if (!modbusDevice->setData(table, quint16(id), value)) statusBar()->showMessage(tr("Could not set data: ") + modbusDevice->errorString(), 5000); } @@ -256,11 +259,11 @@ void MainWindow::setRegister(const QString &value) const QString objectName = QObject::sender()->objectName(); if (registers.contains(objectName)) { bool ok = true; - const int id = QObject::sender()->property("ID").toInt(); + const quint16 id = quint16(QObject::sender()->property("ID").toUInt()); if (objectName.startsWith(QStringLiteral("inReg"))) - ok = modbusDevice->setData(QModbusDataUnit::InputRegisters, id, value.toInt(&ok, 16)); + ok = modbusDevice->setData(QModbusDataUnit::InputRegisters, id, value.toUShort(&ok, 16)); else if (objectName.startsWith(QStringLiteral("holdReg"))) - ok = modbusDevice->setData(QModbusDataUnit::HoldingRegisters, id, value.toInt(&ok, 16)); + ok = modbusDevice->setData(QModbusDataUnit::HoldingRegisters, id, value.toUShort(&ok, 16)); if (!ok) statusBar()->showMessage(tr("Could not set register: ") + modbusDevice->errorString(), @@ -275,11 +278,11 @@ void MainWindow::updateWidgets(QModbusDataUnit::RegisterType table, int address, QString text; switch (table) { case QModbusDataUnit::Coils: - modbusDevice->data(QModbusDataUnit::Coils, address + i, &value); + modbusDevice->data(QModbusDataUnit::Coils, quint16(address + i), &value); coilButtons.button(address + i)->setChecked(value); break; case QModbusDataUnit::HoldingRegisters: - modbusDevice->data(QModbusDataUnit::HoldingRegisters, address + i, &value); + modbusDevice->data(QModbusDataUnit::HoldingRegisters, quint16(address + i), &value); registers.value(QStringLiteral("holdReg_%1").arg(address + i))->setText(text .setNum(value, 16)); break; @@ -296,10 +299,10 @@ void MainWindow::setupDeviceData() if (!modbusDevice) return; - for (int i = 0; i < coilButtons.buttons().count(); ++i) + for (quint16 i = 0; i < coilButtons.buttons().count(); ++i) modbusDevice->setData(QModbusDataUnit::Coils, i, coilButtons.button(i)->isChecked()); - for (int i = 0; i < discreteButtons.buttons().count(); ++i) { + for (quint16 i = 0; i < discreteButtons.buttons().count(); ++i) { modbusDevice->setData(QModbusDataUnit::DiscreteInputs, i, discreteButtons.button(i)->isChecked()); } @@ -307,11 +310,11 @@ void MainWindow::setupDeviceData() bool ok; for (QLineEdit *widget : qAsConst(registers)) { if (widget->objectName().startsWith(QStringLiteral("inReg"))) { - modbusDevice->setData(QModbusDataUnit::InputRegisters, widget->property("ID").toInt(), - widget->text().toInt(&ok, 16)); + modbusDevice->setData(QModbusDataUnit::InputRegisters, quint16(widget->property("ID").toUInt()), + widget->text().toUShort(&ok, 16)); } else if (widget->objectName().startsWith(QStringLiteral("holdReg"))) { - modbusDevice->setData(QModbusDataUnit::HoldingRegisters, widget->property("ID").toInt(), - widget->text().toInt(&ok, 16)); + modbusDevice->setData(QModbusDataUnit::HoldingRegisters, quint16(widget->property("ID").toUInt()), + widget->text().toUShort(&ok, 16)); } } } diff --git a/examples/serialbus/modbus/slave/mainwindow.h b/examples/serialbus/modbus/slave/mainwindow.h index aec1e9d..e824649 100644 --- a/examples/serialbus/modbus/slave/mainwindow.h +++ b/examples/serialbus/modbus/slave/mainwindow.h @@ -77,7 +77,7 @@ public: ~MainWindow(); private Q_SLOTS: - void on_connectButton_clicked(); + void onConnectButtonClicked(); void onStateChanged(int state); void coilChanged(int id); @@ -87,7 +87,7 @@ private Q_SLOTS: void setRegister(const QString &value); void updateWidgets(QModbusDataUnit::RegisterType table, int address, int size); - void on_connectType_currentIndexChanged(int); + void onCurrentConnectTypeChanged(int); void handleDeviceError(QModbusDevice::Error newError); @@ -96,13 +96,13 @@ private: void setupDeviceData(); void setupWidgetContainers(); - Ui::MainWindow *ui; - QModbusServer *modbusDevice; + Ui::MainWindow *ui = nullptr; + QModbusServer *modbusDevice = nullptr; QButtonGroup coilButtons; QButtonGroup discreteButtons; QHash<QString, QLineEdit *> registers; - SettingsDialog *m_settingsDialog; + SettingsDialog *m_settingsDialog = nullptr; }; #endif // MAINWINDOW_H diff --git a/examples/serialbus/modbus/slave/slave.pro b/examples/serialbus/modbus/slave/slave.pro index aadef07..2e128c2 100644 --- a/examples/serialbus/modbus/slave/slave.pro +++ b/examples/serialbus/modbus/slave/slave.pro @@ -7,13 +7,18 @@ TARGET = modbusslave TEMPLATE = app CONFIG += c++11 -SOURCES += main.cpp\ - mainwindow.cpp \ - settingsdialog.cpp +SOURCES += \ + main.cpp \ + mainwindow.cpp \ + settingsdialog.cpp -HEADERS += mainwindow.h settingsdialog.h +HEADERS += \ + mainwindow.h \ + settingsdialog.h -FORMS += mainwindow.ui settingsdialog.ui +FORMS += \ + mainwindow.ui \ + settingsdialog.ui RESOURCES += slave.qrc diff --git a/src/plugins/canbus/peakcan/peakcan_symbols_p.h b/src/plugins/canbus/peakcan/peakcan_symbols_p.h index 9ab2e67..3470df2 100644 --- a/src/plugins/canbus/peakcan/peakcan_symbols_p.h +++ b/src/plugins/canbus/peakcan/peakcan_symbols_p.h @@ -267,21 +267,28 @@ #define PCAN_TYPE_DNG_SJA_EPP 0x06U // PCAN-Dongle EPP SJA1000 // Type definitions -#define TPCANHandle quint16 // Represents a PCAN hardware channel handle -#define TPCANStatus quint32 // Represents a PCAN status/error code -#define TPCANParameter quint8 // Represents a PCAN parameter to be read or set -#define TPCANDevice quint8 // Represents a PCAN device -#define TPCANMessageType quint8 // Represents the type of a PCAN message -#define TPCANType quint8 // Represents the type of PCAN hardware to be initialized -#define TPCANMode quint8 // Represents a PCAN filter mode -#define TPCANBaudrate quint16 // Represents a PCAN Baud rate register value -#define TPCANBitrateFD char * // Represents a PCAN-FD bit rate string -#define TPCANTimestampFD quint64 // Represents a timestamp of a received PCAN FD message +#ifdef Q_OS_MACOS +#define TPCANLong quint64 +#define TPCANLongToFrameID(a) static_cast<quint32>(a) +#else +#define TPCANLong quint32 +#define TPCANLongToFrameID(a) a +#endif +#define TPCANHandle quint16 // Represents a PCAN hardware channel handle +#define TPCANStatus TPCANLong // Represents a PCAN status/error code +#define TPCANParameter quint8 // Represents a PCAN parameter to be read or set +#define TPCANDevice quint8 // Represents a PCAN device +#define TPCANMessageType quint8 // Represents the type of a PCAN message +#define TPCANType quint8 // Represents the type of PCAN hardware to be initialized +#define TPCANMode quint8 // Represents a PCAN filter mode +#define TPCANBaudrate quint16 // Represents a PCAN Baud rate register value +#define TPCANBitrateFD char * // Represents a PCAN-FD bit rate string +#define TPCANTimestampFD quint64 // Represents a timestamp of a received PCAN FD message // Represents a PCAN message typedef struct tagTPCANMsg { - quint32 ID; // 11/29-bit message identifier + TPCANLong ID; // 11/29-bit message identifier TPCANMessageType MSGTYPE; // Type of the message quint8 LEN; // Data Length Code of the message (0..8) quint8 DATA[8]; // Data of the message (DATA[0]..DATA[7]) @@ -291,15 +298,15 @@ typedef struct tagTPCANMsg // Total Microseconds = micros + 1000 * millis + 0xFFFFFFFF * 1000 * millis_overflow typedef struct tagTPCANTimestamp { - quint32 millis; // Base-value: milliseconds: 0.. 2^32-1 - quint16 millis_overflow; // Roll-arounds of millis - quint16 micros; // Microseconds: 0..999 + TPCANLong millis; // Base-value: milliseconds: 0.. 2^32-1 + quint16 millis_overflow; // Roll-arounds of millis + quint16 micros; // Microseconds: 0..999 } TPCANTimestamp; // Represents a PCAN message from a FD capable hardware typedef struct tagTPCANMsgFD { - quint32 ID; // 11/29-bit message identifier + TPCANLong ID; // 11/29-bit message identifier TPCANMessageType MSGTYPE; // Type of the message quint8 DLC; // Data Length Code of the message (0..15) quint8 DATA[64]; // Data of the message (DATA[0]..DATA[63]) @@ -314,7 +321,7 @@ typedef struct tagTPCANMsgFD if (!symbolName) \ return false; -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Initialize, TPCANHandle, TPCANBaudrate, TPCANType, quint32, quint16) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Initialize, TPCANHandle, TPCANBaudrate, TPCANType, TPCANLong, quint16) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_InitializeFD, TPCANHandle, TPCANBitrateFD) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Uninitialize, TPCANHandle) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Reset, TPCANHandle) @@ -323,15 +330,19 @@ GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Read, TPCANHandle, TPCANMsg *, TPCANTi GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_ReadFD, TPCANHandle, TPCANMsgFD *, TPCANTimestampFD *) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_Write, TPCANHandle, TPCANMsg *) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_WriteFD, TPCANHandle, TPCANMsgFD *) -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_FilterMessages, TPCANHandle, quint32, quint32, TPCANMode) -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetValue, TPCANHandle, TPCANParameter, void *, quint32) -GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_SetValue, TPCANHandle, TPCANParameter, void *, quint32) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_FilterMessages, TPCANHandle, TPCANLong, TPCANLong, TPCANMode) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetValue, TPCANHandle, TPCANParameter, void *, TPCANLong) +GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_SetValue, TPCANHandle, TPCANParameter, void *, TPCANLong) GENERATE_SYMBOL_VARIABLE(TPCANStatus, CAN_GetErrorText, TPCANStatus, quint16, char *) inline bool resolveSymbols(QLibrary *pcanLibrary) { if (!pcanLibrary->isLoaded()) { - pcanLibrary->setFileName(QStringLiteral("pcanbasic")); + #ifdef Q_OS_MACOS + pcanLibrary->setFileName(QStringLiteral("PCBUSB")); + #else + pcanLibrary->setFileName(QStringLiteral("pcanbasic")); + #endif if (!pcanLibrary->load()) return false; } diff --git a/src/plugins/canbus/peakcan/peakcanbackend.cpp b/src/plugins/canbus/peakcan/peakcanbackend.cpp index f8932e9..b1a991d 100644 --- a/src/plugins/canbus/peakcan/peakcanbackend.cpp +++ b/src/plugins/canbus/peakcan/peakcanbackend.cpp @@ -68,6 +68,8 @@ bool PeakCanBackend::canCreate(QString *errorReason) #else static bool symbolsResolved = resolveSymbols(pcanLibrary()); if (Q_UNLIKELY(!symbolsResolved)) { + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot load library: %ls", + qUtf16Printable(pcanLibrary()->errorString())); *errorReason = pcanLibrary()->errorString(); return false; } @@ -331,15 +333,21 @@ bool PeakCanBackendPrivate::open() } if (Q_UNLIKELY(st != PCAN_ERROR_OK)) { - q->setError(systemErrorString(st), QCanBusDevice::ConnectionError); + const QString errorString = systemErrorString(st); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot initialize hardware: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); return false; } #if defined(Q_OS_WIN32) if (readHandle == INVALID_HANDLE_VALUE) { readHandle = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (!readHandle) { - q->setError(qt_error_string(::GetLastError()), QCanBusDevice::ConnectionError); + if (Q_UNLIKELY(!readHandle)) { + const QString errorString = qt_error_string(::GetLastError()); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot create receive event handler: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); return false; } } @@ -353,7 +361,10 @@ bool PeakCanBackendPrivate::open() #else const TPCANStatus err = ::CAN_GetValue(channelIndex, PCAN_RECEIVE_EVENT, &readHandle, sizeof(readHandle)); if (Q_UNLIKELY(err != PCAN_ERROR_OK)) { - q->setError(systemErrorString(err), QCanBusDevice::ConnectionError); + const QString errorString = systemErrorString(err); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot create receive event handler: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); return false; } #endif @@ -380,8 +391,12 @@ void PeakCanBackendPrivate::close() quint32 value = 0; const TPCANStatus err = ::CAN_SetValue(channelIndex, PCAN_RECEIVE_EVENT, &value, sizeof(value)); - if (Q_UNLIKELY(err != PCAN_ERROR_OK)) - q->setError(systemErrorString(err), QCanBusDevice::ConnectionError); + if (Q_UNLIKELY(err != PCAN_ERROR_OK)) { + const QString errorString = systemErrorString(err); + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot unregister receive event handler: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); + } const TPCANStatus st = ::CAN_Uninitialize(channelIndex); if (Q_UNLIKELY(st != PCAN_ERROR_OK)) @@ -389,8 +404,12 @@ void PeakCanBackendPrivate::close() #if defined(Q_OS_WIN32) if (readHandle && (readHandle != INVALID_HANDLE_VALUE)) { - if (Q_UNLIKELY(!::CloseHandle(readHandle))) - q->setError(qt_error_string(::GetLastError()), QCanBusDevice::ConnectionError); + const QString errorString = qt_error_string(::GetLastError()); + if (Q_UNLIKELY(!::CloseHandle(readHandle))) { + qCCritical(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot close read handle: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::ConnectionError); + } readHandle = INVALID_HANDLE_VALUE; } #else @@ -413,6 +432,7 @@ bool PeakCanBackendPrivate::setConfigurationParameter(int key, const QVariant &v case QCanBusDevice::DataBitRateKey: { const int dataBitrate = value.toInt(); if (Q_UNLIKELY(dataBitrateString(dataBitrate).isEmpty())) { + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported data bitrate value: %d", dataBitrate); q->setError(PeakCanBackend::tr("Unsupported data bitrate value: %1.").arg(dataBitrate), QCanBusDevice::ConfigurationError); return false; @@ -420,6 +440,7 @@ bool PeakCanBackendPrivate::setConfigurationParameter(int key, const QVariant &v return true; } default: + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported configuration key: %d", key); q->setError(PeakCanBackend::tr("Unsupported configuration key: %1").arg(key), QCanBusDevice::ConfigurationError); return false; @@ -558,8 +579,9 @@ void PeakCanBackendPrivate::startWrite() ::memcpy(message.DATA, payload.constData(), sizeof(message.DATA)); st = ::CAN_WriteFD(channelIndex, &message); } else if (frame.hasFlexibleDataRateFormat()) { - q->setError(PeakCanBackend::tr("Cannot send CAN FD frame format as CAN FD is not enabled."), - QCanBusDevice::WriteError); + const char errorString[] = "Cannot send CAN FD frame format as CAN FD is not enabled."; + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN(), errorString); + q->setError(PeakCanBackend::tr(errorString), QCanBusDevice::WriteError); } else { TPCANMsg message; ::memset(&message, 0, sizeof(message)); @@ -576,10 +598,14 @@ void PeakCanBackendPrivate::startWrite() st = ::CAN_Write(channelIndex, &message); } - if (Q_UNLIKELY(st != PCAN_ERROR_OK)) - q->setError(systemErrorString(st), QCanBusDevice::WriteError); - else + if (Q_UNLIKELY(st != PCAN_ERROR_OK)) { + const QString errorString = systemErrorString(st); + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Cannot write frame: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::WriteError); + } else { emit q->framesWritten(qint64(1)); + } if (q->hasOutgoingFrames() && !writeNotifier->isActive()) writeNotifier->start(); @@ -611,7 +637,7 @@ void PeakCanBackendPrivate::startRead() continue; const int size = dlcToSize(static_cast<CanFrameDlc>(message.DLC)); - QCanBusFrame frame(message.ID, + QCanBusFrame frame(TPCANLongToFrameID(message.ID), QByteArray(reinterpret_cast<const char *>(message.DATA), size)); frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(static_cast<qint64>(timestamp))); frame.setExtendedFrameFormat(message.MSGTYPE & PCAN_MESSAGE_EXTENDED); @@ -641,7 +667,7 @@ void PeakCanBackendPrivate::startRead() continue; const int size = static_cast<int>(message.LEN); - QCanBusFrame frame(message.ID, + QCanBusFrame frame(TPCANLongToFrameID(message.ID), QByteArray(reinterpret_cast<const char *>(message.DATA), size)); const quint64 millis = timestamp.millis + Q_UINT64_C(0xFFFFFFFF) * timestamp.millis_overflow; const quint64 micros = Q_UINT64_C(1000) * millis + timestamp.micros; @@ -662,8 +688,9 @@ bool PeakCanBackendPrivate::verifyBitRate(int bitrate) Q_Q(PeakCanBackend); if (Q_UNLIKELY(isOpen)) { - q->setError(PeakCanBackend::tr("Cannot change bitrate for already opened device."), - QCanBusDevice::ConfigurationError); + const char errorString[] = "Cannot change bitrate for already opened device."; + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, errorString); + q->setError(PeakCanBackend::tr(errorString), QCanBusDevice::ConfigurationError); return false; } @@ -674,6 +701,7 @@ bool PeakCanBackendPrivate::verifyBitRate(int bitrate) isValidBitrate = bitrateCodeFromBitrate(bitrate) != PCAN_BAUD_INVALID; if (Q_UNLIKELY(!isValidBitrate)) { + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unsupported bitrate value: %d.", bitrate); q->setError(PeakCanBackend::tr("Unsupported bitrate value: %1.").arg(bitrate), QCanBusDevice::ConfigurationError); } @@ -689,6 +717,12 @@ PeakCanBackend::PeakCanBackend(const QString &name, QObject *parent) d->setupChannel(name.toLatin1()); d->setupDefaultConfigurations(); + + std::function<void()> f = std::bind(&PeakCanBackend::resetController, this); + setResetControllerFunction(f); + + std::function<CanBusStatus()> g = std::bind(&PeakCanBackend::busStatus, this); + setCanBusStatusGetter(g); } PeakCanBackend::~PeakCanBackend() @@ -709,11 +743,11 @@ bool PeakCanBackend::open() if (Q_UNLIKELY(!d->open())) return false; - // apply all stored configurations except bitrate, because - // the bitrate can not be applied after opening of device + // Apply all stored configurations except bitrate, because + // the bitrate cannot be changed after opening the device const auto keys = configurationKeys(); for (int key : keys) { - if (key == QCanBusDevice::BitRateKey) + if (key == QCanBusDevice::BitRateKey || key == QCanBusDevice::DataBitRateKey) continue; const QVariant param = configurationParameter(key); const bool success = d->setConfigurationParameter(key, param); @@ -780,4 +814,29 @@ QString PeakCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +void PeakCanBackend::resetController() +{ + close(); + open(); +} + +QCanBusDevice::CanBusStatus PeakCanBackend::busStatus() const +{ + const TPCANStatus status = ::CAN_GetStatus(d_ptr->channelIndex); + + switch (status & PCAN_ERROR_ANYBUSERR) { + case PCAN_ERROR_OK: + return QCanBusDevice::CanBusStatus::Good; + case PCAN_ERROR_BUSWARNING: + return QCanBusDevice::CanBusStatus::Warning; + case PCAN_ERROR_BUSPASSIVE: + return QCanBusDevice::CanBusStatus::Error; + case PCAN_ERROR_BUSOFF: + return QCanBusDevice::CanBusStatus::BusOff; + default: + qCWarning(QT_CANBUS_PLUGINS_PEAKCAN, "Unknown CAN bus status: %lu.", ulong(status)); + return QCanBusDevice::CanBusStatus::Unknown; + } +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/peakcan/peakcanbackend.h b/src/plugins/canbus/peakcan/peakcanbackend.h index 7f7083e..a9e108e 100644 --- a/src/plugins/canbus/peakcan/peakcanbackend.h +++ b/src/plugins/canbus/peakcan/peakcanbackend.h @@ -72,6 +72,9 @@ public: static QList<QCanBusDeviceInfo> interfaces(); private: + void resetController(); + CanBusStatus busStatus() const; + PeakCanBackendPrivate * const d_ptr; }; diff --git a/src/plugins/canbus/socketcan/libsocketcan.cpp b/src/plugins/canbus/socketcan/libsocketcan.cpp new file mode 100644 index 0000000..c6144db --- /dev/null +++ b/src/plugins/canbus/socketcan/libsocketcan.cpp @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Andre Hartmann <aha_1980@gmx.de> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtSerialBus module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "libsocketcan.h" + +#include <QtCore/qloggingcategory.h> + +#if QT_CONFIG(library) +# include <QtCore/qlibrary.h> +#endif + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_SOCKETCAN) + +#define GENERATE_SYMBOL(returnType, symbolName, ...) \ + typedef returnType (*fp_##symbolName)(__VA_ARGS__); \ + static fp_##symbolName symbolName = nullptr; + +#define RESOLVE_SYMBOL(symbolName) \ + symbolName = reinterpret_cast<fp_##symbolName>(library->resolve(#symbolName)); \ + if (!symbolName) \ + return false; + +struct can_bittiming { + quint32 bitrate = 0; /* Bit-rate in bits/second */ + quint32 sample_point = 0; /* Sample point in one-tenth of a percent */ + quint32 tq = 0; /* Time quanta (TQ) in nanoseconds */ + quint32 prop_seg = 0; /* Propagation segment in TQs */ + quint32 phase_seg1 = 0; /* Phase buffer segment 1 in TQs */ + quint32 phase_seg2 = 0; /* Phase buffer segment 2 in TQs */ + quint32 sjw = 0; /* Synchronization jump width in TQs */ + quint32 brp = 0; /* Bit-rate prescaler */ +}; + +enum can_state { + CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */ + CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */ + CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */ + CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */ + CAN_STATE_STOPPED, /* Device is stopped */ + CAN_STATE_SLEEPING, /* Device is sleeping */ + CAN_STATE_MAX +}; + +GENERATE_SYMBOL(int, can_do_restart, const char * /* name */) +GENERATE_SYMBOL(int, can_do_stop, const char * /* name */) +GENERATE_SYMBOL(int, can_do_start, const char * /* name */) +GENERATE_SYMBOL(int, can_set_bitrate, const char * /* name */, quint32 /* bitrate */) +GENERATE_SYMBOL(int, can_get_bittiming, const char * /* name */, struct can_bittiming * /* bt */) +GENERATE_SYMBOL(int, can_get_state, const char * /* name */, int * /* state */) + +LibSocketCan::LibSocketCan(QString *errorString) +{ +#if QT_CONFIG(library) + auto resolveSymbols = [](QLibrary *library) { + if (!library->isLoaded()) { + library->setFileName(QStringLiteral("socketcan")); + if (!library->load()) + return false; + } + + RESOLVE_SYMBOL(can_do_start); + RESOLVE_SYMBOL(can_do_stop); + RESOLVE_SYMBOL(can_do_restart); + RESOLVE_SYMBOL(can_set_bitrate); + RESOLVE_SYMBOL(can_get_bittiming); + RESOLVE_SYMBOL(can_get_state); + + return true; + }; + + QLibrary lib; + if (Q_UNLIKELY(!resolveSymbols(&lib))) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(lib.errorString())); + if (errorString) + *errorString = lib.errorString(); + } +#else + const QString error = + QObject::tr("Cannot load library libsocketcan as Qt was built without QLibrary."); + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(error)); + if (errorString) + *errorString = error; +#endif +} + +/*! + Brings the CAN \a interface up. + + \internal + \note Requires appropriate permissions. +*/ +bool LibSocketCan::start(const QString &interface) +{ + if (!::can_do_start) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_start() is not available."); + return false; + } + + return ::can_do_start(interface.toLatin1().constData()) == 0; +} + +/*! + Brings the CAN \a interface down. + + \internal + \note Requires appropriate permissions. +*/ +bool LibSocketCan::stop(const QString &interface) +{ + if (!::can_do_stop) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_stop() is not available."); + return false; + } + + return ::can_do_stop(interface.toLatin1().constData()) == 0; +} + +/*! + Performs a CAN controller reset on the CAN \a interface. + + \internal + \note Reset can only be triggerd if the controller is in bus off + and the auto restart not turned on. + \note Requires appropriate permissions. + */ +bool LibSocketCan::restart(const QString &interface) +{ + if (!::can_do_restart) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_do_restart() is not available."); + return false; + } + + return ::can_do_restart(interface.toLatin1().constData()) == 0; +} + +/*! + Returns the configured bitrate for \a interface. + \internal +*/ +quint32 LibSocketCan::bitrate(const QString &interface) const +{ + if (!::can_get_bittiming) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_get_bittiming() is not available."); + return 0; + } + + struct can_bittiming bt; + if (::can_get_bittiming(interface.toLatin1().constData(), &bt) == 0) + return bt.bitrate; + + return 0; +} + +/*! + Sets the bitrate for the CAN \a interface. + + \internal + \note Requires appropriate permissions. + */ +bool LibSocketCan::setBitrate(const QString &interface, quint32 bitrate) +{ + if (!::can_set_bitrate) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_set_bitrate() is not available."); + return false; + } + + return ::can_set_bitrate(interface.toLatin1().constData(), bitrate) == 0; +} + +bool LibSocketCan::hasBusStatus() const +{ + return ::can_get_state != nullptr; +} + +QCanBusDevice::CanBusStatus LibSocketCan::busStatus(const QString &interface) const +{ + if (!::can_get_state) { + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "Function can_get_state() is not available."); + return QCanBusDevice::CanBusStatus::Unknown; + } + + int status = 0; + int result = ::can_get_state(interface.toLatin1().constData(), &status); + + if (result < 0) + return QCanBusDevice::CanBusStatus::Unknown; + + switch (status) { + case CAN_STATE_ERROR_ACTIVE: + return QCanBusDevice::CanBusStatus::Good; + case CAN_STATE_ERROR_WARNING: + return QCanBusDevice::CanBusStatus::Warning; + case CAN_STATE_ERROR_PASSIVE: + return QCanBusDevice::CanBusStatus::Error; + case CAN_STATE_BUS_OFF: + return QCanBusDevice::CanBusStatus::BusOff; + default: + // Device is stopped or sleeping, so status is unknown + return QCanBusDevice::CanBusStatus::Unknown; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/canbus/socketcan/libsocketcan.h b/src/plugins/canbus/socketcan/libsocketcan.h new file mode 100644 index 0000000..b77afa1 --- /dev/null +++ b/src/plugins/canbus/socketcan/libsocketcan.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Andre Hartmann <aha_1980@gmx.de> +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtSerialBus module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LIBSOCKETCAN_H +#define LIBSOCKETCAN_H + +#include <QtCore/qglobal.h> +#include <QtSerialBus/qcanbusdevice.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QString; + +class LibSocketCan final +{ +public: + explicit LibSocketCan(QString *errorString = nullptr); + + bool start(const QString &interface); + bool stop(const QString &interface); + bool restart(const QString &interface); + + quint32 bitrate(const QString &interface) const; + bool setBitrate(const QString &interface, quint32 bitrate); + + bool hasBusStatus() const; + QCanBusDevice::CanBusStatus busStatus(const QString &interface) const; +}; + +QT_END_NAMESPACE + +#endif // LIBSOCKETCAN_H diff --git a/src/plugins/canbus/socketcan/socketcan.pro b/src/plugins/canbus/socketcan/socketcan.pro index 20a2c5d..cd731fd 100644 --- a/src/plugins/canbus/socketcan/socketcan.pro +++ b/src/plugins/canbus/socketcan/socketcan.pro @@ -3,9 +3,11 @@ TARGET = qtsocketcanbus QT = core serialbus HEADERS += \ + libsocketcan.h \ socketcanbackend.h SOURCES += \ + libsocketcan.cpp \ main.cpp \ socketcanbackend.cpp diff --git a/src/plugins/canbus/socketcan/socketcanbackend.cpp b/src/plugins/canbus/socketcan/socketcanbackend.cpp index a2da146..2ed1310 100644 --- a/src/plugins/canbus/socketcan/socketcanbackend.cpp +++ b/src/plugins/canbus/socketcan/socketcanbackend.cpp @@ -36,6 +36,10 @@ #include "socketcanbackend.h" +#include "libsocketcan.h" + +#include <QtSerialBus/qcanbusdevice.h> + #include <QtCore/qdatastream.h> #include <QtCore/qdebug.h> #include <QtCore/qdiriterator.h> @@ -182,7 +186,25 @@ QList<QCanBusDeviceInfo> SocketCanBackend::interfaces() SocketCanBackend::SocketCanBackend(const QString &name) : canSocketName(name) { + QString errorString; + libSocketCan.reset(new LibSocketCan(&errorString)); + if (Q_UNLIKELY(!errorString.isEmpty())) { + qCInfo(QT_CANBUS_PLUGINS_SOCKETCAN, + "Cannot load library libsocketcan, some functionality will not be available.\n%ls", + qUtf16Printable(errorString)); + } + resetConfigurations(); + + std::function<void()> f = std::bind(&SocketCanBackend::resetController, this); + setResetControllerFunction(f); + + if (hasBusStatus()) { + // Only register busStatus when libsocketcan is available + // QCanBusDevice::hasBusStatus() will return false otherwise + std::function<CanBusStatus()> g = std::bind(&SocketCanBackend::busStatus, this); + setCanBusStatusGetter(g); + } } SocketCanBackend::~SocketCanBackend() @@ -201,6 +223,8 @@ void SocketCanBackend::resetConfigurations() QVariant::fromValue(QCanBusFrame::FrameErrors(QCanBusFrame::AnyError))); QCanBusDevice::setConfigurationParameter( QCanBusDevice::CanFdKey, false); + QCanBusDevice::setConfigurationParameter( + QCanBusDevice::BitRateKey, 500000); } bool SocketCanBackend::open() @@ -346,6 +370,12 @@ bool SocketCanBackend::applyConfigurationParameter(int key, const QVariant &valu success = true; break; } + case QCanBusDevice::BitRateKey: + { + const quint32 bitRate = value.toUInt(); + libSocketCan->setBitrate(canSocketName, bitRate); + break; + } default: setError(tr("SocketCanBackend: No such configuration as %1 in SocketCanBackend").arg(key), QCanBusDevice::CanBusError::ConfigurationError); @@ -359,7 +389,7 @@ bool SocketCanBackend::connectSocket() { struct ifreq interface; - if (Q_UNLIKELY((canSocket = socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, CAN_RAW)) < 0)) { + if (Q_UNLIKELY((canSocket = socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, protocol)) < 0)) { setError(qt_error_string(errno), QCanBusDevice::CanBusError::ConnectionError); return false; @@ -434,6 +464,16 @@ void SocketCanBackend::setConfigurationParameter(int key, const QVariant &value) return; } } + } else if (key == QCanBusDevice::ProtocolKey) { + bool ok = false; + const int newProtocol = value.toInt(&ok); + if (Q_UNLIKELY(!ok || (newProtocol < 0))) { + const QString errorString = tr("Cannot set protocol to value %1.").arg(value.toString()); + setError(errorString, QCanBusDevice::ConfigurationError); + qCWarning(QT_CANBUS_PLUGINS_SOCKETCAN, "%ls", qUtf16Printable(errorString)); + return; + } + protocol = newProtocol; } // connected & params not applyable/invalid if (canSocket != -1 && !applyConfigurationParameter(key, value)) @@ -731,4 +771,19 @@ void SocketCanBackend::readSocket() enqueueReceivedFrames(newFrames); } +void SocketCanBackend::resetController() +{ + libSocketCan->restart(canSocketName); +} + +bool SocketCanBackend::hasBusStatus() const +{ + return libSocketCan->hasBusStatus(); +} + +QCanBusDevice::CanBusStatus SocketCanBackend::busStatus() const +{ + return libSocketCan->busStatus(canSocketName); +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/socketcan/socketcanbackend.h b/src/plugins/canbus/socketcan/socketcanbackend.h index b49d267..0497244 100644 --- a/src/plugins/canbus/socketcan/socketcanbackend.h +++ b/src/plugins/canbus/socketcan/socketcanbackend.h @@ -52,8 +52,12 @@ #include <linux/can.h> #include <sys/time.h> +#include <memory> + QT_BEGIN_NAMESPACE +class LibSocketCan; + class SocketCanBackend : public QCanBusDevice { Q_OBJECT @@ -79,7 +83,11 @@ private: void resetConfigurations(); bool connectSocket(); bool applyConfigurationParameter(int key, const QVariant &value); + void resetController(); + bool hasBusStatus() const; + QCanBusDevice::CanBusStatus busStatus() const; + int protocol = CAN_RAW; canfd_frame m_frame; sockaddr_can m_address; msghdr m_msg; @@ -89,6 +97,7 @@ private: qint64 canSocket = -1; QSocketNotifier *notifier = nullptr; + std::unique_ptr<LibSocketCan> libSocketCan; QString canSocketName; bool canFdOptionEnabled = false; }; diff --git a/src/plugins/canbus/systeccan/systeccan_symbols_p.h b/src/plugins/canbus/systeccan/systeccan_symbols_p.h index 102817c..ce32422 100644 --- a/src/plugins/canbus/systeccan/systeccan_symbols_p.h +++ b/src/plugins/canbus/systeccan/systeccan_symbols_p.h @@ -81,6 +81,20 @@ typedef void (DRV_CALLBACK_TYPE *tCallbackFktEx) (tUcanHandle handle, quint32 ev #define USBCAN_EVENT_FATALDISCON 8 // a USB-CANmodul has been disconnected during operation #define USBCAN_EVENT_RESERVED1 0x80 +// CAN status flags (is returned with function UcanGetStatus() or UcanGetStatusEx() ) +#define USBCAN_CANERR_OK 0x0000 // no error +#define USBCAN_CANERR_XMTFULL 0x0001 // Tx-buffer of the CAN controller is full +#define USBCAN_CANERR_OVERRUN 0x0002 // Rx-buffer of the CAN controller is full +#define USBCAN_CANERR_BUSLIGHT 0x0004 // Bus error: Error Limit 1 exceeded (refer to SJA1000 manual) +#define USBCAN_CANERR_BUSHEAVY 0x0008 // Bus error: Error Limit 2 exceeded (refer to SJA1000 manual) +#define USBCAN_CANERR_BUSOFF 0x0010 // Bus error: CAN controller has gone into Bus-Off state +#define USBCAN_CANERR_QRCVEMPTY 0x0020 // RcvQueue is empty +#define USBCAN_CANERR_QOVERRUN 0x0040 // RcvQueue overrun +#define USBCAN_CANERR_QXMTFULL 0x0080 // transmit queue is full +#define USBCAN_CANERR_REGTEST 0x0100 // Register test of the SJA1000 failed +#define USBCAN_CANERR_MEMTEST 0x0200 // Memory test failed +#define USBCAN_CANERR_TXMSGLOST 0x0400 // transmit CAN message was automatically deleted by firmware + #define kUcanModeNormal 0x00 // normal mode (send and receive) #define kUcanModeListenOnly 0x01 // listen only mode (only receive) #define kUcanModeTxEcho 0x02 // CAN messages which was sent will be received at UcanReadCanMsg.. @@ -88,8 +102,8 @@ typedef void (DRV_CALLBACK_TYPE *tCallbackFktEx) (tUcanHandle handle, quint32 ev #define kUcanModeHighResTimer 0x08 // high resolution time stamps in received CAN messages (only available with STM derivates) // ABR and ACR for mode "receive all CAN messages" -#define USBCAN_AMR_ALL (quint32) 0xffffffff -#define USBCAN_ACR_ALL (quint32) 0x00000000 +#define USBCAN_AMR_ALL 0xffffffffU +#define USBCAN_ACR_ALL 0x00000000U #define USBCAN_OCR_DEFAULT 0x1A // default OCR for standard GW-002 #define USBCAN_OCR_RS485_ISOLATED 0x1E // OCR for RS485 interface and galvanic isolation @@ -143,6 +157,11 @@ typedef struct _tCanMsgStruct { quint32 m_dwTime; // Time in ms } tCanMsgStruct; +typedef struct _tStatusStruct { + quint16 m_wCanStatus; // current CAN status + quint16 m_wUsbStatus; // current USB status +} tStatusStruct; + // Function return codes (encoding) #define USBCAN_SUCCESSFUL 0x00 // no error #define USBCAN_ERR 0x01 // error in library; function has not been executed @@ -280,7 +299,9 @@ GENERATE_SYMBOL_VARIABLE(UCANRET, UcanDeinitHardware, tUcanHandle) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanInitCanEx2, tUcanHandle, quint8 /* channel */, tUcanInitCanParam *) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanDeinitCanEx, tUcanHandle, quint8 /* channel */) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanReadCanMsgEx, tUcanHandle, quint8 *, tCanMsgStruct *, quint32 *) +GENERATE_SYMBOL_VARIABLE(UCANRET, UcanResetCan, tUcanHandle) GENERATE_SYMBOL_VARIABLE(UCANRET, UcanWriteCanMsgEx, tUcanHandle, quint8, tCanMsgStruct *, quint32 *) +GENERATE_SYMBOL_VARIABLE(UCANRET, UcanGetStatus, tUcanHandle, tStatusStruct *) inline bool resolveSymbols(QLibrary *systecLibrary) { @@ -300,7 +321,9 @@ inline bool resolveSymbols(QLibrary *systecLibrary) RESOLVE_SYMBOL(UcanInitCanEx2); RESOLVE_SYMBOL(UcanDeinitCanEx); RESOLVE_SYMBOL(UcanReadCanMsgEx); + RESOLVE_SYMBOL(UcanResetCan); RESOLVE_SYMBOL(UcanWriteCanMsgEx); + RESOLVE_SYMBOL(UcanGetStatus); return true; } diff --git a/src/plugins/canbus/systeccan/systeccanbackend.cpp b/src/plugins/canbus/systeccan/systeccanbackend.cpp index 3fdd135..0663f39 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend.cpp +++ b/src/plugins/canbus/systeccan/systeccanbackend.cpp @@ -114,7 +114,7 @@ QList<QCanBusDeviceInfo> SystecCanBackend::interfaces() { QList<QCanBusDeviceInfo> result; - ::UcanEnumerateHardware(&ucanEnumCallback, &result, false, 0, ~0, 0, ~0, 0, ~0); + ::UcanEnumerateHardware(&ucanEnumCallback, &result, false, 0, quint8(~0), 0, quint32(~0), 0, quint32(~0)); return result; } @@ -281,8 +281,8 @@ bool SystecCanBackendPrivate::setupChannel(const QString &interfaceName) const QRegularExpressionMatch match = re.match(interfaceName); if (Q_LIKELY(match.hasMatch())) { - device = match.captured(1).toInt(); - channel = match.captured(2).toInt(); + device = quint8(match.captured(1).toUShort()); + channel = quint8(match.captured(2).toUShort()); } else { q->setError(SystecCanBackend::tr("Invalid interface '%1'.") .arg(interfaceName), QCanBusDevice::ConnectionError); @@ -377,7 +377,7 @@ void SystecCanBackendPrivate::startWrite() ::memset(&message, 0, sizeof(message)); message.m_dwID = frame.frameId(); - message.m_bDLC = payload.size(); + message.m_bDLC = quint8(payload.size()); message.m_bFF = frame.hasExtendedFrameFormat() ? USBCAN_MSG_FF_EXT : USBCAN_MSG_FF_STD; @@ -454,6 +454,40 @@ bool SystecCanBackendPrivate::verifyBitRate(int bitrate) return true; } +void SystecCanBackendPrivate::resetController() +{ + ::UcanResetCan(handle); +} + +QCanBusDevice::CanBusStatus SystecCanBackendPrivate::busStatus() +{ + Q_Q(SystecCanBackend); + + tStatusStruct status; + ::memset(&status, 0, sizeof(status)); + const UCANRET result = ::UcanGetStatus(handle, &status); + + if (Q_UNLIKELY(result != USBCAN_SUCCESSFUL)) { + qCWarning(QT_CANBUS_PLUGINS_SYSTECCAN, "Can not query CAN bus status."); + q->setError(SystecCanBackend::tr("Can not query CAN bus status."), QCanBusDevice::ConfigurationError); + return QCanBusDevice::CanBusStatus::Unknown; + } + + if (status.m_wCanStatus & USBCAN_CANERR_BUSOFF) + return QCanBusDevice::CanBusStatus::BusOff; + + if (status.m_wCanStatus & USBCAN_CANERR_BUSHEAVY) + return QCanBusDevice::CanBusStatus::Error; + + if (status.m_wCanStatus & USBCAN_CANERR_BUSLIGHT) + return QCanBusDevice::CanBusStatus::Warning; + + if (status.m_wCanStatus == USBCAN_CANERR_OK) + return QCanBusDevice::CanBusStatus::Good; + + return QCanBusDevice::CanBusStatus::Unknown; +} + SystecCanBackend::SystecCanBackend(const QString &name, QObject *parent) : QCanBusDevice(parent), d_ptr(new SystecCanBackendPrivate(this)) @@ -462,6 +496,12 @@ SystecCanBackend::SystecCanBackend(const QString &name, QObject *parent) : d->setupChannel(name); d->setupDefaultConfigurations(); + + std::function<void()> f = std::bind(&SystecCanBackend::resetController, this); + setResetControllerFunction(f); + + std::function<CanBusStatus()> g = std::bind(&SystecCanBackend::busStatus, this); + setCanBusStatusGetter(g); } SystecCanBackend::~SystecCanBackend() @@ -553,4 +593,17 @@ QString SystecCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +void SystecCanBackend::resetController() +{ + Q_D(SystecCanBackend); + d->resetController(); +} + +QCanBusDevice::CanBusStatus SystecCanBackend::busStatus() +{ + Q_D(SystecCanBackend); + + return d->busStatus(); +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/systeccan/systeccanbackend.h b/src/plugins/canbus/systeccan/systeccanbackend.h index cb62808..22c1193 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend.h +++ b/src/plugins/canbus/systeccan/systeccanbackend.h @@ -76,6 +76,9 @@ public: int channelNumber); private: + void resetController(); + QCanBusDevice::CanBusStatus busStatus(); + SystecCanBackendPrivate * const d_ptr; }; diff --git a/src/plugins/canbus/systeccan/systeccanbackend_p.h b/src/plugins/canbus/systeccan/systeccanbackend_p.h index 28eb7ff..b2da322 100644 --- a/src/plugins/canbus/systeccan/systeccanbackend_p.h +++ b/src/plugins/canbus/systeccan/systeccanbackend_p.h @@ -94,6 +94,8 @@ public: void startWrite(); void readAllReceivedMessages(); bool verifyBitRate(int bitrate); + void resetController(); + QCanBusDevice::CanBusStatus busStatus(); SystecCanBackend * const q_ptr; diff --git a/src/plugins/canbus/tinycan/tinycanbackend.cpp b/src/plugins/canbus/tinycan/tinycanbackend.cpp index 3891186..411b917 100644 --- a/src/plugins/canbus/tinycan/tinycanbackend.cpp +++ b/src/plugins/canbus/tinycan/tinycanbackend.cpp @@ -405,8 +405,7 @@ void TinyCanBackendPrivate::startRead() } else { if (status.CanStatus == CAN_STATUS_BUS_OFF) { qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "CAN bus is in off state, trying to reset the bus."); - if (::CanSetMode(channelIndex, OP_CAN_RESET, CAN_CMD_NONE) < 0) - q->setError(systemErrorString(ret), QCanBusDevice::CanBusError::ReadError); + resetController(); } } @@ -446,7 +445,8 @@ void TinyCanBackendPrivate::startupDriver() ::CanSetEvents(EVENT_ENABLE_RX_MESSAGES); } else if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_TINYCAN, "Wrong driver reference counter: %d", + driverRefCount); return; } @@ -458,7 +458,8 @@ void TinyCanBackendPrivate::cleanupDriver() --driverRefCount; if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_TINYCAN, "Wrong driver reference counter: %d", + driverRefCount); driverRefCount = 0; } else if (driverRefCount == 0) { ::CanSetEvents(EVENT_DISABLE_ALL); @@ -466,6 +467,18 @@ void TinyCanBackendPrivate::cleanupDriver() } } +void TinyCanBackendPrivate::resetController() +{ + Q_Q(TinyCanBackend); + qint32 ret = ::CanSetMode(channelIndex, OP_CAN_RESET, CAN_CMD_NONE); + if (Q_UNLIKELY(ret < 0)) { + const QString errorString = systemErrorString(ret); + qCWarning(QT_CANBUS_PLUGINS_TINYCAN, "Cannot perform hardware reset: %ls", + qUtf16Printable(errorString)); + q->setError(errorString, QCanBusDevice::CanBusError::ConfigurationError); + } +} + bool TinyCanBackendPrivate::setBitRate(int bitrate) { Q_Q(TinyCanBackend); @@ -496,6 +509,9 @@ TinyCanBackend::TinyCanBackend(const QString &name, QObject *parent) d->setupChannel(name); d->setupDefaultConfigurations(); + + std::function<void()> f = std::bind(&TinyCanBackend::resetController, this); + setResetControllerFunction(f); } TinyCanBackend::~TinyCanBackend() @@ -589,4 +605,10 @@ QString TinyCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +void TinyCanBackend::resetController() +{ + Q_D(TinyCanBackend); + d->resetController(); +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/tinycan/tinycanbackend.h b/src/plugins/canbus/tinycan/tinycanbackend.h index 5f504ca..428e9bc 100644 --- a/src/plugins/canbus/tinycan/tinycanbackend.h +++ b/src/plugins/canbus/tinycan/tinycanbackend.h @@ -72,6 +72,8 @@ public: static QList<QCanBusDeviceInfo> interfaces(); private: + void resetController(); + TinyCanBackendPrivate * const d_ptr; }; diff --git a/src/plugins/canbus/tinycan/tinycanbackend_p.h b/src/plugins/canbus/tinycan/tinycanbackend_p.h index 905175c..25316a2 100644 --- a/src/plugins/canbus/tinycan/tinycanbackend_p.h +++ b/src/plugins/canbus/tinycan/tinycanbackend_p.h @@ -75,6 +75,7 @@ public: void startRead(); void startupDriver(); void cleanupDriver(); + void resetController(); bool setBitRate(int bitrate); diff --git a/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h index 3f2501f..8c1ad52 100644 --- a/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h +++ b/src/plugins/canbus/vectorcan/vectorcan_symbols_p.h @@ -249,9 +249,27 @@ static_assert(sizeof(s_xl_can_msg) == 32, "Invalid size of s_xl_can_msg structur #define XL_TRANSCEIVER_EVENT_ERROR 1 #define XL_TRANSCEIVER_EVENT_CHANGED 2 +#define XL_CHIPSTAT_BUSOFF 0x01 +#define XL_CHIPSTAT_ERROR_PASSIVE 0x02 +#define XL_CHIPSTAT_ERROR_WARNING 0x04 +#define XL_CHIPSTAT_ERROR_ACTIVE 0x08 + +#define XL_CAN_STATE_FLAG_SJA_MODE 0x00000001 + +// CAN Chip status +struct s_xl_chip_state { + unsigned char busStatus; + unsigned char txErrorCounter; + unsigned char rxErrorCounter; + unsigned char chipState; // raw Status Register Value + unsigned int flags; +}; +static_assert(sizeof(s_xl_chip_state) == 8, "Invalid size of s_xl_chip_state structure"); + // basic bus message structure union s_xl_tag_data { struct s_xl_can_msg msg; + struct s_xl_chip_state chipState; }; // event type definition (48 bytes) @@ -456,6 +474,7 @@ GENERATE_SYMBOL_VARIABLE(XLstatus, xlCanSetChannelBitrate, XLportHandle, XLacces GENERATE_SYMBOL_VARIABLE(XLstatus, xlCanTransmit, XLportHandle, XLaccess, quint32 *, void *) GENERATE_SYMBOL_VARIABLE(XLstatus, xlReceive, XLportHandle, quint32 *, XLevent *) GENERATE_SYMBOL_VARIABLE(XLstatus, xlSetNotification, XLportHandle, XLhandle *, int) +GENERATE_SYMBOL_VARIABLE(XLstatus, xlCanRequestChipState, XLportHandle, XLaccess) GENERATE_SYMBOL_VARIABLE(char *, xlGetErrorString, XLstatus) inline bool resolveSymbols(QLibrary *vectorcanLibrary) @@ -481,6 +500,7 @@ inline bool resolveSymbols(QLibrary *vectorcanLibrary) RESOLVE_SYMBOL(xlCanTransmit) RESOLVE_SYMBOL(xlReceive) RESOLVE_SYMBOL(xlSetNotification) + RESOLVE_SYMBOL(xlCanRequestChipState) RESOLVE_SYMBOL(xlGetErrorString) return true; diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend.cpp b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp index 9026888..9d3bdef 100644 --- a/src/plugins/canbus/vectorcan/vectorcanbackend.cpp +++ b/src/plugins/canbus/vectorcan/vectorcanbackend.cpp @@ -271,7 +271,8 @@ void VectorCanBackendPrivate::setupChannel(const QString &interfaceName) } } - qCritical("Unable to parse the channel %ls", qUtf16Printable(interfaceName)); + qCCritical(QT_CANBUS_PLUGINS_VECTORCAN, "Unable to parse the channel %ls", + qUtf16Printable(interfaceName)); } void VectorCanBackendPrivate::setupDefaultConfigurations() @@ -388,7 +389,8 @@ XLstatus VectorCanBackendPrivate::loadDriver() return status; } else if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_VECTORCAN, "Wrong driver reference counter: %d", + driverRefCount); return XL_ERR_CANNOT_OPEN_DRIVER; } @@ -412,7 +414,8 @@ void VectorCanBackendPrivate::cleanupDriver() --driverRefCount; if (Q_UNLIKELY(driverRefCount < 0)) { - qCritical("Wrong reference counter: %d", driverRefCount); + qCCritical(QT_CANBUS_PLUGINS_VECTORCAN, "Wrong driver reference counter: %d", + driverRefCount); driverRefCount = 0; } else if (driverRefCount == 0) { ::xlCloseDriver(); @@ -443,6 +446,9 @@ VectorCanBackend::VectorCanBackend(const QString &name, QObject *parent) d->setupChannel(name); d->setupDefaultConfigurations(); + + std::function<CanBusStatus()> g = std::bind(&VectorCanBackend::busStatus, this); + setCanBusStatusGetter(g); } VectorCanBackend::~VectorCanBackend() @@ -537,4 +543,48 @@ QString VectorCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame) return QString(); } +QCanBusDevice::CanBusStatus VectorCanBackend::busStatus() +{ + Q_D(VectorCanBackend); + + const XLstatus requestStatus = ::xlCanRequestChipState(d->portHandle, d->channelMask); + if (Q_UNLIKELY(requestStatus != XL_SUCCESS)) { + const QString errorString = d->systemErrorString(requestStatus); + qCWarning(QT_CANBUS_PLUGINS_VECTORCAN, "Can not query CAN bus status: %ls.", + qUtf16Printable(errorString)); + setError(errorString, QCanBusDevice::CanBusError::ReadError); + return QCanBusDevice::CanBusStatus::Unknown; + } + + quint32 eventCount = 1; + XLevent event; + ::memset(&event, 0, sizeof(event)); + + const XLstatus receiveStatus = ::xlReceive(d->portHandle, &eventCount, &event); + if (Q_UNLIKELY(receiveStatus != XL_SUCCESS)) { + const QString errorString = d->systemErrorString(receiveStatus); + qCWarning(QT_CANBUS_PLUGINS_VECTORCAN, "Can not query CAN bus status: %ls.", + qUtf16Printable(errorString)); + setError(errorString, QCanBusDevice::CanBusError::ReadError); + return QCanBusDevice::CanBusStatus::Unknown; + } + + if (Q_LIKELY(event.tag == XL_CHIP_STATE)) { + switch (event.tagData.chipState.busStatus) { + case XL_CHIPSTAT_BUSOFF: + return QCanBusDevice::CanBusStatus::BusOff; + case XL_CHIPSTAT_ERROR_PASSIVE: + return QCanBusDevice::CanBusStatus::Error; + case XL_CHIPSTAT_ERROR_WARNING: + return QCanBusDevice::CanBusStatus::Warning; + case XL_CHIPSTAT_ERROR_ACTIVE: + return QCanBusDevice::CanBusStatus::Good; + } + } + + qCWarning(QT_CANBUS_PLUGINS_VECTORCAN, "Unknown CAN bus status: %u", + uint(event.tagData.chipState.busStatus)); + return QCanBusDevice::CanBusStatus::Unknown; +} + QT_END_NAMESPACE diff --git a/src/plugins/canbus/vectorcan/vectorcanbackend.h b/src/plugins/canbus/vectorcan/vectorcanbackend.h index 165d369..89b9da6 100644 --- a/src/plugins/canbus/vectorcan/vectorcanbackend.h +++ b/src/plugins/canbus/vectorcan/vectorcanbackend.h @@ -71,6 +71,8 @@ public: static QList<QCanBusDeviceInfo> interfaces(); private: + QCanBusDevice::CanBusStatus busStatus(); + VectorCanBackendPrivate * const d_ptr; }; diff --git a/src/serialbus/doc/src/peakcan.qdoc b/src/serialbus/doc/src/peakcan.qdoc index fd12ab7..a38ddf7 100644 --- a/src/serialbus/doc/src/peakcan.qdoc +++ b/src/serialbus/doc/src/peakcan.qdoc @@ -34,8 +34,9 @@ \l{http://www.peak-system.com/}{PEAK-System} CAN adapters. This plugin requires the PCAN device drivers and the PCAN-Basic library - version 4.0.0 or higher. Supported platforms are Windows (pcanbasic.dll) - and Linux (libpcanbasic.so). + version 4.0.0 or higher on Windows (pcanbasic.dll) and Linux (libpcanbasic.so). + On macOS the plugin requires the PCBUSB library from + \l{http://www.mac-can.com}{UV Software}. \section1 Creating CAN Bus Devices @@ -70,6 +71,8 @@ \note Only the USB and PCI adapters are currently supported by this plugin. + \note On macOS, only USB adapters are currently supported by this plugin. + The device is now open for writing and reading CAN frames: \code @@ -117,4 +120,12 @@ this configuration parameter can only be adjusted while the QCanBusDevice is not connected. \endtable + + PeakCAN supports the following additional functions: + + \list + \li QCanBusDevice::resetController() + \li QCanBusDevice::busStatus() + \endlist + */ diff --git a/src/serialbus/doc/src/socketcan.qdoc b/src/serialbus/doc/src/socketcan.qdoc index df58dae..e578969 100644 --- a/src/serialbus/doc/src/socketcan.qdoc +++ b/src/serialbus/doc/src/socketcan.qdoc @@ -179,9 +179,13 @@ By default, the connection is configured to accept any CAN bus message. \row \li QCanBusDevice::BitRateKey - \li This configuration is not supported by the socketcan plugin. However - it is possible to set the rate when configuring the CAN network interface using - the \c {ip link} command. + \li Determines the bit rate of the CAN bus connection. The following bit rates + are supported: 5000, 10000, 20000, 33000, 47000, 50000, 83000, 95000, + 100000, 125000, 250000, 500000, 800000, 1000000. Note that this configuration + parameter can only be adjusted while the QCanBusDevice is not connected. To set + this configuration parameter, the library libsocketcan is needed at runtime + http://www.pengutronix.de/software/libsocketcan + Usually, root rights are needed to set the CAN bus bitrate. \row \li QCanBusDevice::CanFdKey \li This configuration option determines whether CANFD frames may be sent or received. @@ -192,6 +196,10 @@ \li This configuration is not supported by the socketcan plugin. However it is possible to set the data rate when configuring the CAN network interface using the \c {ip link} command. + \row + \li QCanBusDevice::ProtocolKey + \li Allows to use another protocol inside the protocol family PF_CAN. The default + value for this configuration option is CAN_RAW (1). \endtable For example: @@ -199,4 +207,12 @@ \snippet snippetmain.cpp SocketCan Filter Example Extended frame format and flexible data-rate are supported in SocketCAN. - */ + + SocketCAN supports the following additional functions: + + \list + \li QCanBusDevice::resetController() (needs libsocketcan) + \li QCanBusDevice::busStatus() (needs libsocketcan) + \endlist + +*/ diff --git a/src/serialbus/doc/src/systeccan.qdoc b/src/serialbus/doc/src/systeccan.qdoc index af34665..dc66023 100644 --- a/src/serialbus/doc/src/systeccan.qdoc +++ b/src/serialbus/doc/src/systeccan.qdoc @@ -109,4 +109,12 @@ is disabled by default. If this option is enabled, the therefore received frames are marked with QCanBusFrame::hasLocalEcho() \endtable + + SystecCAN supports the following additional functions: + + \list + \li QCanBusDevice::resetController() + \li QCanBusDevice::busStatus() + \endlist + */ diff --git a/src/serialbus/doc/src/tinycan.qdoc b/src/serialbus/doc/src/tinycan.qdoc index 43349dc..8479576 100644 --- a/src/serialbus/doc/src/tinycan.qdoc +++ b/src/serialbus/doc/src/tinycan.qdoc @@ -95,4 +95,11 @@ \li Determines the bit rate of the CAN bus connection. The following bit rates are supported: 10000, 20000, 50000, 100000, 125000, 250000, 500000, 800000, 1000000. \endtable - */ + + TinyCAN supports the following additional functions: + + \list + \li QCanBusDevice::resetController() + \endlist + +*/ diff --git a/src/serialbus/doc/src/vectorcan.qdoc b/src/serialbus/doc/src/vectorcan.qdoc index eb4acdd..70b6521 100644 --- a/src/serialbus/doc/src/vectorcan.qdoc +++ b/src/serialbus/doc/src/vectorcan.qdoc @@ -106,4 +106,11 @@ was successful. If this option is enabled, the therefore received frames are marked with QCanBusFrame::hasLocalEcho() \endtable + + VectorCAN supports the following additional functions: + + \list + \li QCanBusDevice::busStatus() + \endlist + */ diff --git a/src/serialbus/qcanbusdevice.cpp b/src/serialbus/qcanbusdevice.cpp index 6723f77..222f19c 100644 --- a/src/serialbus/qcanbusdevice.cpp +++ b/src/serialbus/qcanbusdevice.cpp @@ -73,6 +73,11 @@ Q_LOGGING_CATEGORY(QT_CANBUS, "qt.canbus") \value ConfigurationError An error occurred when attempting to set a configuration parameter. \value UnknownError An unknown error occurred. + \value OperationError An operation was attempted while the device was in + a state that did not permit it. This enum was introduced + in Qt 5.14. + \value TimeoutError An timeout occurred while waiting for frames written or + received. This enum was introduced in Qt 5.14. */ /*! @@ -118,6 +123,9 @@ Q_LOGGING_CATEGORY(QT_CANBUS, "qt.canbus") after the arbitration phase at the nominal bitrate is finished. This enum value was introduced in Qt 5.9. See also \c QCanBusDevice::BitRateKey + \value ProtocolKey This key allows to specify another protocol. For now, this + parameter can only be set and used in the SocketCAN plugin. + This enum value was introduced in Qt 5.14. \value UserKey This key defines the range where custom keys start. Its most common purpose is to permit platform-specific configuration options. @@ -142,6 +150,22 @@ Q_LOGGING_CATEGORY(QT_CANBUS, "qt.canbus") */ /*! + \fn bool operator==(const QCanBusDevice::Filter &a, const QCanBusDevice::Filter &b) + \relates QCanBusDevice::Filter + + Returns true, if the filter \a a is equal to the filter \a b, + otherwise returns false. +*/ + +/*! + \fn bool operator!=(const QCanBusDevice::Filter &a, const QCanBusDevice::Filter &b) + \relates QCanBusDevice::Filter + + Returns true, if the filter \a a is not equal to the filter \a b, + otherwise returns false. +*/ + +/*! \enum QCanBusDevice::Filter::FormatFilter This enum describes the format pattern, which is used to filter incoming CAN bus frames. @@ -231,7 +255,7 @@ QCanBusDevice::QCanBusDevice(QObject *parent) : CAN bus implementations must use this function to update the device's error state. - \sa error(), errorOccurred() + \sa error(), errorOccurred(), clearError() */ void QCanBusDevice::setError(const QString &errorText, CanBusError errorId) { @@ -244,6 +268,24 @@ void QCanBusDevice::setError(const QString &errorText, CanBusError errorId) } /*! + \since 5.14 + Clears the error id and the human readable description of the last + device error. + + CAN bus implementations must use this function to update the device's + error state. + + \sa error(), errorOccurred(), setError() +*/ +void QCanBusDevice::clearError() +{ + Q_D(QCanBusDevice); + + d->errorText.clear(); + d->lastError = NoError; +} + +/*! Appends \a newFrames to the internal list of frames which can be accessed using \l readFrame() and emits the \l framesReceived() signal. @@ -303,6 +345,30 @@ bool QCanBusDevice::hasOutgoingFrames() const } /*! + * \since 5.14 + * Called from the derived plugin to register a function that performs the + * CAN controller hardware reset when \a resetController() is called. + */ +void QCanBusDevice::setResetControllerFunction(std::function<void()> resetter) +{ + Q_D(QCanBusDevice); + + d->m_resetControllerFunction = std::move(resetter); +} + +/*! + * \since 5.14 + * Called from the derived plugin to register a function that returns the + * CAN controller bus status when \a busStatus() is called. + */ +void QCanBusDevice::setCanBusStatusGetter(std::function<CanBusStatus()> busStatusGetter) +{ + Q_D(QCanBusDevice); + + d->m_busStatusGetter = std::move(busStatusGetter); +} + +/*! Sets the configuration parameter \a key for the CAN bus connection to \a value. The potential keys are represented by \l ConfigurationKey. @@ -428,6 +494,82 @@ qint64 QCanBusDevice::framesToWrite() const } /*! + \since 5.14 + + Performs a CAN controller reset to release the CAN controller from + bus off state, if possible. + + \note CAN controller resets disturb the running communication and + may take up to one second to complete. Only call this function to + recover from bus errors. + + \note This function may not be implemented in all CAN plugins. + Please refer to the plugins help pages for more information. + + \sa busStatus() +*/ +void QCanBusDevice::resetController() +{ + if (d_func()->m_resetControllerFunction) { + d_func()->m_resetControllerFunction(); + } else { + const char error[] = QT_TRANSLATE_NOOP("QCanBusDevice", + "This CAN bus plugin does not support hardware controller reset."); + qCWarning(QT_CANBUS, error); + setError(tr(error), QCanBusDevice::CanBusError::ConfigurationError); + } +} + +/*! + \since 5.14 + + Return true, if the CAN plugin supports requesting the CAN bus status. + + \sa busStatus() + */ +bool QCanBusDevice::hasBusStatus() const +{ + return d_func()->m_busStatusGetter != nullptr; +} + +/*! + \since 5.14 + \enum QCanBusDevice::CanBusStatus + + This enum describes possible CAN bus status values. + + \value Unknown The CAN bus status is unknown + (e.g. not supported by the CAN plugin). + \value Good The CAN controller is fully operational + \value Warning The CAN controller is in warning status + \value Error The CAN controller is in error status + (no longer sending CAN frames) + \value BusOff The CAN controller is in bus off status + (disconnected from the CAN bus) +*/ + +/*! + \since 5.14 + + Returns the current CAN bus status. If the status cannot be requested, + QCanBusDevice::UnknownStatus is returned. + + \note This function may not be implemented in all CAN plugins. + Please refer to the plugins help pages for more information. + The function hasBusStatus() can be used at runtime to check if + the used CAN plugin has support for requesting the CAN bus status. + + \sa hasBusStatus(), hardwareControllerReset() +*/ +QCanBusDevice::CanBusStatus QCanBusDevice::busStatus() const +{ + if (d_func()->m_busStatusGetter) + return d_func()->m_busStatusGetter(); + + return QCanBusDevice::CanBusStatus::Unknown; +} + +/*! \since 5.12 \enum QCanBusDevice::Direction @@ -454,8 +596,14 @@ void QCanBusDevice::clear(QCanBusDevice::Directions direction) { Q_D(QCanBusDevice); - if (Q_UNLIKELY(d->state != ConnectedState)) + if (Q_UNLIKELY(d->state != ConnectedState)) { + const QString error = tr("Cannot clear buffers as device is not connected."); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + setError(error, CanBusError::OperationError); return; + } + + clearError(); if (direction & Direction::Input) { QMutexLocker(&d->incomingFramesGuard); @@ -492,31 +640,46 @@ bool QCanBusDevice::waitForFramesWritten(int msecs) "recursively. Check that no slot containing waitForFramesReceived() " "is called in response to framesWritten(qint64) or " "errorOccurred(CanBusError) signals."); + setError(tr("QCanBusDevice::waitForFramesWritten() must not be called recursively."), + CanBusError::OperationError); return false; } - QScopedValueRollback<bool> guard(d_func()->waitForWrittenEntered); - d_func()->waitForWrittenEntered = true; - - if (d_func()->state != ConnectedState) + if (Q_UNLIKELY(d_func()->state != ConnectedState)) { + const QString error = tr("Cannot wait for frames written as device is not connected."); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + setError(error, CanBusError::OperationError); return false; + } if (!framesToWrite()) return false; // nothing pending, nothing to wait upon + QScopedValueRollback<bool> guard(d_func()->waitForWrittenEntered); + d_func()->waitForWrittenEntered = true; + + enum { Written = 0, Error, Timeout }; QEventLoop loop; - connect(this, &QCanBusDevice::framesWritten, &loop, [&]() { loop.exit(0); }); - connect(this, &QCanBusDevice::errorOccurred, &loop, [&]() { loop.exit(1); }); + connect(this, &QCanBusDevice::framesWritten, &loop, [&]() { loop.exit(Written); }); + connect(this, &QCanBusDevice::errorOccurred, &loop, [&]() { loop.exit(Error); }); if (msecs >= 0) - QTimer::singleShot(msecs, &loop, [&]() { loop.exit(2); }); + QTimer::singleShot(msecs, &loop, [&]() { loop.exit(Timeout); }); - int result = 0; + int result = Written; while (framesToWrite() > 0) { // wait till all written or time out result = loop.exec(QEventLoop::ExcludeUserInputEvents); - if (result > 0) + if (Q_UNLIKELY(result == Timeout)) { + const QString error = tr("Timeout (%1 ms) during wait for frames written.").arg(msecs); + setError(error, CanBusError::TimeoutError); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + } + + if (result > Written) return false; } + + clearError(); return true; } @@ -545,25 +708,39 @@ bool QCanBusDevice::waitForFramesReceived(int msecs) "recursively. Check that no slot containing waitForFramesReceived() " "is called in response to framesReceived() or " "errorOccurred(CanBusError) signals."); + setError(tr("QCanBusDevice::waitForFramesReceived() must not be called recursively."), + CanBusError::OperationError); + return false; + } + + if (Q_UNLIKELY(d_func()->state != ConnectedState)) { + const QString error = tr("Cannot wait for frames received as device is not connected."); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + setError(error, CanBusError::OperationError); return false; } QScopedValueRollback<bool> guard(d_func()->waitForReceivedEntered); d_func()->waitForReceivedEntered = true; - if (d_func()->state != ConnectedState) - return false; - + enum { Received = 0, Error, Timeout }; QEventLoop loop; - - connect(this, &QCanBusDevice::framesReceived, &loop, [&]() { loop.exit(0); }); - connect(this, &QCanBusDevice::errorOccurred, &loop, [&]() { loop.exit(1); }); + connect(this, &QCanBusDevice::framesReceived, &loop, [&]() { loop.exit(Received); }); + connect(this, &QCanBusDevice::errorOccurred, &loop, [&]() { loop.exit(Error); }); if (msecs >= 0) - QTimer::singleShot(msecs, &loop, [&]() { loop.exit(2); }); + QTimer::singleShot(msecs, &loop, [&]() { loop.exit(Timeout); }); int result = loop.exec(QEventLoop::ExcludeUserInputEvents); - return result == 0; + if (Q_UNLIKELY(result == Timeout)) { + const QString error = tr("Timeout (%1 ms) during wait for frames received.").arg(msecs); + setError(error, CanBusError::TimeoutError); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + } + + if (result == Received) + clearError(); + return result == Received; } /*! @@ -619,8 +796,14 @@ QCanBusFrame QCanBusDevice::readFrame() { Q_D(QCanBusDevice); - if (Q_UNLIKELY(d->state != ConnectedState)) + if (Q_UNLIKELY(d->state != ConnectedState)) { + const QString error = tr("Cannot read frame as device is not connected."); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + setError(error, CanBusError::OperationError); return QCanBusFrame(QCanBusFrame::InvalidFrame); + } + + clearError(); QMutexLocker locker(&d->incomingFramesGuard); @@ -643,8 +826,14 @@ QVector<QCanBusFrame> QCanBusDevice::readAllFrames() { Q_D(QCanBusDevice); - if (Q_UNLIKELY(d->state != ConnectedState)) + if (Q_UNLIKELY(d->state != ConnectedState)) { + const QString error = tr("Cannot read frame as device is not connected."); + qCWarning(QT_CANBUS, "%ls", qUtf16Printable(error)); + setError(error, CanBusError::OperationError); return QVector<QCanBusFrame>(); + } + + clearError(); QMutexLocker locker(&d->incomingFramesGuard); @@ -675,9 +864,10 @@ QVector<QCanBusFrame> QCanBusDevice::readAllFrames() As per CAN bus specification, frames of type \l {QCanBusFrame::RemoteRequestFrame} {remote transfer request (RTR)} - do not have a payload, but a length from 0 to 8 (including). This length indicates - the expected response payload length from the remote party. Therefore when sending a RTR frame using - this function it may still be required to set an arbitrary payload on \a frame. The length of + do not have a payload, but a length from 0 to 8 (including). This length + indicates the expected response payload length from the remote party. + Therefore when sending a RTR frame using this function it may still + be required to set an arbitrary payload on \a frame. The length of the arbitrary payload is what is set as size expectation for the RTR frame. \sa QCanBusFrame::setPayload() @@ -705,8 +895,10 @@ bool QCanBusDevice::connectDevice() Q_D(QCanBusDevice); if (Q_UNLIKELY(d->state != QCanBusDevice::UnconnectedState)) { - setError(tr("Can not connect an already connected device"), - QCanBusDevice::ConnectionError); + const char error[] = QT_TRANSLATE_NOOP("QCanBusDevice", + "Can not connect an already connected device."); + qCWarning(QT_CANBUS, error); + setError(tr(error), QCanBusDevice::ConnectionError); return false; } @@ -717,6 +909,8 @@ bool QCanBusDevice::connectDevice() return false; } + clearError(); + //Connected is set by backend -> might be delayed by event loop return true; } diff --git a/src/serialbus/qcanbusdevice.h b/src/serialbus/qcanbusdevice.h index a47fac4..5d2d976 100644 --- a/src/serialbus/qcanbusdevice.h +++ b/src/serialbus/qcanbusdevice.h @@ -41,6 +41,8 @@ #include <QtSerialBus/qcanbusframe.h> #include <QtSerialBus/qcanbusdeviceinfo.h> +#include <functional> + QT_BEGIN_NAMESPACE class QCanBusDevicePrivate; @@ -58,7 +60,9 @@ public: WriteError, ConnectionError, ConfigurationError, - UnknownError + UnknownError, + OperationError, + TimeoutError }; Q_ENUM(CanBusError) @@ -70,6 +74,15 @@ public: }; Q_ENUM(CanBusDeviceState) + enum class CanBusStatus { + Unknown, + Good, + Warning, + Error, + BusOff + }; + Q_ENUM(CanBusStatus) + enum ConfigurationKey { RawFilterKey = 0, ErrorFilterKey, @@ -78,12 +91,24 @@ public: BitRateKey, CanFdKey, DataBitRateKey, + ProtocolKey, UserKey = 30 }; Q_ENUM(ConfigurationKey) struct Filter { + friend constexpr bool operator==(const Filter &a, const Filter &b) noexcept + { + return a.frameId == b.frameId && a.frameIdMask == b.frameIdMask + && a.type == b.type && a.format == b.format; + } + + friend constexpr bool operator!=(const Filter &a, const Filter &b) noexcept + { + return !operator==(a, b); + } + enum FormatFilter { MatchBaseFormat = 0x0001, MatchExtendedFormat = 0x0002, @@ -109,6 +134,10 @@ public: qint64 framesAvailable() const; qint64 framesToWrite() const; + void resetController(); + bool hasBusStatus() const; + QCanBusDevice::CanBusStatus busStatus() const; + enum Direction { Input = 1, Output = 2, @@ -140,6 +169,7 @@ Q_SIGNALS: protected: void setState(QCanBusDevice::CanBusDeviceState newState); void setError(const QString &errorText, QCanBusDevice::CanBusError); + void clearError(); void enqueueReceivedFrames(const QVector<QCanBusFrame> &newFrames); @@ -152,6 +182,9 @@ protected: virtual bool open() = 0; virtual void close() = 0; + void setResetControllerFunction(std::function<void()> resetter); + void setCanBusStatusGetter(std::function<CanBusStatus()> busStatusGetter); + static QCanBusDeviceInfo createDeviceInfo(const QString &name, bool isVirtual = false, bool isFlexibleDataRateCapable = false); diff --git a/src/serialbus/qcanbusdevice_p.h b/src/serialbus/qcanbusdevice_p.h index faeae65..e74168c 100644 --- a/src/serialbus/qcanbusdevice_p.h +++ b/src/serialbus/qcanbusdevice_p.h @@ -74,6 +74,9 @@ public: bool waitForReceivedEntered = false; bool waitForWrittenEntered = false; + + std::function<void()> m_resetControllerFunction; + std::function<QCanBusDevice::CanBusStatus()> m_busStatusGetter; }; QT_END_NAMESPACE diff --git a/src/serialbus/qmodbusdevice.cpp b/src/serialbus/qmodbusdevice.cpp index 9891d16..ab8e126 100644 --- a/src/serialbus/qmodbusdevice.cpp +++ b/src/serialbus/qmodbusdevice.cpp @@ -98,8 +98,8 @@ QModbusDevice::~QModbusDevice() User options: - \value UserParameter The first parameter that can be used for user-specific - purposes. \c QVariant + \value UserParameter This enum value has been deprecated. There + will be no replacement. */ /*! @@ -143,7 +143,7 @@ QVariant QModbusDevice::connectionParameter(int parameter) const default: break; } - return d->m_userConnectionParams.value(parameter); + return d->m_userConnectionParams.value(parameter); // ### Qt6: remove } /*! @@ -182,7 +182,7 @@ void QModbusDevice::setConnectionParameter(int parameter, const QVariant &value) d->m_networkAddress = value.toString(); break; default: - d->m_userConnectionParams.insert(parameter, value); + d->m_userConnectionParams.insert(parameter, value); // ### Qt6: remove break; } } @@ -337,6 +337,20 @@ QString QModbusDevice::errorString() const } /*! + \since 5.14 + + Returns the underlying \l QIODevice used for ModBus communication or + \c nullptr if the device was not yet fully initialized. + + \note Do not store a pointer to the underlying device, because it can be + invalidated at any point in time. +*/ +QIODevice *QModbusDevice::device() const +{ + return d_func()->device(); +} + +/*! \fn bool QModbusDevice::open() This function is called by connectDevice(). Subclasses must provide diff --git a/src/serialbus/qmodbusdevice.h b/src/serialbus/qmodbusdevice.h index 468fdf4..0e6dad7 100644 --- a/src/serialbus/qmodbusdevice.h +++ b/src/serialbus/qmodbusdevice.h @@ -82,7 +82,7 @@ public: NetworkAddressParameter, // Reserved - UserParameter = 0x100 + UserParameter = 0x100 // ### Qt6: remove }; Q_ENUM(ConnectionParameter) @@ -100,6 +100,8 @@ public: Error error() const; QString errorString() const; + QIODevice *device() const; + Q_SIGNALS: void errorOccurred(QModbusDevice::Error error); void stateChanged(QModbusDevice::State state); diff --git a/src/serialbus/qmodbusdevice_p.h b/src/serialbus/qmodbusdevice_p.h index a845a89..1526397 100644 --- a/src/serialbus/qmodbusdevice_p.h +++ b/src/serialbus/qmodbusdevice_p.h @@ -78,7 +78,9 @@ public: int m_networkPort = 502; QString m_networkAddress = QStringLiteral("127.0.0.1"); - QHash<int, QVariant> m_userConnectionParams; + QHash<int, QVariant> m_userConnectionParams; // ### Qt6: remove + + virtual QIODevice *device() const { return nullptr; } }; QT_END_NAMESPACE diff --git a/src/serialbus/qmodbuspdu.cpp b/src/serialbus/qmodbuspdu.cpp index c42352d..9ca9027 100644 --- a/src/serialbus/qmodbuspdu.cpp +++ b/src/serialbus/qmodbuspdu.cpp @@ -106,9 +106,9 @@ static int minimumDataSize(const QModbusPdu &pdu, Type type) static QDataStream &pduFromStream(QDataStream &stream, QModbusPdu &pdu, Type type) { quint8 codeByte = 0; - if (stream.readRawData((char *) (&codeByte), sizeof(quint8)) != sizeof(quint8)) + if (stream.readRawData(reinterpret_cast<char *>(&codeByte), sizeof(quint8)) != sizeof(quint8)) return stream; - QModbusPdu::FunctionCode code = (QModbusPdu::FunctionCode) codeByte; + QModbusPdu::FunctionCode code = QModbusPdu::FunctionCode(codeByte); pdu.setFunctionCode(code); auto needsAdditionalRead = [](QModbusPdu &pdu, int size) -> bool { @@ -157,13 +157,13 @@ static QDataStream &pduFromStream(QDataStream &stream, QModbusPdu &pdu, Type typ return stream; // early return to avoid second read } } else { - data.resize(stream.device()->size() - 1); // One byte for the function code. + data.resize(int(stream.device()->size() - 1)); // One byte for the function code. } } else if (pdu.functionCode() == QModbusPdu::Diagnostics) { quint16 subCode; pdu.decodeData(&subCode); if (subCode == Diagnostics::ReturnQueryData) - data.resize(stream.device()->size() - 1); // One byte for the function code. + data.resize(int(stream.device()->size() - 1)); // One byte for the function code. } // reset what we have so far, next read might fail as well @@ -439,7 +439,7 @@ static QDataStream &pduFromStream(QDataStream &stream, QModbusPdu &pdu, Type typ QDebug operator<<(QDebug debug, const QModbusPdu &pdu) { QDebugStateSaver _(debug); - debug.nospace().noquote() << "0x" << hex << qSetFieldWidth(2) << qSetPadChar('0') + debug.nospace().noquote() << "0x" << Qt::hex << qSetFieldWidth(2) << qSetPadChar('0') << (pdu.isException() ? pdu.functionCode() | QModbusPdu::ExceptionByte : pdu.functionCode()) << qSetFieldWidth(0) << pdu.data().toHex(); return debug; @@ -547,7 +547,7 @@ int QModbusRequest::minimumDataSize(const QModbusRequest &request) int QModbusRequest::calculateDataSize(const QModbusRequest &request) { if (requestSizeCalculators.exists()) { - if (auto ptr = requestSizeCalculators()->value(request.functionCode(), nullptr)) + if (auto ptr = requestSizeCalculators()->value(quint8(request.functionCode()), nullptr)) return ptr(request); } @@ -604,7 +604,7 @@ int QModbusRequest::calculateDataSize(const QModbusRequest &request) */ void QModbusRequest::registerDataSizeCalculator(FunctionCode fc, CalcFuncPtr calculator) { - requestSizeCalculators()->insert(fc, calculator); + requestSizeCalculators()->insert(quint8(fc), calculator); } /*! @@ -703,7 +703,7 @@ int QModbusResponse::minimumDataSize(const QModbusResponse &response) int QModbusResponse::calculateDataSize(const QModbusResponse &response) { if (responseSizeCalculators.exists()) { - if (auto ptr = responseSizeCalculators()->value(response.functionCode(), nullptr)) + if (auto ptr = responseSizeCalculators()->value(quint8(response.functionCode()), nullptr)) return ptr(response); } @@ -765,7 +765,7 @@ int QModbusResponse::calculateDataSize(const QModbusResponse &response) for (int i = 1; i < numOfObjects; ++i) { if (data.size() <= nextSizeField) break; - objectSize = data[nextSizeField]; + objectSize = quint8(data[nextSizeField]); size += objectSize; nextSizeField += objectSize + 2; // object size + object id field + object size field } @@ -790,7 +790,7 @@ int QModbusResponse::calculateDataSize(const QModbusResponse &response) */ void QModbusResponse::registerDataSizeCalculator(FunctionCode fc, CalcFuncPtr calculator) { - responseSizeCalculators()->insert(fc, calculator); + responseSizeCalculators()->insert(quint8(fc), calculator); } /*! diff --git a/src/serialbus/qmodbuspdu.h b/src/serialbus/qmodbuspdu.h index 14cef59..30904a7 100644 --- a/src/serialbus/qmodbuspdu.h +++ b/src/serialbus/qmodbuspdu.h @@ -101,7 +101,7 @@ public: bool isException() const { return m_code & ExceptionByte; } qint16 size() const { return dataSize() + 1; } - qint16 dataSize() const { return m_data.size(); } + qint16 dataSize() const { return qint16(m_data.size()); } FunctionCode functionCode() const { return FunctionCode(quint8(m_code) &~ ExceptionByte); diff --git a/src/serialbus/qmodbusrtuserialmaster_p.h b/src/serialbus/qmodbusrtuserialmaster_p.h index 9672684..d206eec 100644 --- a/src/serialbus/qmodbusrtuserialmaster_p.h +++ b/src/serialbus/qmodbusrtuserialmaster_p.h @@ -310,7 +310,7 @@ public: onBytesWritten(bytes); }); - QObject::connect(m_serialPort, QOverload<QSerialPort::SerialPortError>::of(&QSerialPort::error), + QObject::connect(m_serialPort, &QSerialPort::errorOccurred, q, [this](QSerialPort::SerialPortError error) { onError(error); }); @@ -427,6 +427,8 @@ public: return false; } + QIODevice *device() const override { return m_serialPort; } + Timer m_responseTimer; QByteArray m_responseBuffer; diff --git a/src/serialbus/qmodbusrtuserialslave_p.h b/src/serialbus/qmodbusrtuserialslave_p.h index da59e5e..d6ff8d6 100644 --- a/src/serialbus/qmodbusrtuserialslave_p.h +++ b/src/serialbus/qmodbusrtuserialslave_p.h @@ -280,8 +280,7 @@ public: storeModbusCommEvent(event); // store the final event after processing }); - using TypeId = void (QSerialPort::*)(QSerialPort::SerialPortError); - QObject::connect(m_serialPort, static_cast<TypeId>(&QSerialPort::error), q, + QObject::connect(m_serialPort, &QSerialPort::errorOccurred, q, [this](QSerialPort::SerialPortError error) { if (error == QSerialPort::NoError) return; @@ -355,6 +354,8 @@ public: m_requestBuffer.clear(); } + QIODevice *device() const override { return m_serialPort; } + QByteArray m_requestBuffer; bool m_processesBroadcast = false; QSerialPort *m_serialPort = nullptr; diff --git a/src/serialbus/qmodbustcpclient_p.h b/src/serialbus/qmodbustcpclient_p.h index db1d4e4..649b48e 100644 --- a/src/serialbus/qmodbustcpclient_p.h +++ b/src/serialbus/qmodbustcpclient_p.h @@ -86,8 +86,8 @@ public: cleanupTransactionStore(); }); - using TypeId = void (QAbstractSocket::*)(QAbstractSocket::SocketError); - QObject::connect(m_socket, static_cast<TypeId>(&QAbstractSocket::error), q, + QObject::connect(m_socket, + QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), q, [this](QAbstractSocket::SocketError /*error*/) { Q_Q(QModbusTcpClient); @@ -121,7 +121,7 @@ public: if (knownTransaction && m_transactionStore[transactionId].timer) m_transactionStore[transactionId].timer->stop(); - qCDebug(QT_MODBUS) << "(TCP client) tid:" << hex << transactionId << "size:" + qCDebug(QT_MODBUS) << "(TCP client) tid:" << Qt::hex << transactionId << "size:" << bytesPdu << "server address:" << serverAddress; // The length field is the byte count of the following fields, including the Unit @@ -169,7 +169,7 @@ public: return false; } qCDebug(QT_MODBUS_LOW) << "(TCP client) Sent TCP ADU:" << buffer.toHex(); - qCDebug(QT_MODBUS) << "(TCP client) Sent TCP PDU:" << request << "with tId:" << hex + qCDebug(QT_MODBUS) << "(TCP client) Sent TCP PDU:" << request << "with tId:" <<Qt:: hex << tId; return true; }; @@ -193,9 +193,8 @@ public: }); if (element.timer) { - using TypeId = void (QTimer::*)(int); q->connect(q, &QModbusClient::timeoutChanged, - element.timer.data(), static_cast<TypeId>(&QTimer::setInterval)); + element.timer.data(), QOverload<int>::of(&QTimer::setInterval)); QObject::connect(element.timer.data(), &QTimer::timeout, q, [this, writeToSocket, tId]() { if (!m_transactionStore.contains(tId)) return; @@ -210,9 +209,9 @@ public: return; m_transactionStore.insert(tId, elem); elem.timer->start(); - qCDebug(QT_MODBUS) << "(TCP client) Resend request with tId:" << hex << tId; + qCDebug(QT_MODBUS) << "(TCP client) Resend request with tId:" << Qt::hex << tId; } else { - qCDebug(QT_MODBUS) << "(TCP client) Timeout of request with tId:" << hex << tId; + qCDebug(QT_MODBUS) << "(TCP client) Timeout of request with tId:" <<Qt::hex << tId; elem.reply->setError(QModbusDevice::TimeoutError, QModbusClient::tr("Request timeout.")); } @@ -220,7 +219,7 @@ public: element.timer->start(); } else { qCWarning(QT_MODBUS) << "(TCP client) No response timeout timer for request with tId:" - << hex << tId << ". Expected timeout:" << m_responseTimeoutDuration; + << Qt::hex << tId << ". Expected timeout:" << m_responseTimeoutDuration; } incrementTransactionId(); @@ -255,6 +254,8 @@ public: inline void incrementTransactionId() { m_transactionId++; } inline int transactionId() const { return m_transactionId; } + QIODevice *device() const override { return m_socket; } + QTcpSocket *m_socket = nullptr; QByteArray responseBuffer; QHash<quint16, QueueElement> m_transactionStore; diff --git a/src/serialbus/qmodbustcpserver_p.h b/src/serialbus/qmodbustcpserver_p.h index 56c45e9..3e73de3 100644 --- a/src/serialbus/qmodbustcpserver_p.h +++ b/src/serialbus/qmodbustcpserver_p.h @@ -167,7 +167,7 @@ public: input >> transactionId >> protocolId >> bytesPdu >> unitId; qCDebug(QT_MODBUS_LOW) << "(TCP server) Request MBPA:" << "Transaction Id:" - << hex << transactionId << "Protocol Id:" << protocolId << "PDU bytes:" + << Qt::hex << transactionId << "Protocol Id:" << protocolId << "PDU bytes:" << bytesPdu << "Unit Id:" << unitId; // The length field is the byte count of the following fields, including the Unit @@ -224,7 +224,7 @@ public: }); } - QTcpServer *m_tcpServer; + QTcpServer *m_tcpServer { nullptr }; QVector<QTcpSocket *> connections; std::unique_ptr<QModbusTcpConnectionObserver> m_observer; diff --git a/src/tools/canbusutil/canbusutil.cpp b/src/tools/canbusutil/canbusutil.cpp index a4e1a58..b07d97c 100644 --- a/src/tools/canbusutil/canbusutil.cpp +++ b/src/tools/canbusutil/canbusutil.cpp @@ -53,9 +53,9 @@ void CanBusUtil::setShowTimeStamp(bool showTimeStamp) m_readTask->setShowTimeStamp(showTimeStamp); } -void CanBusUtil::setShowFdFlags(bool showFdFlags) +void CanBusUtil::setShowFlags(bool showFlags) { - m_readTask->setShowFdFlags(showFdFlags); + m_readTask->setShowFlags(showFlags); } void CanBusUtil::setConfigurationParameter(QCanBusDevice::ConfigurationKey key, @@ -67,7 +67,7 @@ void CanBusUtil::setConfigurationParameter(QCanBusDevice::ConfigurationKey key, bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, const QString &data) { if (!m_canBus) { - m_output << tr("Error: Cannot create QCanBus.") << endl; + m_output << tr("Error: Cannot create QCanBus.") << Qt::endl; return false; } @@ -80,7 +80,7 @@ bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, con return false; if (m_listening) { - if (m_readTask->isShowFdFlags()) + if (m_readTask->isShowFlags()) m_canDevice->setConfigurationParameter(QCanBusDevice::CanFdKey, true); connect(m_canDevice.data(), &QCanBusDevice::framesReceived, m_readTask, &ReadTask::handleFrames); @@ -96,32 +96,32 @@ bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, con int CanBusUtil::printPlugins() { if (!m_canBus) { - m_output << tr("Error: Cannot create QCanBus.") << endl; + m_output << tr("Error: Cannot create QCanBus.") << Qt::endl; return 1; } const QStringList plugins = m_canBus->plugins(); for (const QString &plugin : plugins) - m_output << plugin << endl; + m_output << plugin << Qt::endl; return 0; } int CanBusUtil::printDevices(const QString &pluginName) { if (!m_canBus) { - m_output << tr("Error: Cannot create QCanBus.") << endl; + m_output << tr("Error: Cannot create QCanBus.") << Qt::endl; return 1; } QString errorMessage; const QList<QCanBusDeviceInfo> devices = m_canBus->availableDevices(pluginName, &errorMessage); if (!errorMessage.isEmpty()) { - m_output << tr("Error gathering available devices: '%1'").arg(errorMessage) << endl; + m_output << tr("Error gathering available devices: '%1'").arg(errorMessage) << Qt::endl; return 1; } for (const QCanBusDeviceInfo &info : devices) - m_output << info.name() << endl; + m_output << info.name() << Qt::endl; return 0; } @@ -129,7 +129,7 @@ bool CanBusUtil::parseDataField(quint32 &id, QString &payload) { int hashMarkPos = m_data.indexOf('#'); if (hashMarkPos < 0) { - m_output << tr("Data field invalid: No hash mark found!") << endl; + m_output << tr("Data field invalid: No hash mark found!") << Qt::endl; return false; } @@ -154,7 +154,7 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) return true; } - m_output << tr("Error: RTR frame length must be between 0 and 8 (including).") << endl; + m_output << tr("Error: RTR frame length must be between 0 and 8 (including).") << Qt::endl; return false; } @@ -165,7 +165,7 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) const QRegularExpression re(QStringLiteral("^[0-9A-Fa-f]*$")); if (!re.match(payload).hasMatch()) { - m_output << tr("Data field invalid: Only hex numbers allowed.") << endl; + m_output << tr("Data field invalid: Only hex numbers allowed.") << Qt::endl; return false; } @@ -177,7 +177,7 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) frame->setErrorStateIndicator(flags & ErrorStateIndicatorFlag); payload.remove(0, 1); } else { - m_output << tr("Data field invalid: Size is not multiple of two.") << endl; + m_output << tr("Data field invalid: Size is not multiple of two.") << Qt::endl; return false; } } @@ -186,7 +186,7 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) const int maxSize = frame->hasFlexibleDataRateFormat() ? 64 : 8; if (bytes.size() > maxSize) { - m_output << tr("Data field invalid: Size is longer than %1 bytes.").arg(maxSize) << endl; + m_output << tr("Data field invalid: Size is longer than %1 bytes.").arg(maxSize) << Qt::endl; return false; } @@ -198,13 +198,13 @@ bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame) bool CanBusUtil::connectCanDevice() { if (!m_canBus->plugins().contains(m_pluginName)) { - m_output << tr("Cannot find CAN bus plugin '%1'.").arg(m_pluginName) << endl; + m_output << tr("Cannot find CAN bus plugin '%1'.").arg(m_pluginName) << Qt::endl; return false; } m_canDevice.reset(m_canBus->createDevice(m_pluginName, m_deviceName)); if (!m_canDevice) { - m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << endl; + m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << Qt::endl; return false; } @@ -214,7 +214,7 @@ bool CanBusUtil::connectCanDevice() connect(m_canDevice.data(), &QCanBusDevice::errorOccurred, m_readTask, &ReadTask::handleError); if (!m_canDevice->connectDevice()) { - m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << endl; + m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << Qt::endl; return false; } @@ -234,7 +234,7 @@ bool CanBusUtil::sendData() return false; if (id > 0x1FFFFFFF) { // 29 bits - m_output << tr("Cannot send invalid frame ID: '%1'").arg(id, 0, 16) << endl; + m_output << tr("Cannot send invalid frame ID: '%1'").arg(id, 0, 16) << Qt::endl; return false; } diff --git a/src/tools/canbusutil/canbusutil.h b/src/tools/canbusutil/canbusutil.h index 19b7868..4d1b7f4 100644 --- a/src/tools/canbusutil/canbusutil.h +++ b/src/tools/canbusutil/canbusutil.h @@ -57,7 +57,7 @@ public: explicit CanBusUtil(QTextStream &output, QCoreApplication &app, QObject *parent = nullptr); void setShowTimeStamp(bool showTimeStamp); - void setShowFdFlags(bool showFdFlags); + void setShowFlags(bool showFlags); void setConfigurationParameter(QCanBusDevice::ConfigurationKey key, const QVariant &value); bool start(const QString &pluginName, const QString &deviceName, const QString &data = QString()); int printPlugins(); diff --git a/src/tools/canbusutil/main.cpp b/src/tools/canbusutil/main.cpp index 3a71192..1277325 100644 --- a/src/tools/canbusutil/main.cpp +++ b/src/tools/canbusutil/main.cpp @@ -94,9 +94,10 @@ int main(int argc, char *argv[]) CanBusUtil::tr("Show timestamp for each received CAN bus frame.")); parser.addOption(showTimeStampOption); - const QCommandLineOption showFdFlagsOption({"i", "info"}, - CanBusUtil::tr("Show extra info (CAN FD flags) for each received CAN bus frame.")); - parser.addOption(showFdFlagsOption); + const QCommandLineOption showFlagsOption({"i", "info"}, + CanBusUtil::tr("Show flags bitrate switch, error indicator, and local echo" + " for each received CAN bus frame.")); + parser.addOption(showFlagsOption); const QCommandLineOption listDevicesOption({"d", "devices"}, CanBusUtil::tr("Show available CAN bus devices for the given plugin.")); @@ -149,14 +150,14 @@ int main(int argc, char *argv[]) if (parser.isSet(listeningOption)) { util.setShowTimeStamp(parser.isSet(showTimeStampOption)); - util.setShowFdFlags(parser.isSet(showFdFlagsOption)); + util.setShowFlags(parser.isSet(showFlagsOption)); } else if (args.size() == 3) { data = args.at(2); } else if (args.size() == 1 && parser.isSet(listDevicesOption)) { return util.printDevices(args.at(0)); } else if (args.size() != 2) { output << CanBusUtil::tr("Invalid number of arguments (%1 given).").arg(args.size()); - output << endl << endl << parser.helpText(); + output << Qt::endl << Qt::endl << parser.helpText(); return 1; } diff --git a/src/tools/canbusutil/readtask.cpp b/src/tools/canbusutil/readtask.cpp index 2bde509..86dfb73 100644 --- a/src/tools/canbusutil/readtask.cpp +++ b/src/tools/canbusutil/readtask.cpp @@ -45,14 +45,14 @@ void ReadTask::setShowTimeStamp(bool showTimeStamp) m_showTimeStamp = showTimeStamp; } -bool ReadTask::isShowFdFlags() const +bool ReadTask::isShowFlags() const { - return m_showFdFlags; + return m_showFlags; } -void ReadTask::setShowFdFlags(bool showFlags) +void ReadTask::setShowFlags(bool showFlags) { - m_showFdFlags = showFlags; + m_showFlags = showFlags; } void ReadTask::handleFrames() { @@ -73,12 +73,16 @@ void ReadTask::handleFrames() { .arg(frame.timeStamp().microSeconds() / 100, 4, 10, QLatin1Char('0')); } - if (m_showFdFlags) { - QString flags = QLatin1String("- - "); + if (m_showFlags) { + QString flags = QLatin1String("- - - "); + if (frame.hasBitrateSwitch()) flags[0] = QLatin1Char('B'); if (frame.hasErrorStateIndicator()) flags[2] = QLatin1Char('E'); + if (frame.hasLocalEcho()) + flags[4] = QLatin1Char('L'); + view += flags; } @@ -87,7 +91,7 @@ void ReadTask::handleFrames() { else view += frame.toString(); - m_output << view << endl; + m_output << view << Qt::endl; } } @@ -99,5 +103,5 @@ void ReadTask::handleError(QCanBusDevice::CanBusError /*error*/) return; } - m_output << tr("Read error: '%1'").arg(canDevice->errorString()) << endl; + m_output << tr("Read error: '%1'").arg(canDevice->errorString()) << Qt::endl; } diff --git a/src/tools/canbusutil/readtask.h b/src/tools/canbusutil/readtask.h index a866d3d..345af35 100644 --- a/src/tools/canbusutil/readtask.h +++ b/src/tools/canbusutil/readtask.h @@ -47,8 +47,8 @@ class ReadTask : public QObject public: explicit ReadTask(QTextStream &m_output, QObject *parent = nullptr); void setShowTimeStamp(bool showStamp); - bool isShowFdFlags() const; - void setShowFdFlags(bool isShowFdFlags); + bool isShowFlags() const; + void setShowFlags(bool isShowFlags); public slots: void handleFrames(); @@ -57,7 +57,7 @@ public slots: private: QTextStream &m_output; bool m_showTimeStamp = false; - bool m_showFdFlags = false; + bool m_showFlags = false; }; #endif // READTASK_H diff --git a/src/tools/canbusutil/sigtermhandler.cpp b/src/tools/canbusutil/sigtermhandler.cpp index 8611916..a882a55 100644 --- a/src/tools/canbusutil/sigtermhandler.cpp +++ b/src/tools/canbusutil/sigtermhandler.cpp @@ -47,7 +47,7 @@ SigTermHandler *SigTermHandler::instance() void SigTermHandler::handle(int s) { QTextStream out(stdout); - out << " Caught signal " << s << endl; + out << " Caught signal " << s << Qt::endl; emit instance()->sigTermSignal(); } diff --git a/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp b/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp index 85d445f..61d30c0 100644 --- a/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp +++ b/tests/auto/qcanbusdevice/tst_qcanbusdevice.cpp @@ -44,6 +44,8 @@ #include <memory> +Q_DECLARE_METATYPE(QCanBusDevice::Filter) + class tst_Backend : public QCanBusDevice { Q_OBJECT @@ -84,8 +86,11 @@ public: bool writeFrame(const QCanBusFrame &data) { - if (state() != QCanBusDevice::ConnectedState) + if (state() != QCanBusDevice::ConnectedState) { + setError(QStringLiteral("Cannot write frame as device is not connected"), + QCanBusDevice::OperationError); return false; + } if (writeBufferUsed) { enqueueOutgoingFrame(data); @@ -149,6 +154,8 @@ private slots: void error(); void cleanupTestCase(); void tst_filtering(); + void filterEqual_data(); + void filterEqual(); void tst_bufferingAttribute(); void tst_waitForFramesReceived(); @@ -161,6 +168,7 @@ tst_QCanBusDevice::tst_QCanBusDevice() { qRegisterMetaType<QCanBusDevice::CanBusDeviceState>(); qRegisterMetaType<QCanBusDevice::CanBusError>(); + qRegisterMetaType<QCanBusDevice::Filter>(); } void tst_QCanBusDevice::initTestCase() @@ -171,7 +179,9 @@ void tst_QCanBusDevice::initTestCase() QSignalSpy stateSpy(device.data(), &QCanBusDevice::stateChanged); QVERIFY(!device->connectDevice()); // first connect triggered to fail + QCOMPARE(device->error(), QCanBusDevice::NoError); QVERIFY(device->connectDevice()); + QCOMPARE(device->error(), QCanBusDevice::NoError); QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); QCOMPARE(stateSpy.count(), 4); QCOMPARE(stateSpy.at(0).at(0).value<QCanBusDevice::CanBusDeviceState>(), @@ -233,7 +243,8 @@ void tst_QCanBusDevice::write() stateSpy.clear(); QVERIFY(stateSpy.isEmpty()); - device->writeFrame(frame); + QVERIFY(!device->writeFrame(frame)); + QCOMPARE(device->error(), QCanBusDevice::OperationError); QCOMPARE(spy.count(), 0); device->connectDevice(); @@ -244,7 +255,8 @@ void tst_QCanBusDevice::write() QCOMPARE(stateSpy.at(1).at(0).value<QCanBusDevice::CanBusDeviceState>(), QCanBusDevice::ConnectedState); - device->writeFrame(frame); + QVERIFY(device->writeFrame(frame)); + QCOMPARE(device->error(), QCanBusDevice::NoError); QCOMPARE(spy.count(), 1); } @@ -257,6 +269,7 @@ void tst_QCanBusDevice::read() stateSpy.clear(); const QCanBusFrame frame1 = device->readFrame(); + QCOMPARE(device->error(), QCanBusDevice::OperationError); QVERIFY(device->connectDevice()); QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); @@ -268,6 +281,7 @@ void tst_QCanBusDevice::read() device->triggerNewFrame(); const QCanBusFrame frame2 = device->readFrame(); + QCOMPARE(device->error(), QCanBusDevice::NoError); QVERIFY(!frame1.frameId()); QVERIFY(!frame1.isValid()); QVERIFY(frame2.frameId()); @@ -278,6 +292,12 @@ void tst_QCanBusDevice::readAll() { enum { FrameNumber = 10 }; device->disconnectDevice(); + QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000); + + const QVector<QCanBusFrame> empty = device->readAllFrames(); + QCOMPARE(device->error(), QCanBusDevice::OperationError); + QVERIFY(empty.isEmpty()); + QVERIFY(device->connectDevice()); QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); @@ -285,21 +305,30 @@ void tst_QCanBusDevice::readAll() device->triggerNewFrame(); const QVector<QCanBusFrame> frames = device->readAllFrames(); + QCOMPARE(device->error(), QCanBusDevice::NoError); 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); - } + device->disconnectDevice(); + QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000); + + device->clear(QCanBusDevice::Input); + QCOMPARE(device->error(), QCanBusDevice::OperationError); + + QVERIFY(device->connectDevice()); + QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); + + device->clear(QCanBusDevice::Input); + QCOMPARE(device->error(), QCanBusDevice::NoError); for (int i = 0; i < 10; ++i) device->triggerNewFrame(); device->clear(QCanBusDevice::Input); + QCOMPARE(device->error(), QCanBusDevice::NoError); QVERIFY(!device->framesAvailable()); } @@ -308,11 +337,17 @@ void tst_QCanBusDevice::clearOutputBuffer() { // this test requires buffered writing device->setWriteBuffered(true); + device->disconnectDevice(); + QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::UnconnectedState, 5000); - if (device->state() != QCanBusDevice::ConnectedState) { - QVERIFY(device->connectDevice()); - QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); - } + device->clear(QCanBusDevice::Output); + QCOMPARE(device->error(), QCanBusDevice::OperationError); + + QVERIFY(device->connectDevice()); + QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); + + device->clear(QCanBusDevice::Output); + QCOMPARE(device->error(), QCanBusDevice::NoError); // first test buffered writing, frames will be written after some delay QSignalSpy spy(device.data(), &QCanBusDevice::framesWritten); @@ -326,6 +361,7 @@ void tst_QCanBusDevice::clearOutputBuffer() device->writeFrame(QCanBusFrame(0x123, "output")); device->clear(QCanBusDevice::Output); + QCOMPARE(device->error(), QCanBusDevice::NoError); QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 0, 5000); } @@ -421,6 +457,89 @@ void tst_QCanBusDevice::tst_filtering() QVERIFY(!(newFilter.at(1).format & QCanBusDevice::Filter::MatchExtendedFormat)); } +void tst_QCanBusDevice::filterEqual_data() +{ + using Filter = QCanBusDevice::Filter; + using Frame = QCanBusFrame; + + QTest::addColumn<QCanBusDevice::Filter>("first"); + QTest::addColumn<QCanBusDevice::Filter>("second"); + QTest::addColumn<bool>("isEqual"); + + auto filter = [](quint32 frameId, quint32 frameIdMask, + Frame::FrameType type, + Filter::FormatFilter format) { + Filter result; + result.frameId = frameId; + result.frameIdMask = frameIdMask; + result.type = type; + result.format = format; + return result; + }; + + QTest::newRow("empty-equal") + << Filter() + << Filter() + << true; + QTest::newRow("empty-default-equal") + << Filter() + << filter(0, 0, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) + << true; + QTest::newRow("empty-non-default-different") + << Filter() + << filter(1, 2, Frame::ErrorFrame, Filter::MatchBaseFormat) + << false; + + QTest::newRow("frame-id-equal") + << filter(0x345, 0x800, Frame::RemoteRequestFrame, Filter::MatchBaseFormat) + << filter(0x345, 0x800, Frame::RemoteRequestFrame, Filter::MatchBaseFormat) + << true; + QTest::newRow("frame-id-different") + << filter(0x345, 0x000, Frame::RemoteRequestFrame, Filter::MatchBaseFormat) + << filter(0x346, 0x000, Frame::RemoteRequestFrame, Filter::MatchBaseFormat) + << false; + + QTest::newRow("frame-mask-equal") + << filter(0x123, 0x7FF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) + << filter(0x123, 0x7FF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) + << true; + QTest::newRow("frame-mask-different") + << filter(0x123, 0x7FF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) + << filter(0x123, 0x7FE, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) + << false; + + QTest::newRow("frame-type-equal") + << filter(0xFFF, 0xBFF, Frame::DataFrame, Filter::MatchBaseAndExtendedFormat) + << filter(0xFFF, 0xBFF, Frame::DataFrame, Filter::MatchBaseAndExtendedFormat) + << true; + QTest::newRow("frame-type-different") + << filter(0xFFF, 0xBFF, Frame::DataFrame, Filter::MatchBaseAndExtendedFormat) + << filter(0xFFF, 0xBFF, Frame::InvalidFrame, Filter::MatchBaseAndExtendedFormat) + << false; + + QTest::newRow("filter-equal") + << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchExtendedFormat) + << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchExtendedFormat) + << true; + QTest::newRow("filter-different") + << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchExtendedFormat) + << filter(0xFFF, 0xBFF, Frame::ErrorFrame, Filter::MatchBaseAndExtendedFormat) + << false; +} + +void tst_QCanBusDevice::filterEqual() +{ + QFETCH(QCanBusDevice::Filter, first); + QFETCH(QCanBusDevice::Filter, second); + QFETCH(bool, isEqual); + + if (isEqual) { + QCOMPARE(first, second); + } else { + QVERIFY(first != second); + } +} + void tst_QCanBusDevice::tst_bufferingAttribute() { std::unique_ptr<tst_Backend> canDevice(new tst_Backend); @@ -436,6 +555,10 @@ void tst_QCanBusDevice::tst_bufferingAttribute() void tst_QCanBusDevice::tst_waitForFramesReceived() { + device->disconnectDevice(); + QVERIFY(!device->waitForFramesReceived(100)); + QCOMPARE(device->error(), QCanBusDevice::OperationError); + if (device->state() != QCanBusDevice::ConnectedState) { QVERIFY(device->connectDevice()); QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); @@ -445,9 +568,10 @@ void tst_QCanBusDevice::tst_waitForFramesReceived() QVERIFY(device->triggerNewFrame()); QVERIFY(device->framesAvailable()); - // frame is available, function blocks and times out - bool result = device->waitForFramesReceived(2000); - QVERIFY(!result); + // frame is already available, but no new frame comes in + // while function blocks, therefore times out + QVERIFY(!device->waitForFramesReceived(2000)); + QCOMPARE(device->error(), QCanBusDevice::TimeoutError); QCanBusFrame frame = device->readFrame(); QVERIFY(frame.isValid()); @@ -457,16 +581,16 @@ void tst_QCanBusDevice::tst_waitForFramesReceived() QElapsedTimer elapsed; elapsed.start(); // no pending frame (should trigger active wait & timeout) - result = device->waitForFramesReceived(5000); + QVERIFY(!device->waitForFramesReceived(5000)); QVERIFY(elapsed.hasExpired(4000)); // should have caused time elapse - QVERIFY(!result); + QCOMPARE(device->error(), QCanBusDevice::TimeoutError); QTimer::singleShot(2000, [&]() { device->triggerNewFrame(); }); elapsed.restart(); // frame will be inserted after 2s - result = device->waitForFramesReceived(8000); + QVERIFY(device->waitForFramesReceived(8000)); + QCOMPARE(device->error(), QCanBusDevice::NoError); QVERIFY(!elapsed.hasExpired(8000)); - QVERIFY(result); frame = device->readFrame(); QVERIFY(frame.isValid()); @@ -478,9 +602,8 @@ void tst_QCanBusDevice::tst_waitForFramesReceived() }); elapsed.restart(); // error will be inserted after 2s - result = device->waitForFramesReceived(8000); + QVERIFY(!device->waitForFramesReceived(8000)); QVERIFY(!elapsed.hasExpired(8000)); - QVERIFY(!result); QCOMPARE(device->errorString(), QStringLiteral("TriggerError")); QCOMPARE(device->error(), QCanBusDevice::ReadError); @@ -494,56 +617,71 @@ void tst_QCanBusDevice::tst_waitForFramesReceived() QObject::connect(device.data(), &QCanBusDevice::framesReceived, [this, &handleCounter]() { handleCounter++; // this should trigger a recursion which we want to catch - device->waitForFramesReceived(5000); + QVERIFY(!device->waitForFramesReceived(5000)); + // Only the first two frames create a recursion, as the outer + // waitForFramesReceived() will immediately exit once at least + // one frame was received. Therefore the third frame here leads + // to TimeoutError, as no further frame is received. + if (handleCounter < 3) { + QCOMPARE(device->error(), QCanBusDevice::OperationError); + } else { + QCOMPARE(device->error(), QCanBusDevice::TimeoutError); + } }); - result = device->waitForFramesReceived(8000); - QVERIFY(result); + QVERIFY(device->waitForFramesReceived(8000)); + QCOMPARE(device->error(), QCanBusDevice::NoError); QTRY_COMPARE_WITH_TIMEOUT(handleCounter, 3, 5000); } void tst_QCanBusDevice::tst_waitForFramesWritten() { + device->disconnectDevice(); + QVERIFY(!device->waitForFramesWritten(100)); + QCOMPARE(device->error(), QCanBusDevice::OperationError); + if (device->state() != QCanBusDevice::ConnectedState) { + QVERIFY(!device->waitForFramesWritten(100)); + QCOMPARE(device->error(), QCanBusDevice::OperationError); + QVERIFY(device->connectDevice()); QTRY_VERIFY_WITH_TIMEOUT(device->state() == QCanBusDevice::ConnectedState, 5000); } device->setWriteBuffered(false); - bool result = device->waitForFramesWritten(1000); - QVERIFY(!result); // no buffer, waiting not possible + QVERIFY(!device->waitForFramesWritten(1000)); // no buffer, waiting not possible + QCOMPARE(device->error(), QCanBusDevice::NoError); device->setWriteBuffered(true); QVERIFY(device->framesToWrite() == 0); - result = device->waitForFramesWritten(1000); - QVERIFY(!result); // nothing in buffer, nothing to wait for + QVERIFY(!device->waitForFramesWritten(1000)); // nothing in buffer, nothing to wait for + QCOMPARE(device->error(), QCanBusDevice::NoError); QCanBusFrame frame; frame.setPayload(QByteArray("testData")); // test error case QTimer::singleShot(500, [&]() { - device->emulateError(QStringLiteral("TriggerWriteError"), QCanBusDevice::ReadError); + device->emulateError(QStringLiteral("TriggerWriteError"), QCanBusDevice::WriteError); }); device->writeFrame(frame); QElapsedTimer elapsed; elapsed.start(); // error will be triggered - result = device->waitForFramesWritten(8000); + QVERIFY(!device->waitForFramesWritten(8000)); QVERIFY(!elapsed.hasExpired(8000)); - QVERIFY(!result); QCOMPARE(device->errorString(), QStringLiteral("TriggerWriteError")); - QCOMPARE(device->error(), QCanBusDevice::ReadError); + QCOMPARE(device->error(), QCanBusDevice::WriteError); // flush remaining frames out to reset the test QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000); // test timeout device->writeFrame(frame); - result = device->waitForFramesWritten(500); + QVERIFY(!device->waitForFramesWritten(500)); + QCOMPARE(device->error(), QCanBusDevice::TimeoutError); QVERIFY(elapsed.hasExpired(500)); - QVERIFY(!result); // flush remaining frames out to reset the test QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000); @@ -551,9 +689,9 @@ void tst_QCanBusDevice::tst_waitForFramesWritten() device->writeFrame(frame); device->writeFrame(frame); elapsed.restart(); - result = device->waitForFramesWritten(8000); + QVERIFY(device->waitForFramesWritten(8000)); + QCOMPARE(device->error(), QCanBusDevice::NoError); QVERIFY(!elapsed.hasExpired(8000)); - QVERIFY(result); // flush remaining frames out to reset the test QTRY_VERIFY_WITH_TIMEOUT(device->framesToWrite() == 0, 10000); @@ -566,10 +704,11 @@ void tst_QCanBusDevice::tst_waitForFramesWritten() QObject::connect(device.data(), &QCanBusDevice::framesWritten, [this, &handleCounter]() { handleCounter++; // this should trigger a recursion which we want to catch - device->waitForFramesWritten(5000); + QVERIFY(!device->waitForFramesWritten(5000)); + QCOMPARE(device->error(), QCanBusDevice::OperationError); }); - result = device->waitForFramesWritten(8000); - QVERIFY(result); + QVERIFY(device->waitForFramesWritten(8000)); + QCOMPARE(device->error(), QCanBusDevice::NoError); QTRY_COMPARE_WITH_TIMEOUT(handleCounter, 3, 5000); device->setWriteBuffered(false); |