1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "canbusutil.h"
5
6#include <QCoreApplication>
7#include <QTextStream>
8
9CanBusUtil::CanBusUtil(QTextStream &output, QCoreApplication &app, QObject *parent) :
10 QObject(parent),
11 m_canBus(QCanBus::instance()),
12 m_output(output),
13 m_app(app),
14 m_readTask(new ReadTask(output, this))
15{
16}
17
18void CanBusUtil::setShowTimeStamp(bool showTimeStamp)
19{
20 m_readTask->setShowTimeStamp(showTimeStamp);
21}
22
23void CanBusUtil::setShowFlags(bool showFlags)
24{
25 m_readTask->setShowFlags(showFlags);
26}
27
28void CanBusUtil::setConfigurationParameter(QCanBusDevice::ConfigurationKey key,
29 const QVariant &value)
30{
31 m_configurationParameter[key] = value;
32}
33
34bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, const QString &data)
35{
36 if (!m_canBus) {
37 m_output << tr(s: "Error: Cannot create QCanBus.") << Qt::endl;
38 return false;
39 }
40
41 m_pluginName = pluginName;
42 m_deviceName = deviceName;
43 m_data = data;
44 m_listening = data.isEmpty();
45
46 if (!connectCanDevice())
47 return false;
48
49 if (m_listening) {
50 if (m_readTask->isShowFlags())
51 m_canDevice->setConfigurationParameter(key: QCanBusDevice::CanFdKey, value: true);
52 connect(sender: m_canDevice.get(), signal: &QCanBusDevice::framesReceived,
53 context: m_readTask, slot: &ReadTask::handleFrames);
54 } else {
55 if (!sendData())
56 return false;
57 QTimer::singleShot(interval: 0, receiver: &m_app, slot&: QCoreApplication::quit);
58 }
59
60 return true;
61}
62
63int CanBusUtil::printPlugins()
64{
65 if (!m_canBus) {
66 m_output << tr(s: "Error: Cannot create QCanBus.") << Qt::endl;
67 return 1;
68 }
69
70 const QStringList plugins = m_canBus->plugins();
71 for (const QString &plugin : plugins)
72 m_output << plugin << Qt::endl;
73 return 0;
74}
75
76int CanBusUtil::printDevices(const QString &pluginName)
77{
78 if (!m_canBus) {
79 m_output << tr(s: "Error: Cannot create QCanBus.") << Qt::endl;
80 return 1;
81 }
82
83 QString errorMessage;
84 const QList<QCanBusDeviceInfo> devices = m_canBus->availableDevices(plugin: pluginName, errorMessage: &errorMessage);
85 if (!errorMessage.isEmpty()) {
86 m_output << tr(s: "Error gathering available devices: '%1'").arg(a: errorMessage) << Qt::endl;
87 return 1;
88 }
89
90 for (const QCanBusDeviceInfo &info : devices)
91 m_output << info.name() << Qt::endl;
92 return 0;
93}
94
95bool CanBusUtil::parseDataField(QCanBusFrame::FrameId &id, QString &payload)
96{
97 int hashMarkPos = m_data.indexOf(c: '#');
98 if (hashMarkPos < 0) {
99 m_output << tr(s: "Data field invalid: No hash mark found!") << Qt::endl;
100 return false;
101 }
102
103 id = QStringView{m_data}.left(n: hashMarkPos).toUInt(ok: nullptr, base: 16);
104 payload = m_data.right(n: m_data.size() - hashMarkPos - 1);
105
106 return true;
107}
108
109bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame)
110{
111 if (!payload.isEmpty() && payload.at(i: 0).toUpper() == 'R') {
112 frame->setFrameType(QCanBusFrame::RemoteRequestFrame);
113
114 if (payload.size() == 1) // payload = "R"
115 return true;
116
117 bool ok = false;
118 int rtrFrameLength = QStringView{payload}.mid(pos: 1).toInt(ok: &ok);
119 if (ok && rtrFrameLength >= 0 && rtrFrameLength <= 8) { // payload = "R8"
120 frame->setPayload(QByteArray(rtrFrameLength, 0));
121 return true;
122 }
123
124 m_output << tr(s: "Error: RTR frame length must be between 0 and 8 (including).") << Qt::endl;
125 return false;
126 }
127
128 if (!payload.isEmpty() && payload.at(i: 0) == '#') {
129 frame->setFlexibleDataRateFormat(true);
130 payload.remove(i: 0, len: 1);
131 }
132
133 const QRegularExpression re(QStringLiteral("^[0-9A-Fa-f]*$"));
134 if (!re.match(subject: payload).hasMatch()) {
135 m_output << tr(s: "Data field invalid: Only hex numbers allowed.") << Qt::endl;
136 return false;
137 }
138
139 if (payload.size() % 2 != 0) {
140 if (frame->hasFlexibleDataRateFormat()) {
141 enum { BitrateSwitchFlag = 1, ErrorStateIndicatorFlag = 2 };
142 const int flags = QStringView{payload}.left(n: 1).toInt(ok: nullptr, base: 16);
143 frame->setBitrateSwitch(flags & BitrateSwitchFlag);
144 frame->setErrorStateIndicator(flags & ErrorStateIndicatorFlag);
145 payload.remove(i: 0, len: 1);
146 } else {
147 m_output << tr(s: "Data field invalid: Size is not multiple of two.") << Qt::endl;
148 return false;
149 }
150 }
151
152 QByteArray bytes = QByteArray::fromHex(hexEncoded: payload.toLatin1());
153
154 const int maxSize = frame->hasFlexibleDataRateFormat() ? 64 : 8;
155 if (bytes.size() > maxSize) {
156 m_output << tr(s: "Data field invalid: Size is longer than %1 bytes.").arg(a: maxSize) << Qt::endl;
157 return false;
158 }
159
160 frame->setPayload(bytes);
161
162 return true;
163}
164
165bool CanBusUtil::connectCanDevice()
166{
167 if (!m_canBus->plugins().contains(str: m_pluginName)) {
168 m_output << tr(s: "Cannot find CAN bus plugin '%1'.").arg(a: m_pluginName) << Qt::endl;
169 return false;
170 }
171
172 m_canDevice.reset(p: m_canBus->createDevice(plugin: m_pluginName, interfaceName: m_deviceName));
173 if (!m_canDevice) {
174 m_output << tr(s: "Cannot create CAN bus device: '%1'").arg(a: m_deviceName) << Qt::endl;
175 return false;
176 }
177
178 const auto constEnd = m_configurationParameter.constEnd();
179 for (auto i = m_configurationParameter.constBegin(); i != constEnd; ++i)
180 m_canDevice->setConfigurationParameter(key: i.key(), value: i.value());
181
182 connect(sender: m_canDevice.get(), signal: &QCanBusDevice::errorOccurred, context: m_readTask, slot: &ReadTask::handleError);
183 if (!m_canDevice->connectDevice()) {
184 m_output << tr(s: "Cannot create CAN bus device: '%1'").arg(a: m_deviceName) << Qt::endl;
185 return false;
186 }
187
188 return true;
189}
190
191bool CanBusUtil::sendData()
192{
193 quint32 id;
194 QString payload;
195 QCanBusFrame frame;
196
197 if (!parseDataField(id, payload))
198 return false;
199
200 if (!setFrameFromPayload(payload, frame: &frame))
201 return false;
202
203 if (id > 0x1FFFFFFF) { // 29 bits
204 m_output << tr(s: "Cannot send invalid frame ID: '%1'").arg(a: id, fieldWidth: 0, base: 16) << Qt::endl;
205 return false;
206 }
207
208 frame.setFrameId(id);
209
210 if (frame.hasFlexibleDataRateFormat())
211 m_canDevice->setConfigurationParameter(key: QCanBusDevice::CanFdKey, value: true);
212
213 return m_canDevice->writeFrame(frame);
214}
215

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