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 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 "qcanbus.h"
38#include "qcanbusfactory.h"
39
40#include <QtCore/qcoreapplication.h>
41#include <QtCore/qglobalstatic.h>
42#include <QtCore/qlist.h>
43#include <QtCore/qobject.h>
44#include <QtCore/qpluginloader.h>
45
46#include <private/qfactoryloader_p.h>
47
48#define QCanBusFactory_iid "org.qt-project.Qt.QCanBusFactory"
49
50QT_BEGIN_NAMESPACE
51
52class QCanBusPrivate
53{
54public:
55 QCanBusPrivate() { }
56 QCanBusPrivate(int index, const QJsonObject &meta) : meta(meta), index(index) {}
57
58 QJsonObject meta;
59 QObject *factory = nullptr;
60 int index = -1;
61};
62
63Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qFactoryLoader,
64 (QCanBusFactory_iid, QLatin1String("/canbus")))
65
66typedef QMap<QString, QCanBusPrivate> QCanBusPluginStore;
67Q_GLOBAL_STATIC(QCanBusPluginStore, qCanBusPlugins)
68
69static QCanBus *globalInstance = nullptr;
70
71static void loadPlugins()
72{
73 const QList<QJsonObject> meta = qFactoryLoader()->metaData();
74 for (int i = 0; i < meta.count(); i++) {
75 const QJsonObject obj = meta.at(i).value(key: QLatin1String("MetaData")).toObject();
76 if (obj.isEmpty())
77 continue;
78
79 qCanBusPlugins()->insert(akey: obj.value(key: QLatin1String("Key")).toString(), avalue: {i, obj});
80 }
81}
82
83/*!
84 \class QCanBus
85 \inmodule QtSerialBus
86 \since 5.8
87
88 \brief The QCanBus class handles registration and creation of bus plugins.
89
90 QCanBus loads Qt CAN Bus plugins at runtime. The ownership of serial bus plugins is
91 transferred to the loader.
92*/
93
94/*!
95 Returns a pointer to the QCanBus class. The object is loaded if necessary. QCanBus
96 uses the singleton design pattern.
97*/
98QCanBus *QCanBus::instance()
99{
100 if (!globalInstance)
101 globalInstance = new QCanBus();
102 return globalInstance;
103}
104
105/*!
106 Returns a list of identifiers for all loaded plugins.
107*/
108QStringList QCanBus::plugins() const
109{
110 return qCanBusPlugins()->keys();
111}
112
113static void setErrorMessage(QString *result, const QString &message)
114{
115 if (!result)
116 return;
117
118 *result = message;
119}
120
121static QObject *canBusFactory(const QString &plugin, QString *errorMessage)
122{
123 if (Q_UNLIKELY(!qCanBusPlugins()->contains(plugin))) {
124 setErrorMessage(result: errorMessage, message: QCanBus::tr(s: "No such plugin: '%1'").arg(a: plugin));
125 return nullptr;
126 }
127
128 QCanBusPrivate d = qCanBusPlugins()->value(akey: plugin);
129 if (!d.factory) {
130 d.factory = qFactoryLoader->instance(index: d.index);
131
132 if (d.factory)
133 qCanBusPlugins()->insert(akey: plugin, avalue: d);
134 }
135
136 if (Q_UNLIKELY(!d.factory))
137 setErrorMessage(result: errorMessage, message: QCanBus::tr(s: "No factory for plugin: '%1'").arg(a: plugin));
138
139 return d.factory;
140}
141
142/*!
143 \since 5.9
144
145 Returns the available interfaces for \a plugin. In case of failure, the optional
146 parameter \a errorMessage returns a textual error description.
147
148 \note Some plugins might not or only partially support this function.
149
150 For example, the following call returns a list of all available SocketCAN
151 interfaces (which can be used for \l createDevice()):
152
153 \code
154 QString errorString;
155 const QList<QCanBusDeviceInfo> devices = QCanBus::instance()->availableDevices(
156 QStringLiteral("socketcan"), &errorString);
157 if (!errorString.isEmpty())
158 qDebug() << errorString;
159 \endcode
160
161 \sa createDevice()
162*/
163QList<QCanBusDeviceInfo> QCanBus::availableDevices(const QString &plugin, QString *errorMessage) const
164{
165 const QObject *obj = canBusFactory(plugin, errorMessage);
166 if (Q_UNLIKELY(!obj))
167 return QList<QCanBusDeviceInfo>();
168
169 const QCanBusFactoryV2 *factoryV2 = qobject_cast<QCanBusFactoryV2 *>(object: obj);
170 if (Q_UNLIKELY(!factoryV2)) {
171 setErrorMessage(result: errorMessage,
172 message: tr(s: "The plugin '%1' does not provide this function.").arg(a: plugin));
173 return QList<QCanBusDeviceInfo>();
174 }
175
176 QString errorString;
177 QList<QCanBusDeviceInfo> result = factoryV2->availableDevices(errorMessage: &errorString);
178
179 setErrorMessage(result: errorMessage, message: errorString);
180 return result;
181}
182
183/*!
184 Creates a CAN bus device. \a plugin is the name of the plugin as returned by the \l plugins()
185 method. \a interfaceName is the CAN bus interface name. In case of failure, the optional
186 parameter \a errorMessage returns a textual error description.
187
188 Ownership of the returned plugin is transferred to the caller.
189 Returns \c nullptr if no suitable device can be found.
190
191 For example, the following call would connect to the SocketCAN interface vcan0:
192
193 \code
194 QString errorString;
195 QCanBusDevice *device = QCanBus::instance()->createDevice(
196 QStringLiteral("socketcan"), QStringLiteral("vcan0"), &errorString);
197 if (!device)
198 qDebug() << errorString;
199 else
200 device->connectDevice();
201 \endcode
202
203 \note The \a interfaceName is plugin-dependent. See the corresponding plugin documentation
204 for more information: \l {CAN Bus Plugins}. To get a list of available interfaces,
205 \l availableDevices() can be used.
206
207 \sa availableDevices()
208*/
209QCanBusDevice *QCanBus::createDevice(const QString &plugin, const QString &interfaceName,
210 QString *errorMessage) const
211{
212 const QObject *obj = canBusFactory(plugin, errorMessage);
213 if (Q_UNLIKELY(!obj))
214 return nullptr;
215
216 const QCanBusFactoryV2 *factoryV2 = qobject_cast<QCanBusFactoryV2 *>(object: obj);
217 if (Q_LIKELY(factoryV2))
218 return factoryV2->createDevice(interfaceName, errorMessage);
219
220 const QCanBusFactory *factory = qobject_cast<QCanBusFactory *>(object: obj);
221 if (factory)
222 return factory->createDevice(interfaceName, errorMessage);
223
224 setErrorMessage(result: errorMessage,
225 message: tr(s: "The plugin '%1' does not provide this function.").arg(a: plugin));
226 return nullptr;
227}
228
229QCanBus::QCanBus(QObject *parent) :
230 QObject(parent)
231{
232 loadPlugins();
233}
234
235QT_END_NAMESPACE
236

source code of qtserialbus/src/serialbus/qcanbus.cpp