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 
CanBusUtil(QTextStream & output,QCoreApplication & app,QObject * parent)42 CanBusUtil::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 
setShowTimeStamp(bool showTimeStamp)51 void CanBusUtil::setShowTimeStamp(bool showTimeStamp)
52 {
53     m_readTask->setShowTimeStamp(showTimeStamp);
54 }
55 
setShowFlags(bool showFlags)56 void CanBusUtil::setShowFlags(bool showFlags)
57 {
58     m_readTask->setShowFlags(showFlags);
59 }
60 
setConfigurationParameter(QCanBusDevice::ConfigurationKey key,const QVariant & value)61 void CanBusUtil::setConfigurationParameter(QCanBusDevice::ConfigurationKey key,
62                                            const QVariant &value)
63 {
64     m_configurationParameter[key] = value;
65 }
66 
start(const QString & pluginName,const QString & deviceName,const QString & data)67 bool CanBusUtil::start(const QString &pluginName, const QString &deviceName, const QString &data)
68 {
69     if (!m_canBus) {
70         m_output << tr("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(QCanBusDevice::CanFdKey, true);
85         connect(m_canDevice.data(), &QCanBusDevice::framesReceived,
86                 m_readTask, &ReadTask::handleFrames);
87     } else {
88         if (!sendData())
89             return false;
90         QTimer::singleShot(0, &m_app, QCoreApplication::quit);
91     }
92 
93     return true;
94 }
95 
printPlugins()96 int CanBusUtil::printPlugins()
97 {
98     if (!m_canBus) {
99         m_output << tr("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 
printDevices(const QString & pluginName)109 int CanBusUtil::printDevices(const QString &pluginName)
110 {
111     if (!m_canBus) {
112         m_output << tr("Error: Cannot create QCanBus.") << Qt::endl;
113         return 1;
114     }
115 
116     QString errorMessage;
117     const QList<QCanBusDeviceInfo> devices = m_canBus->availableDevices(pluginName, &errorMessage);
118     if (!errorMessage.isEmpty()) {
119         m_output << tr("Error gathering available devices: '%1'").arg(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 
parseDataField(quint32 & id,QString & payload)128 bool CanBusUtil::parseDataField(quint32 &id, QString &payload)
129 {
130     int hashMarkPos = m_data.indexOf('#');
131     if (hashMarkPos < 0) {
132         m_output << tr("Data field invalid: No hash mark found!") << Qt::endl;
133         return false;
134     }
135 
136     id = m_data.leftRef(hashMarkPos).toUInt(nullptr, 16);
137     payload = m_data.right(m_data.size() - hashMarkPos - 1);
138 
139     return true;
140 }
141 
setFrameFromPayload(QString payload,QCanBusFrame * frame)142 bool CanBusUtil::setFrameFromPayload(QString payload, QCanBusFrame *frame)
143 {
144     if (!payload.isEmpty() && payload.at(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(1).toInt(&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("Error: RTR frame length must be between 0 and 8 (including).") << Qt::endl;
158         return false;
159     }
160 
161     if (!payload.isEmpty() && payload.at(0) == '#') {
162         frame->setFlexibleDataRateFormat(true);
163         payload.remove(0, 1);
164     }
165 
166     const QRegularExpression re(QStringLiteral("^[0-9A-Fa-f]*$"));
167     if (!re.match(payload).hasMatch()) {
168         m_output << tr("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(1).toInt(nullptr, 16);
176             frame->setBitrateSwitch(flags & BitrateSwitchFlag);
177             frame->setErrorStateIndicator(flags & ErrorStateIndicatorFlag);
178             payload.remove(0, 1);
179         } else {
180             m_output << tr("Data field invalid: Size is not multiple of two.") << Qt::endl;
181             return false;
182         }
183     }
184 
185     QByteArray bytes = QByteArray::fromHex(payload.toLatin1());
186 
187     const int maxSize = frame->hasFlexibleDataRateFormat() ? 64 : 8;
188     if (bytes.size() > maxSize) {
189         m_output << tr("Data field invalid: Size is longer than %1 bytes.").arg(maxSize) << Qt::endl;
190         return false;
191     }
192 
193     frame->setPayload(bytes);
194 
195     return true;
196 }
197 
connectCanDevice()198 bool CanBusUtil::connectCanDevice()
199 {
200     if (!m_canBus->plugins().contains(m_pluginName)) {
201         m_output << tr("Cannot find CAN bus plugin '%1'.").arg(m_pluginName) << Qt::endl;
202         return false;
203     }
204 
205     m_canDevice.reset(m_canBus->createDevice(m_pluginName, m_deviceName));
206     if (!m_canDevice) {
207         m_output << tr("Cannot create CAN bus device: '%1'").arg(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(i.key(), i.value());
214 
215     connect(m_canDevice.data(), &QCanBusDevice::errorOccurred, m_readTask, &ReadTask::handleError);
216     if (!m_canDevice->connectDevice()) {
217         m_output << tr("Cannot create CAN bus device: '%1'").arg(m_deviceName) << Qt::endl;
218         return false;
219     }
220 
221     return true;
222 }
223 
sendData()224 bool 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))
234         return false;
235 
236     if (id > 0x1FFFFFFF) { // 29 bits
237         m_output << tr("Cannot send invalid frame ID: '%1'").arg(id, 0, 16) << Qt::endl;
238         return false;
239     }
240 
241     frame.setFrameId(id);
242 
243     if (frame.hasFlexibleDataRateFormat())
244         m_canDevice->setConfigurationParameter(QCanBusDevice::CanFdKey, true);
245 
246     return m_canDevice->writeFrame(frame);
247 }
248