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 Creates a CAN bus device. \a plugin is the name of the plugin as returned by the \l plugins()
152 method. \a interfaceName is the CAN bus interface name. In case of failure, the optional
153 parameter \a errorMessage returns a textual error description.
154
155 Ownership of the returned plugin is transferred to the caller.
156 Returns \c nullptr if no suitable device can be found.
157
158 For example, the following call would connect to the SocketCAN interface vcan0:
159
160 \code
161 QString errorString;
162 QCanBusDevice *device = QCanBus::instance()->createDevice(
163 QStringLiteral("socketcan"), QStringLiteral("vcan0"), &errorString);
164 if (!device)
165 qDebug() << errorString;
166 else
167 device->connectDevice();
168 \endcode
169
170 \note The \a interfaceName is plugin-dependent. See the corresponding plugin documentation
171 for more information: \l {CAN Bus Plugins}. To get a list of available interfaces,
172 \l availableDevices() can be used.
173
174 \sa availableDevices()
175*/
176QCanBusDevice *QCanBus::createDevice(const QString &plugin, const QString &interfaceName,
177 QString *errorMessage) const
178{
179 const QObject *obj = canBusFactory(plugin, errorMessage);
180 if (Q_UNLIKELY(!obj))
181 return nullptr;
182
183 const QCanBusFactory *factory = qobject_cast<const QCanBusFactory *>(object: obj);
184 if (Q_LIKELY(factory))
185 return factory->createDevice(interfaceName, errorMessage);
186
187 setErrorMessage(result: errorMessage,
188 message: tr(s: "The plugin '%1' does not provide this function.").arg(a: plugin));
189 return nullptr;
190}
191
192QCanBus::QCanBus(QObject *parent) :
193 QObject(parent)
194{
195 loadPlugins();
196}
197
198QT_END_NAMESPACE
199

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