1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the tools applications of the QtSerialBus module.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
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 http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "canbusutil.h"
38
39#include <QCoreApplication>
40#include <QTextStream>
41
42CanBusUtil::CanBusUtil(QTextStream &output, QCoreApplication &app, QObject *parent) :
43 QObject(parent),
44 m_canBus(QCanBus::instance()),
45 m_output(output),
46 m_app(app),
47 m_readTask(new ReadTask(output, this))
48{
49}
50
51void CanBusUtil::setShowTimeStamp(bool showTimeStamp)
52{
53 m_readTask->setShowTimeStamp(showTimeStamp);
54}
55
56void CanBusUtil::setShowFlags(bool showFlags)
57{
58 m_readTask->setShowFlags(showFlags);
59}
60
61void CanBusUtil::setConfigurationParameter(QCanBusDevice::ConfigurationKey key,
62 const QVariant &value)
63{
64 m_configurationParameter[key] = value;
65}
66
67bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, const QString &data)
68{
69 if (!m_canBus) {
70 m_output << tr(s: "Error: Cannot create QCanBus.") << Qt::endl;
71 return false;
72 }
73
74 m_pluginName = pluginName;
75 m_deviceName = deviceName;
76 m_data = data;
77 m_listening = data.isEmpty();
78
79 if (!connectCanDevice())
80 return false;
81
82 if (m_listening) {
83 if (m_readTask->isShowFlags())
84 m_canDevice->setConfigurationParameter(key: QCanBusDevice::CanFdKey, value: true);
85 connect(sender: m_canDevice.data(), signal: &QCanBusDevice::framesReceived,
86 receiver: m_readTask, slot: &ReadTask::handleFrames);
87 } else {
88 if (!sendData())
89 return false;
90 QTimer::singleShot(interval: 0, context: &m_app, slot: QCoreApplication::quit);
91 }
92
93 return true;
94}
95
96int CanBusUtil::printPlugins()
97{
98 if (!m_canBus) {
99 m_output << tr(s: "Error: Cannot create QCanBus.") << Qt::endl;
100 return 1;
101 }
102
103 const QStringList plugins = m_canBus->plugins();
104 for (const QString &plugin : plugins)
105 m_output << plugin << Qt::endl;
106 return 0;
107}
108
109int CanBusUtil::printDevices(const QString &pluginName)
110{
111 if (!m_canBus) {
112 m_output << tr(s: "Error: Cannot create QCanBus.") << Qt::endl;
113 return 1;
114 }
115
116 QString errorMessage;
117 const QList<QCanBusDeviceInfo> devices = m_canBus->availableDevices(plugin: pluginName, errorMessage: &errorMessage);
118 if (!errorMessage.isEmpty()) {
119 m_output << tr(s: "Error gathering available devices: '%1'").arg(a: errorMessage) << Qt::endl;
120 return 1;
121 }
122
123 for (const QCanBusDeviceInfo &info : devices)
124 m_output << info.name() << Qt::endl;
125 return 0;
126}
127
128bool CanBusUtil::parseDataField(quint32 &id, QString &payload)
129{
130 int hashMarkPos = m_data.indexOf(c: '#');
131 if (hashMarkPos < 0) {
132 m_output << tr(s: "Data field invalid: No hash mark found!") << Qt::endl;
133 return false;
134 }
135
136 id = m_data.leftRef(n: hashMarkPos).toUInt(ok: nullptr, base: 16);
137 payload = m_data.right(n: m_data.size() - hashMarkPos - 1);
138
139 return true;
140}
141
142bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame)
143{
144 if (!payload.isEmpty() && payload.at(i: 0).toUpper() == 'R') {
145 frame->setFrameType(QCanBusFrame::RemoteRequestFrame);
146
147 if (payload.size() == 1) // payload = "R"
148 return true;
149
150 bool ok = false;
151 int rtrFrameLength = payload.midRef(position: 1).toInt(ok: &ok);
152 if (ok && rtrFrameLength >= 0 && rtrFrameLength <= 8) { // payload = "R8"
153 frame->setPayload(QByteArray(rtrFrameLength, 0));
154 return true;
155 }
156
157 m_output << tr(s: "Error: RTR frame length must be between 0 and 8 (including).") << Qt::endl;
158 return false;
159 }
160
161 if (!payload.isEmpty() && payload.at(i: 0) == '#') {
162 frame->setFlexibleDataRateFormat(true);
163 payload.remove(i: 0, len: 1);
164 }
165
166 const QRegularExpression re(QStringLiteral("^[0-9A-Fa-f]*$"));
167 if (!re.match(subject: payload).hasMatch()) {
168 m_output << tr(s: "Data field invalid: Only hex numbers allowed.") << Qt::endl;
169 return false;
170 }
171
172 if (payload.size() % 2 != 0) {
173 if (frame->hasFlexibleDataRateFormat()) {
174 enum { BitrateSwitchFlag = 1, ErrorStateIndicatorFlag = 2 };
175 const int flags = payload.leftRef(n: 1).toInt(ok: nullptr, base: 16);
176 frame->setBitrateSwitch(flags & BitrateSwitchFlag);
177 frame->setErrorStateIndicator(flags & ErrorStateIndicatorFlag);
178 payload.remove(i: 0, len: 1);
179 } else {
180 m_output << tr(s: "Data field invalid: Size is not multiple of two.") << Qt::endl;
181 return false;
182 }
183 }
184
185 QByteArray bytes = QByteArray::fromHex(hexEncoded: payload.toLatin1());
186
187 const int maxSize = frame->hasFlexibleDataRateFormat() ? 64 : 8;
188 if (bytes.size() > maxSize) {
189 m_output << tr(s: "Data field invalid: Size is longer than %1 bytes.").arg(a: maxSize) << Qt::endl;
190 return false;
191 }
192
193 frame->setPayload(bytes);
194
195 return true;
196}
197
198bool CanBusUtil::connectCanDevice()
199{
200 if (!m_canBus->plugins().contains(str: m_pluginName)) {
201 m_output << tr(s: "Cannot find CAN bus plugin '%1'.").arg(a: m_pluginName) << Qt::endl;
202 return false;
203 }
204
205 m_canDevice.reset(other: m_canBus->createDevice(plugin: m_pluginName, interfaceName: m_deviceName));
206 if (!m_canDevice) {
207 m_output << tr(s: "Cannot create CAN bus device: '%1'").arg(a: m_deviceName) << Qt::endl;
208 return false;
209 }
210
211 const auto constEnd = m_configurationParameter.constEnd();
212 for (auto i = m_configurationParameter.constBegin(); i != constEnd; ++i)
213 m_canDevice->setConfigurationParameter(key: i.key(), value: i.value());
214
215 connect(sender: m_canDevice.data(), signal: &QCanBusDevice::errorOccurred, receiver: m_readTask, slot: &ReadTask::handleError);
216 if (!m_canDevice->connectDevice()) {
217 m_output << tr(s: "Cannot create CAN bus device: '%1'").arg(a: m_deviceName) << Qt::endl;
218 return false;
219 }
220
221 return true;
222}
223
224bool CanBusUtil::sendData()
225{
226 quint32 id;
227 QString payload;
228 QCanBusFrame frame;
229
230 if (!parseDataField(id, payload))
231 return false;
232
233 if (!setFrameFromPayload(payload, frame: &frame))
234 return false;
235
236 if (id > 0x1FFFFFFF) { // 29 bits
237 m_output << tr(s: "Cannot send invalid frame ID: '%1'").arg(a: id, fieldWidth: 0, base: 16) << Qt::endl;
238 return false;
239 }
240
241 frame.setFrameId(id);
242
243 if (frame.hasFlexibleDataRateFormat())
244 m_canDevice->setConfigurationParameter(key: QCanBusDevice::CanFdKey, value: true);
245
246 return m_canDevice->writeFrame(frame);
247}
248

source code of qtserialbus/src/tools/canbusutil/canbusutil.cpp