1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the QtSerialBus module.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "mainwindow.h"
52#include "ui_mainwindow.h"
53#include "connectdialog.h"
54
55#include <QCanBus>
56#include <QCanBusFrame>
57#include <QCloseEvent>
58#include <QDesktopServices>
59#include <QTimer>
60
61MainWindow::MainWindow(QWidget *parent) :
62 QMainWindow(parent),
63 m_ui(new Ui::MainWindow),
64 m_busStatusTimer(new QTimer(this))
65{
66 m_ui->setupUi(this);
67
68 m_connectDialog = new ConnectDialog;
69
70 m_status = new QLabel;
71 m_ui->statusBar->addPermanentWidget(widget: m_status);
72
73 m_written = new QLabel;
74 m_ui->statusBar->addWidget(widget: m_written);
75
76 initActionsConnections();
77 QTimer::singleShot(interval: 50, receiver: m_connectDialog, slot: &ConnectDialog::show);
78
79 connect(sender: m_busStatusTimer, signal: &QTimer::timeout, receiver: this, slot: &MainWindow::busStatus);
80}
81
82MainWindow::~MainWindow()
83{
84 delete m_connectDialog;
85 delete m_ui;
86}
87
88void MainWindow::initActionsConnections()
89{
90 m_ui->actionDisconnect->setEnabled(false);
91 m_ui->sendFrameBox->setEnabled(false);
92
93 connect(sender: m_ui->sendFrameBox, signal: &SendFrameBox::sendFrame, receiver: this, slot: &MainWindow::sendFrame);
94 connect(sender: m_ui->actionConnect, signal: &QAction::triggered, slot: [this]() {
95 m_canDevice.release()->deleteLater();
96 m_connectDialog->show();
97 });
98 connect(sender: m_connectDialog, signal: &QDialog::accepted, receiver: this, slot: &MainWindow::connectDevice);
99 connect(sender: m_ui->actionDisconnect, signal: &QAction::triggered, receiver: this, slot: &MainWindow::disconnectDevice);
100 connect(sender: m_ui->actionResetController, signal: &QAction::triggered, context: this, slot: [this]() {
101 m_canDevice->resetController();
102 });
103 connect(sender: m_ui->actionQuit, signal: &QAction::triggered, receiver: this, slot: &QWidget::close);
104 connect(sender: m_ui->actionAboutQt, signal: &QAction::triggered, qApp, slot: &QApplication::aboutQt);
105 connect(sender: m_ui->actionClearLog, signal: &QAction::triggered, receiver: m_ui->receivedMessagesEdit, slot: &QTextEdit::clear);
106 connect(sender: m_ui->actionPluginDocumentation, signal: &QAction::triggered, context: this, slot: []() {
107 QDesktopServices::openUrl(url: QUrl("http://doc.qt.io/qt-5/qtcanbus-backends.html#can-bus-plugins"));
108 });
109}
110
111void MainWindow::processErrors(QCanBusDevice::CanBusError error) const
112{
113 switch (error) {
114 case QCanBusDevice::ReadError:
115 case QCanBusDevice::WriteError:
116 case QCanBusDevice::ConnectionError:
117 case QCanBusDevice::ConfigurationError:
118 case QCanBusDevice::UnknownError:
119 m_status->setText(m_canDevice->errorString());
120 break;
121 default:
122 break;
123 }
124}
125
126void MainWindow::connectDevice()
127{
128 const ConnectDialog::Settings p = m_connectDialog->settings();
129
130 QString errorString;
131 m_canDevice.reset(p: QCanBus::instance()->createDevice(plugin: p.pluginName, interfaceName: p.deviceInterfaceName,
132 errorMessage: &errorString));
133 if (!m_canDevice) {
134 m_status->setText(tr(s: "Error creating device '%1', reason: '%2'")
135 .arg(a: p.pluginName).arg(a: errorString));
136 return;
137 }
138
139 m_numberFramesWritten = 0;
140
141 connect(sender: m_canDevice.get(), signal: &QCanBusDevice::errorOccurred,
142 receiver: this, slot: &MainWindow::processErrors);
143 connect(sender: m_canDevice.get(), signal: &QCanBusDevice::framesReceived,
144 receiver: this, slot: &MainWindow::processReceivedFrames);
145 connect(sender: m_canDevice.get(), signal: &QCanBusDevice::framesWritten,
146 receiver: this, slot: &MainWindow::processFramesWritten);
147
148 if (p.useConfigurationEnabled) {
149 for (const ConnectDialog::ConfigurationItem &item : p.configurations)
150 m_canDevice->setConfigurationParameter(key: item.first, value: item.second);
151 }
152
153 if (!m_canDevice->connectDevice()) {
154 m_status->setText(tr(s: "Connection error: %1").arg(a: m_canDevice->errorString()));
155
156 m_canDevice.reset();
157 } else {
158 m_ui->actionConnect->setEnabled(false);
159 m_ui->actionDisconnect->setEnabled(true);
160
161 m_ui->sendFrameBox->setEnabled(true);
162
163 const QVariant bitRate = m_canDevice->configurationParameter(key: QCanBusDevice::BitRateKey);
164 if (bitRate.isValid()) {
165 const bool isCanFd =
166 m_canDevice->configurationParameter(key: QCanBusDevice::CanFdKey).toBool();
167 const QVariant dataBitRate =
168 m_canDevice->configurationParameter(key: QCanBusDevice::DataBitRateKey);
169 if (isCanFd && dataBitRate.isValid()) {
170 m_status->setText(tr(s: "Plugin: %1, connected to %2 at %3 / %4 kBit/s")
171 .arg(a: p.pluginName).arg(a: p.deviceInterfaceName)
172 .arg(a: bitRate.toInt() / 1000).arg(a: dataBitRate.toInt() / 1000));
173 } else {
174 m_status->setText(tr(s: "Plugin: %1, connected to %2 at %3 kBit/s")
175 .arg(a: p.pluginName).arg(a: p.deviceInterfaceName)
176 .arg(a: bitRate.toInt() / 1000));
177 }
178 } else {
179 m_status->setText(tr(s: "Plugin: %1, connected to %2")
180 .arg(a: p.pluginName).arg(a: p.deviceInterfaceName));
181 }
182
183 if (m_canDevice->hasBusStatus())
184 m_busStatusTimer->start(msec: 2000);
185 else
186 m_ui->busStatus->setText(tr(s: "No CAN bus status available."));
187 }
188}
189
190void MainWindow::busStatus()
191{
192 if (!m_canDevice || !m_canDevice->hasBusStatus()) {
193 m_ui->busStatus->setText(tr(s: "No CAN bus status available."));
194 m_busStatusTimer->stop();
195 return;
196 }
197
198 switch (m_canDevice->busStatus()) {
199 case QCanBusDevice::CanBusStatus::Good:
200 m_ui->busStatus->setText("CAN bus status: Good.");
201 break;
202 case QCanBusDevice::CanBusStatus::Warning:
203 m_ui->busStatus->setText("CAN bus status: Warning.");
204 break;
205 case QCanBusDevice::CanBusStatus::Error:
206 m_ui->busStatus->setText("CAN bus status: Error.");
207 break;
208 case QCanBusDevice::CanBusStatus::BusOff:
209 m_ui->busStatus->setText("CAN bus status: Bus Off.");
210 break;
211 default:
212 m_ui->busStatus->setText("CAN bus status: Unknown.");
213 break;
214 }
215}
216
217void MainWindow::disconnectDevice()
218{
219 if (!m_canDevice)
220 return;
221
222 m_busStatusTimer->stop();
223
224 m_canDevice->disconnectDevice();
225
226 m_ui->actionConnect->setEnabled(true);
227 m_ui->actionDisconnect->setEnabled(false);
228
229 m_ui->sendFrameBox->setEnabled(false);
230
231 m_status->setText(tr(s: "Disconnected"));
232}
233
234void MainWindow::processFramesWritten(qint64 count)
235{
236 m_numberFramesWritten += count;
237 m_written->setText(tr(s: "%1 frames written").arg(a: m_numberFramesWritten));
238}
239
240void MainWindow::closeEvent(QCloseEvent *event)
241{
242 m_connectDialog->close();
243 event->accept();
244}
245
246static QString frameFlags(const QCanBusFrame &frame)
247{
248 QString result = QLatin1String(" --- ");
249
250 if (frame.hasBitrateSwitch())
251 result[1] = QLatin1Char('B');
252 if (frame.hasErrorStateIndicator())
253 result[2] = QLatin1Char('E');
254 if (frame.hasLocalEcho())
255 result[3] = QLatin1Char('L');
256
257 return result;
258}
259
260void MainWindow::processReceivedFrames()
261{
262 if (!m_canDevice)
263 return;
264
265 while (m_canDevice->framesAvailable()) {
266 const QCanBusFrame frame = m_canDevice->readFrame();
267
268 QString view;
269 if (frame.frameType() == QCanBusFrame::ErrorFrame)
270 view = m_canDevice->interpretErrorFrame(errorFrame: frame);
271 else
272 view = frame.toString();
273
274 const QString time = QString::fromLatin1(str: "%1.%2 ")
275 .arg(a: frame.timeStamp().seconds(), fieldwidth: 10, base: 10, fillChar: QLatin1Char(' '))
276 .arg(a: frame.timeStamp().microSeconds() / 100, fieldwidth: 4, base: 10, fillChar: QLatin1Char('0'));
277
278 const QString flags = frameFlags(frame);
279
280 m_ui->receivedMessagesEdit->append(text: time + flags + view);
281 }
282}
283
284void MainWindow::sendFrame(const QCanBusFrame &frame) const
285{
286 if (!m_canDevice)
287 return;
288
289 m_canDevice->writeFrame(frame);
290}
291

source code of qtserialbus/examples/serialbus/can/mainwindow.cpp