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 | |
50 | QT_BEGIN_NAMESPACE |
51 | |
52 | class QCanBusPrivate |
53 | { |
54 | public: |
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 | |
63 | Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qFactoryLoader, |
64 | (QCanBusFactory_iid, QLatin1String("/canbus" ))) |
65 | |
66 | typedef QMap<QString, QCanBusPrivate> QCanBusPluginStore; |
67 | Q_GLOBAL_STATIC(QCanBusPluginStore, qCanBusPlugins) |
68 | |
69 | static QCanBus *globalInstance = nullptr; |
70 | |
71 | static 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 | */ |
98 | QCanBus *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 | */ |
108 | QStringList QCanBus::plugins() const |
109 | { |
110 | return qCanBusPlugins()->keys(); |
111 | } |
112 | |
113 | static void setErrorMessage(QString *result, const QString &message) |
114 | { |
115 | if (!result) |
116 | return; |
117 | |
118 | *result = message; |
119 | } |
120 | |
121 | static 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 | */ |
163 | QList<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 | */ |
209 | QCanBusDevice *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 | |
229 | QCanBus::QCanBus(QObject *parent) : |
230 | QObject(parent) |
231 | { |
232 | loadPlugins(); |
233 | } |
234 | |
235 | QT_END_NAMESPACE |
236 | |