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

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