2016-09-07 4 views
0

CAN 버스로 작업하려면 C++ API에 액세스해야합니다. 가장 좋은 솔루션은 QML 래퍼를 작성하여 필요한 모든 기능을 제공하는 것입니다. 여기 module [] is not installed - QML 용 사용자 정의 C++ 래퍼 등록

지금까지 내 canservice.cpp입니다 :

내 QML 파일에서
#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 

내가 먼저 새로 정의 된 서비스를 가져 오려고 : 다음 canservice.h에서

#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); 
} 

나는이 import can.myapp 1.0을하고 나는 선언 그 예 :

CANService { 
    id: "myCanService" 
} 

스피이 응용 프로그램을 실행하고 CANService에 대한 호출을 만드는 QML 파일을로드는로드하지 않고 나는 응용 프로그램 콘솔에서 다음과 같은 오류 얻을 :

component not ready: 
"file:///home/aras/Projects/myapp/apps/com.myapp.diagnostics/Diagnostics.qml:5 module \"can.myapp\" is not installed\n" 

편집을 : 문제가 어디에와 가능성이 높습니다 I 전화는 qmlRegisterType입니다. 내 응용 프로그램이 main 기능이 없도록 appman을 사용하고 있습니다. qmlRegisterType에서 어디를 달릴 수 있습니까?

+0

관심을 가질만한 곳이 http://gitlab.unique-conception.org/groups/qt-can-2.0 – GrecKo

답변

2

이전 게시물에 비슷한 태그를 지정했기 때문에 응답하고 있습니다. 여기

는 몇 가지 질문입니다 : 1) 나의 이해는 당신이 QtQuickApplicationViewer (당신이 그런 식으로로드하는 경우)

를 인스턴스화하기 전에이가 main 방법으로 호출 할 필요가 있다는 것입니다 어디 qmlRegisterType<MyObject>("can.myapp", 1, 0, "CANService");을 요구하고있다. 2) 간단한 예제 (간단한 QString 값을 함수에 전달)로 작동합니까?

내가 적극적으로 바로 순간에 Qt를 코딩 아니지만, 희망이 올바른 방향을 가리킬 수 있습니다 간단한 위해 (

여기

사용자 정의 구성 요소를 사용하여 내 응용 프로그램 중 하나에서 "주요"방법입니다 명령 줄을 통한 Java 연결).

import com.lightassistant 1.0 
Item { 
    LightAssistant { 
    id: la 
    } 

    function isIos(){ 
    return la.isIOS(); 
    } 

} 

은 정말 뭔가를하고 제안 : 여기

#include <QtGui/QGuiApplication> 
#include <QtCore> 
#include <QtQml> 
#include "qtquick2applicationviewer.h" 
#include "globals.h" 
#include "lightassistant.h" 
#include "reporting.h" 

int main(int argc, char *argv[]) 
{ 
    QGuiApplication app(argc, argv); 

    //C++ Types 
    qmlRegisterType<LightAssistant>("com.lightassistant", 1, 0, "LightAssistant"); 
    qmlRegisterType<Reporting>("com.lightassistant", 1, 0, "Reporting"); 
    //Singletons 
    //qmlRegisterSingletonType(QUrl("file:///qml/LightAssistant/Styles.qml"), "com.lightassistant", 1, 0, "Styles"); 

    QtQuick2ApplicationViewer viewer; 
    viewer.setMainQmlFile(QStringLiteral("qml/LightAssistant/main.qml")); 
    viewer.showExpanded(); 
    qDebug() << "offline path is " << viewer.engine()->offlineStoragePath(); 
    LA::OFFLINE_STORAGE_PATH = viewer.engine()->offlineStoragePath(); 
    return app.exec(); 
} 

단순화 클래스 (이 클래스 ~ 600 선입니다)

#ifndefLIGHTASSISTANT_H 
#define LIGHTASSISTANT_H 

#include <QtSql> 
#include <QtCore> 
#include <QtXml> 
#include <QtXmlPatterns> 
#include "globals.h" 



class LightAssistant : public QObject 
    { 
     Q_OBJECT 
    public: 
     explicit LightAssistant(QObject *parent = 0); 

     /** 
     * Is this running on Android? 
     */ 
     Q_INVOKABLE 
     int isAndroid() 
     { 
      #ifdef Q_OS_ANDROID 
      return 1; 
      #else 
      return 0; 
      #endif 
     } 

     /** 
     * Is this running on Android? 
     */ 
     Q_INVOKABLE 
     int isIOS() 
     { 
      #ifdef Q_OS_IOS 
      return 1; 
      #else 
      return 0; 
      #endif 
     } 

     /** 
     * @brief isMobile is this on a mobile device? 
     * @return 
     */ 
     Q_INVOKABLE 
     int isMobile() 
     { 
      return isIOS() + isAndroid(); 
     } 
     .... 
     } 
#endif // LIGHTASSISTANT_H 

은 이론적으로는 지금과 같이 QML이를 호출 할 수 있어요 배관공에 문제가 없는지 확인하기 위해 이와 같이 간단합니다. 그런 다음 작동하는 경우 메서드로 메서드를 이동하여 뭔가를 차단하는 다른 호출이 있는지 확인합니다. 전체 소스가없는 가장 좋은 추측은 아마 당신이 구성 요소를 등록하는 곳일 것입니다.

+0

응답 해 주셔서 감사합니다. 나는 네가 옳다고 생각하는데 틀린 것처럼 보이는 헤더 파일에'qmlRegisterType ...'을 넣는다. 하지만'appman'을 사용하고 있기 때문에 제 응용 프로그램에'main' 메서드가 없습니다. 내가 등록을 할 수있는 어떤 생각? – Aras

+1

@Aras 나는 결코 그것을 사용하지 않았으므로 appman에게는 패배입니다.QML 뷰어가 시작되기 전에로드 할 수 있는지를 확인하기 위해'initialize' 메소드를 시도 할 것입니다. 그러나 개발자에게 직접 연락하여 넥타이를 연결할 수 있는지 확인하지 않아도됩니다. 행운을 빌어 요! –

+1

나는 보통'* .cpp' 파일에서 등록을합니다 :'const int registration = qmlRegisterType <...>'. 이 변수는'main()'이 실행되기 전에 생성됩니다. 조금 위험하지만 아직 문제가 없습니다. – Velkan