1/****************************************************************************
2**
3** Copyright (C) 2018 Andre Hartmann <aha_1980@gmx.de>
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtSerialBus module of the Qt Toolkit.
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 "virtualcanbackend.h"
38
39#include <QtCore/qdatetime.h>
40#include <QtCore/qloggingcategory.h>
41#include <QtCore/qregularexpression.h>
42
43#include <QtNetwork/qtcpserver.h>
44#include <QtNetwork/qtcpsocket.h>
45
46QT_BEGIN_NAMESPACE
47
48Q_DECLARE_LOGGING_CATEGORY(QT_CANBUS_PLUGINS_VIRTUALCAN)
49
50enum {
51 ServerDefaultTcpPort = 35468,
52 VirtualChannels = 2
53};
54
55static const char RemoteRequestFlag = 'R';
56static const char ExtendedFormatFlag = 'X';
57static const char FlexibleDataRateFlag = 'F';
58static const char BitRateSwitchFlag = 'B';
59static const char ErrorStateFlag = 'E';
60static const char LocalEchoFlag = 'L';
61
62VirtualCanServer::VirtualCanServer(QObject *parent)
63 : QObject(parent)
64{
65 qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN, "Server [%p] constructed.", this);
66}
67
68VirtualCanServer::~VirtualCanServer()
69{
70 qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN, "Server [%p] destructed.", this);
71}
72
73void VirtualCanServer::start(quint16 port)
74{
75 // If there is already a server object, return immediately
76 if (m_server) {
77 qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN, "Server [%p] is already running.", this);
78 return;
79 }
80
81 // Otherwise try to start a new server. If there is already
82 // another server listen on the specified port, give up.
83 m_server = new QTcpServer(this);
84 if (!m_server->listen(address: QHostAddress::LocalHost, port)) {
85 qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN,
86 "Server [%p] could not be started, port %d is already in use.", this, port);
87 m_server->deleteLater();
88 m_server = nullptr;
89 return;
90 }
91
92 // Server successfully started
93 connect(sender: m_server, signal: &QTcpServer::newConnection, receiver: this, slot: &VirtualCanServer::connected);
94 qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN,
95 "Server [%p] started and listening on port %d.", this, port);
96 return;
97}
98
99void VirtualCanServer::connected()
100{
101 while (m_server->hasPendingConnections()) {
102 qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN, "Server [%p] client connected.", this);
103 QTcpSocket *next = m_server->nextPendingConnection();
104 m_serverSockets.append(t: next);
105 connect(sender: next, signal: &QIODevice::readyRead, receiver: this, slot: &VirtualCanServer::readyRead);
106 connect(sender: next, signal: &QTcpSocket::disconnected, receiver: this, slot: &VirtualCanServer::disconnected);
107 }
108}
109
110void VirtualCanServer::disconnected()
111{
112 qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN, "Server [%p] client disconnected.", this);
113
114 auto socket = qobject_cast<QTcpSocket *>(object: sender());
115 Q_ASSERT(socket);
116
117 m_serverSockets.removeOne(t: socket);
118 socket->deleteLater();
119}
120
121void VirtualCanServer::readyRead()
122{
123 auto readSocket = qobject_cast<QTcpSocket *>(object: sender());
124 Q_ASSERT(readSocket);
125
126 while (readSocket->canReadLine()) {
127 const QByteArray command = readSocket->readLine().trimmed();
128 qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN,
129 "Server [%p] received: '%s'.", this, command.constData());
130
131 if (command.startsWith(c: "connect:")) {
132 const QVariant interfaces = readSocket->property(name: "interfaces");
133 QStringList list = interfaces.toStringList();
134 list.append(t: command.mid(index: int(strlen(s: "connect:"))));
135 readSocket->setProperty(name: "interfaces", value: list);
136
137 } else if (command.startsWith(c: "disconnect:")) {
138 const QVariant interfaces = readSocket->property(name: "interfaces");
139 QStringList list = interfaces.toStringList();
140 list.removeAll(t: command.mid(index: int(strlen(s: "disconnect:"))));
141 readSocket->setProperty(name: "interfaces", value: list);
142 readSocket->disconnectFromHost();
143
144 } else {
145 const QByteArrayList commandList = command.split(sep: ':');
146 Q_ASSERT(commandList.size() == 2);
147
148 for (QTcpSocket *writeSocket : qAsConst(t&: m_serverSockets)) {
149 // Don't send the frame back to its origin
150 if (writeSocket == readSocket)
151 continue;
152
153 // Send frame to all clients registered to the same interface as sender
154 const QVariant property = writeSocket->property(name: "interfaces");
155 if (!property.isValid())
156 continue;
157
158 const QStringList propertyList = property.toStringList();
159 if (propertyList.contains(str: commandList.first()))
160 writeSocket->write(data: commandList.last() + '\n');
161 }
162 }
163 }
164}
165
166Q_GLOBAL_STATIC(VirtualCanServer, g_server)
167
168VirtualCanBackend::VirtualCanBackend(const QString &interface, QObject *parent)
169 : QCanBusDevice(parent)
170{
171 m_url = QUrl(interface);
172 const QString canDevice = m_url.fileName();
173
174 const QRegularExpression re(QStringLiteral("can(\\d)"));
175 const QRegularExpressionMatch match = re.match(subject: canDevice);
176
177 if (Q_UNLIKELY(!match.hasMatch())) {
178 qCWarning(QT_CANBUS_PLUGINS_VIRTUALCAN,
179 "Invalid interface '%ls'.", qUtf16Printable(interface));
180 setError(errorText: tr(s: "Invalid interface '%1'.").arg(a: interface), QCanBusDevice::ConnectionError);
181 return;
182 }
183
184 const uint channel = match.captured(nth: 1).toUInt();
185 if (Q_UNLIKELY(channel >= VirtualChannels)) {
186 qCWarning(QT_CANBUS_PLUGINS_VIRTUALCAN,
187 "Invalid interface '%ls'.", qUtf16Printable(interface));
188 setError(errorText: tr(s: "Invalid interface '%1'.").arg(a: interface), QCanBusDevice::ConnectionError);
189 return;
190 }
191
192 m_channel = channel;
193}
194
195VirtualCanBackend::~VirtualCanBackend()
196{
197 qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN, "Client [%p] socket destructed.", this);
198}
199
200bool VirtualCanBackend::open()
201{
202 setState(QCanBusDevice::ConnectingState);
203
204 const QString host = m_url.host();
205 const QHostAddress address = host.isEmpty() ? QHostAddress::LocalHost : QHostAddress(host);
206 const quint16 port = static_cast<quint16>(m_url.port(defaultPort: ServerDefaultTcpPort));
207
208 if (address.isLoopback())
209 g_server->start(port);
210
211 m_clientSocket = new QTcpSocket(this);
212 m_clientSocket->connectToHost(address, port, mode: QIODevice::ReadWrite);
213 connect(sender: m_clientSocket, signal: &QAbstractSocket::connected, receiver: this, slot: &VirtualCanBackend::clientConnected);
214 connect(sender: m_clientSocket, signal: &QAbstractSocket::disconnected, receiver: this, slot: &VirtualCanBackend::clientDisconnected);
215 connect(sender: m_clientSocket, signal: &QIODevice::readyRead, receiver: this, slot: &VirtualCanBackend::clientReadyRead);
216 qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN, "Client [%p] socket created.", this);
217 return true;
218}
219
220void VirtualCanBackend::close()
221{
222 qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN, "Client [%p] sends disconnect to server.", this);
223
224 m_clientSocket->write(data: "disconnect:can" + QByteArray::number(m_channel) + '\n');
225}
226
227void VirtualCanBackend::setConfigurationParameter(int key, const QVariant &value)
228{
229 if (key == QCanBusDevice::ReceiveOwnKey || key == QCanBusDevice::CanFdKey)
230 QCanBusDevice::setConfigurationParameter(key, value);
231}
232
233/*
234 Protocol format: All data is in ASCII, one CAN message per line,
235 each line ends with line feed '\n'.
236
237 Format: "<CAN-Channel>:<Flags>#<CAN-ID>#<Data-Bytes>\n"
238 Example: "can0:XF#123#123456\n"
239
240 The first part is the destination CAN channel, "can0" or "can1",
241 followed by the flags list:
242
243 * R - Remote Request
244 * X - Extended Frame Format
245 * F - Flexible Data Rate Format
246 * B - Bitrate Switch
247 * E - Error State Indicator
248 * L - Local Echo
249
250 Afterwards the CAN-ID and the data follows, both separated by '#'.
251*/
252
253bool VirtualCanBackend::writeFrame(const QCanBusFrame &frame)
254{
255 if (Q_UNLIKELY(state() != ConnectedState)) {
256 qCWarning(QT_CANBUS_PLUGINS_VIRTUALCAN, "Error: Cannot write frame as client is not connected!");
257 return false;
258 }
259
260 bool canFdEnabled = configurationParameter(key: QCanBusDevice::CanFdKey).toBool();
261 if (Q_UNLIKELY(frame.hasFlexibleDataRateFormat() && !canFdEnabled)) {
262 qCWarning(QT_CANBUS_PLUGINS_VIRTUALCAN,
263 "Error: Cannot write CAN FD frame as CAN FD is not enabled!");
264 return false;
265 }
266
267 QByteArray flags;
268 if (frame.frameType() == QCanBusFrame::RemoteRequestFrame)
269 flags.append(c: RemoteRequestFlag);
270 if (frame.hasExtendedFrameFormat())
271 flags.append(c: ExtendedFormatFlag);
272 if (frame.hasFlexibleDataRateFormat())
273 flags.append(c: FlexibleDataRateFlag);
274 if (frame.hasBitrateSwitch())
275 flags.append(c: BitRateSwitchFlag);
276 if (frame.hasErrorStateIndicator())
277 flags.append(c: ErrorStateFlag);
278 if (frame.hasLocalEcho())
279 flags.append(c: LocalEchoFlag);
280 const QByteArray frameId = QByteArray::number(frame.frameId());
281 const QByteArray command = "can" + QByteArray::number(m_channel)
282 + ':' + frameId + '#' + flags + '#' + frame.payload().toHex() + '\n';
283 m_clientSocket->write(data: command);
284
285 if (configurationParameter(key: QCanBusDevice::ReceiveOwnKey).toBool()) {
286 const qint64 timeStamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
287 QCanBusFrame echoFrame = frame;
288 echoFrame.setLocalEcho(true);
289 echoFrame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(usec: timeStamp * 1000));
290 enqueueReceivedFrames(newFrames: {echoFrame});
291 }
292
293 emit framesWritten(framesCount: qint64(1));
294 return true;
295}
296
297QString VirtualCanBackend::interpretErrorFrame(const QCanBusFrame &errorFrame)
298{
299 Q_UNUSED(errorFrame);
300 return QString();
301}
302
303QList<QCanBusDeviceInfo> VirtualCanBackend::interfaces()
304{
305 QList<QCanBusDeviceInfo> result;
306
307 for (int channel = 0; channel < VirtualChannels; ++channel) {
308 result.append(t: std::move(createDeviceInfo(
309 QStringLiteral("can%1").arg(a: channel), serialNumber: QString(),
310 QStringLiteral("Qt Virtual CAN bus"), channel,
311 isVirtual: true, isFlexibleDataRateCapable: true)));
312 }
313
314 return result;
315}
316
317void VirtualCanBackend::clientConnected()
318{
319 qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN, "Client [%p] socket connected.", this);
320 m_clientSocket->write(data: "connect:can" + QByteArray::number(m_channel) + '\n');
321
322 setState(QCanBusDevice::ConnectedState);
323}
324
325void VirtualCanBackend::clientDisconnected()
326{
327 qCInfo(QT_CANBUS_PLUGINS_VIRTUALCAN, "Client [%p] socket disconnected.", this);
328
329 setState(UnconnectedState);
330}
331
332void VirtualCanBackend::clientReadyRead()
333{
334 while (m_clientSocket->canReadLine()) {
335 const QByteArray answer = m_clientSocket->readLine().trimmed();
336 qCDebug(QT_CANBUS_PLUGINS_VIRTUALCAN, "Client [%p] received: '%s'.",
337 this, answer.constData());
338
339 if (answer.startsWith(a: "disconnect:can" + QByteArray::number(m_channel))) {
340 m_clientSocket->disconnectFromHost();
341 continue;
342 }
343
344 const QByteArrayList list = answer.split(sep: '#');
345 Q_ASSERT(list.size() == 3);
346
347 const quint32 id = list.at(i: 0).toUInt();
348 const QByteArray flags = list.at(i: 1);
349 const QByteArray data = QByteArray::fromHex(hexEncoded: list.at(i: 2));
350 const qint64 timeStamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
351 QCanBusFrame frame(id, data);
352 frame.setTimeStamp(QCanBusFrame::TimeStamp::fromMicroSeconds(usec: timeStamp * 1000));
353 if (flags.contains(c: RemoteRequestFlag))
354 frame.setFrameType(QCanBusFrame::RemoteRequestFrame);
355 frame.setExtendedFrameFormat(flags.contains(c: ExtendedFormatFlag));
356 frame.setFlexibleDataRateFormat(flags.contains(c: FlexibleDataRateFlag));
357 frame.setBitrateSwitch(flags.contains(c: BitRateSwitchFlag));
358 frame.setErrorStateIndicator(flags.contains(c: ErrorStateFlag));
359 frame.setLocalEcho(flags.contains(c: LocalEchoFlag));
360 enqueueReceivedFrames(newFrames: {frame});
361 }
362}
363
364QT_END_NAMESPACE
365

source code of qtserialbus/src/plugins/canbus/virtualcan/virtualcanbackend.cpp