I need to access the C ++ API to work with the CAN bus. It seems like the best solution is to write a QML wrapper to provide all the functionality I need.
Here is my canservice.cpp :
#include "canservice.h" #include <QCanBus> #include <QDebug> #include <QCanBusFrame> #include <QTimer> #include <QtCore/qbytearray.h> #include <QtCore/qvariant.h> #include <QtCore/qdebug.h> CANService::CANService(QObject *parent) : QObject(parent), m_canDevice(nullptr) { QString status = ""; initializeSettings(); // TODO" disable sending messages until connection is stablished } CANService::~CANService() { delete m_canDevice; } void CANService::receiveError(QCanBusDevice::CanBusError error) const { switch (error) { case QCanBusDevice::ReadError: case QCanBusDevice::WriteError: case QCanBusDevice::ConnectionError: case QCanBusDevice::ConfigurationError: case QCanBusDevice::UnknownError: qWarning() << m_canDevice->errorString(); default: break; } } void CANService::initializeSettings() { foreach (const QByteArray &backend, QCanBus::instance()->plugins()) { qInfo() << "found: " + backend; if (backend == "socketcan") { // found socketcan m_currentSettings.backendName = "socketcan"; break; } } if(m_currentSettings.backendName.length() < 1) { qWarning() << "did not find a backend"; } m_currentSettings.backendName = "socketcan"; m_currentSettings.deviceInterfaceName = QStringLiteral("vcan0"); } void CANService::connectDevice() { m_canDevice = QCanBus::instance()->createDevice(m_currentSettings.backendName.toLocal8Bit(), m_currentSettings.deviceInterfaceName); if (!m_canDevice) { showStatusMessage(tr("Connection error")); return; } connect(m_canDevice, &QCanBusDevice::errorOccurred, this, &MainWindow::receiveError); connect(m_canDevice, &QCanBusDevice::framesReceived, this, &MainWindow::checkMessages); connect(m_canDevice, &QCanBusDevice::framesWritten, this, &MainWindow::framesWritten); if (p.useConfigurationEnabled) { foreach (const ConnectDialog::ConfigurationItem &item, p.configurations) m_canDevice->setConfigurationParameter(item.first, item.second); } if (!m_canDevice->connectDevice()) { delete m_canDevice; m_canDevice = nullptr; qInfo() << "Connection error"; } else { qInfo() << m_currentSettings.backendName << "is connected"; } } void CANService::sendMessage() const { if (!m_canDevice) return; // TODO: replace test message with input QByteArray writings = dataFromHex("1122334455667788"); QCanBusFrame frame; const int maxPayload = 8; // 64 : 8; int size = writings.size(); if (size > maxPayload) size = maxPayload; writings = writings.left(size); frame.setPayload(writings); //TODO: get from UI qint32 id = 100; if (id > 2047) { //11 bits id = 2047; } frame.setFrameId(id); frame.setExtendedFrameFormat(true); // frame.setFrameType(QCanBusFrame::RemoteRequestFrame); // frame.setFrameType(QCanBusFrame::ErrorFrame); frame.setFrameType(QCanBusFrame::DataFrame); m_canDevice->writeFrame(frame); } void CANService::checkMessages() { if (!m_canDevice) return; const QCanBusFrame frame = m_canDevice->readFrame(); const qint8 dataLength = frame.payload().size(); const qint32 id = frame.frameId(); QString view; if (frame.frameType() == QCanBusFrame::ErrorFrame) { interpretError(view, frame); } else { view += QLatin1String("Id: "); view += QString::number(id, 16).toUpper(); view += QLatin1String(" bytes: "); view += QString::number(dataLength, 10); view += QLatin1String(" data: "); view += dataToHex(frame.payload()); } if (frame.frameType() == QCanBusFrame::RemoteRequestFrame) { qInfo() << "got remote request message" << view; } else if (frame.frameType() == QCanBusFrame::ErrorFrame) { qWarning() << "got can error frame: " << view; } else { qInfo() << "got can frame: " << view; } } void CANService::interpretError(QString &view, const QCanBusFrame &frame) { if (!m_canDevice) return; view = m_canDevice->interpretErrorFrame(frame); } static QByteArray dataToHex(const QByteArray &data) { QByteArray result = data.toHex().toUpper(); for (int i = 0; i < result.size(); i += 3) result.insert(i, ' '); return result; } static QByteArray dataFromHex(const QString &hex) { QByteArray line = hex.toLatin1(); line.replace(' ', QByteArray()); return QByteArray::fromHex(line); }
In canservice.h I have:
#ifndef CANSERVICE_H #define CANSERVICE_H #include <QObject> #include <QQuickItem> #include <QCanBusDevice> class CANService : public QObject { Q_OBJECT public: explicit CANService(QObject *parent = 0); typedef QPair<QCanBusDevice::ConfigurationKey, QVariant> ConfigurationItem; struct Settings { QString backendName; QString deviceInterfaceName; QList<ConfigurationItem> configurations; bool useConfigurationEnabled; }; void connectDevice(); Q_INVOKABLE void connect(const QString &query) { qDebug() << "invoking connect with " << query; } explicit ConnectDialog(QWidget *parent = nullptr); ~ConnectDialog(); Settings settings() const; private: Settings m_currentSettings; void initializeSettings(); signals: public slots: }; qmlRegisterType<MyObject>("can.myapp", 1, 0, "CANService"); #endif // CANSERVICE_H
In my QML file, I first try to import the newly defined service: import can.myapp 1.0 , and then declare an instance of it:
CANService { id: "myCanService" }
When I try to run this application and load the QML file that makes the CANService call, it does not load, and I get the following error in the application console:
component not ready: "file:///home/aras/Projects/myapp/apps/com.myapp.diagnostics/Diagnostics.qml:5 module \"can.myapp\" is not installed\n"
EDIT : The problem is most likely where I call qmlRegisterType . I use appman , so my application does not have a main function. Where is the right place to run qmlRegisterType ?