C

Qt Cluster: Rendering and Recovery from Main UI Failure

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial

// This file is part of the Qt Safe Renderer module

#include "clusterdatabackend.h"
#include "crasher.h"
#include <QDebug>
#include <QtGlobal>
#include <QHostAddress>
#include <QNetworkInterface>
#ifdef QT_SERIALBUS_LIB
// #define DEBUG_CAN
#include <QtMath>
#include <QCanBus>
#define SOCKETCAN "socketcan"
#define CANINTERFACE "can0"
// J1939 PGNs
#define PGN_65268 0xFEF4 // Tire Condition - TIRE
#define PGN_64933 0xFDA5 // Door Control 2 - DC2
#define PGN_64972 0xFDCC // Light Control
#define PGN_61445 0xF005 // Electronic Transmission Controller 2 - ETC2
#define PGN_65265 0xFEF1 // Cruise Control/Vehicle Speed - CCVS
#define PGN_65267 0xFEF3 // vehicle position - VP
#define PGN_65256 0xFEE8 // vehicle direction/speed
#define PGN_65262 0xFEEE // Engine Temperature 1 - ET1
#define PGN_65263 0xFEEF // Engine Fluid Level/Pressure 1 - EFL/P1
#define PGN_65271 0xFEF7 // Vehicle Electrical Power - VEP
#define PGN_65276 0xFEFC // Dash Display - DD
#define PGN_61444 0xF004 // Electronic Engine Controller 1 - EEC1
#define PGN_60928 0xEE00 // Address Calimed - ACL
#endif
// door defines
#define OPEN_DOOR_1 0x400000000000000
#define OPEN_DOOR_2 0x1000000000000
#define OPEN_DOOR_3 0x40000000000000
#define OPEN_DOOR_4 0x100000000000
#define OPEN_DOOR_5 0x400000000
#define OPEN_DOOR_6 0x1000000
// light defines
#define HEAD_LIGHT 0x200000000000000
#define PARK_LIGHT 0x100000000000000
#define LIGHT_ERROR 0x400000000000000
#define LEFT_TURN_SIGNAL 0x10000000000000
#define RIGHT_TURN_SIGNAL 0x20000000000000
#define HAZARD_SIGNAL 0x4000000000000
#define LEFT_BLINK_ON 0x01
#define RIGHT_BLINK_ON 0x02
#define HAZARD_ON 0x4
// Current gear defines
#define GEAR_PARK 0xFB
#define GEAR_REVERSE 0x7C
#define GEAR_NEUTRAL 0x7D
// #define GEAR_1 0x7E
// #define GEAR_2 0x7F
// #define GEAR_3 0x80
// #define GEAR_4 0x81
// #define GEAR_5 0x82
// #define GEAR_6 0x83
ClusterDataBackend::ClusterDataBackend(QObject* parent) :
    QObject(parent),
#ifdef QT_SERIALBUS_LIB
    m_canDevice(Q_NULLPTR),
#endif
    m_vehicleSpeed(0.0),
    m_latitude(0.0),
    m_longitude(0.0),
    m_direction(0.0),
    m_flatTire(false),
    m_lightFailure(false),
    m_gear(1),
    m_signalLights(0x0),
    m_headLight(false),
    m_parkLight(false),
    m_carId(2),
    m_brake(false),
    m_engineTemp(60),
    m_oilTemp(0.0),
    m_oilPressure(0),
    m_batteryPotential(80.0),
    m_gasLevel(67.0),
    m_rpm(4000)
{
    m_doorOpen.insert("frontLeft", false);
    m_doorOpen.insert("frontRight", false);
    m_doorOpen.insert("rearLeft", false);
    m_doorOpen.insert("rearRight", false);
    m_doorOpen.insert("trunk", false);
    m_doorOpen.insert("hood", false);
#ifdef QT_SERIALBUS_LIB
    QCanBus* canBus(QCanBus::instance());
    const auto plugins(canBus->plugins());
    if (plugins.contains(SOCKETCAN)) {
        m_canDevice = canBus->createDevice(SOCKETCAN, CANINTERFACE);
        if (m_canDevice) {
            connect(m_canDevice, SIGNAL(errorOccurred(QCanBusDevice::CanBusError)), this, SLOT(canError(QCanBusDevice::CanBusError)));
            connect(m_canDevice, SIGNAL(framesReceived()), this, SLOT(canFramesReceived()));
#ifdef DEBUG_CAN
            connect(m_canDevice, SIGNAL(stateChanged(QCanBusDevice::CanBusDeviceState)), this, SLOT(canStateChanged(QCanBusDevice::CanBusDeviceState)));
            qDebug() << "ClusterDataBackend::ClusterDataBackend - connectDevice:" <<
#endif // DEBUG_CAN
                m_canDevice->connectDevice();
        } else {
            qWarning("Could not create QCanBus socketcan device 'can0'");
        }
    } else {
        qWarning("Could not find QCanBus plugin 'socketcan'");
    }
#endif // QT_SERIALBUS_LIB
}
ClusterDataBackend::~ClusterDataBackend()
{
#ifdef QT_SERIALBUS_LIB
    if (m_canDevice) {
        m_canDevice->disconnectDevice();
    }
#endif
    if (m_tcpServerConnection)
        m_tcpServerConnection->close();
    m_tcpServer.close();
}
QStringList ClusterDataBackend::availableZones() const
{
    return m_doorOpen.keys();
}
QMap<QString, QVariantMap> ClusterDataBackend::zoneMap() const
{
    return m_zonedFeatures;
}
void ClusterDataBackend::initializeAttributes()
{
    emit vehicleSpeedChanged(m_vehicleSpeed);
    emit latitudeChanged(m_latitude);
    emit longitudeChanged(m_longitude);
    emit directionChanged(m_direction);
    emit flatTireChanged(m_flatTire);
    foreach (QString zone, m_doorOpen.keys()) {
        emit doorOpenChanged(m_doorOpen.value(zone), zone);
    }
    emit lightFailureChanged(m_lightFailure);
    emit reverseChanged(m_gear < 0);
    emit leftTurnLightChanged(m_signalLights & LEFT_BLINK_ON);
    emit rightTurnLightChanged(m_signalLights & RIGHT_BLINK_ON);
    emit headLightChanged(m_headLight);
    emit carIdChanged(m_carId);
    emit crashChanged(m_crash);
    QByteArray simu = qgetenv("QT_CLUSTER_SIMU");
    QHostAddress simuAddr("127.0.0.1");
    if (!simu.isEmpty()) {
        if (!simuAddr.setAddress(QString(simu))) {
            qWarning("Failed to set simulator address! Falling back to localhost");
        }
        simuAddr.setAddress("127.0.0.1");
    } else {
        foreach (const QHostAddress &address, QNetworkInterface::allAddresses())
            if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress(QHostAddress::LocalHost))
                simuAddr.setAddress(address.toString());
    }
    if (m_tcpServer.listen(QHostAddress::Any, 32124)) {
        connect(&m_tcpServer, SIGNAL(newConnection()),
                this, SLOT(acceptConnection()));
        qDebug() << "ClusterData server listening " << simuAddr << m_tcpServer.serverPort();
    } else {
        qWarning() << m_tcpServer.errorString();
    }
}
void ClusterDataBackend::acceptConnection()
{
    m_tcpServerConnection = m_tcpServer.nextPendingConnection();
    connect(m_tcpServerConnection, &QTcpSocket::readyRead,
            this, &ClusterDataBackend::updateServerProgress);
}
void ClusterDataBackend::updateServerProgress()
{
    Crasher crasher;
    QString msg(m_tcpServerConnection->readAll());
    foreach (QString msgAttr, msg.split(" ",Qt::SkipEmptyParts)) {
        QString attribute = msgAttr.section(':',0,0);
        QString value = msgAttr.section(':',1,1);
        if (attribute == "vehicleSpeed") {
            emit vehicleSpeedChanged(value.toDouble());
        } else if (attribute == "latitude") {
            emit latitudeChanged(value.toDouble());
        } else if (attribute == "longitude") {
            emit longitudeChanged(value.toDouble());
        } else if (attribute == "direction") {
            emit directionChanged(value.toDouble());
        } else if (attribute == "headLight") {
            emit headLightChanged((bool)value.toInt());
        } else if (attribute == "lightFailure") {
            emit lightFailureChanged((bool)value.toInt());
        } else if (attribute == "flatTire") {
            emit flatTireChanged((bool)value.toInt());
        } else if (attribute == "reverse") {
            emit reverseChanged((bool)value.toInt());
        } else if (attribute == "leftTurnLight") {
            emit leftTurnLightChanged((bool)value.toInt());
        } else if (attribute == "rightTurnLight") {
            emit rightTurnLightChanged((bool)value.toInt());
        } else if (attribute == "headLight") {
            emit headLightChanged((bool)value.toInt());
        } else if (attribute == "parkLight") {
            emit parkLightChanged((bool)value.toInt());
        } else if (attribute == "carId") {
            emit carIdChanged(value.toInt());
        } else if (attribute == "frontLeftDoorOpen") {
            emit doorOpenChanged((bool)value.toInt(), "frontLeft");
        } else if (attribute == "frontRightDoorOpen") {
            emit doorOpenChanged((bool)value.toInt(), "frontRight");
        } else if (attribute == "rearLeftDoorOpen") {
            emit doorOpenChanged((bool)value.toInt(), "rearLeft");
        } else if (attribute == "rearRightDoorOpen") {
            emit doorOpenChanged((bool)value.toInt(), "rearRight");
        } else if (attribute == "hoodOpen") {
            emit doorOpenChanged((bool)value.toInt(), "hood");
        } else if (attribute == "trunkOpen") {
            emit doorOpenChanged((bool)value.toInt(), "trunk");
        } else if (attribute == "hazardSignal") {
            // TODO: Should replace this hack with actual hazardSignal
            emit leftTurnLightChanged((bool)value.toInt());
            emit rightTurnLightChanged((bool)value.toInt());
        } else if (attribute == "brake") {
            emit brakeChanged((bool)value.toInt());
        } else if (attribute == "engineTemp") {
            emit engineTempChanged(value.toInt());
        } else if (attribute == "oilTemp") {
            emit oilTempChanged(value.toDouble());
        } else if (attribute == "oilPressure") {
            emit oilPressureChanged(value.toInt());
        } else if (attribute == "batteryPotential") {
            emit batteryPotentialChanged(value.toDouble());
        } else if (attribute == "gasLevel") {
            emit gasLevelChanged(value.toDouble());
        } else if (attribute == "rpm") {
            emit rpmChanged(value.toInt());
        } else if (attribute == "gear") {
            emit gearChanged(value.toInt());
        } else if (attribute == "crash") {
            emit crashChanged((bool)value.toInt());
        }
        else {
            qWarning() << "Unknown msg" << msg;
        }
    }
}
#ifdef QT_SERIALBUS_LIB
// Tire Condition - TIRE
void ClusterDataBackend::handlePGN65268(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        //        char tireId(data.at(0));
        //        char pressure(data.at(1));
        //        quint16 temperatureData(data.at(2) << 8);
        //        temperatureData |= data.at(3);
        //        double tireTemperature((0.03125 * (double)temperatureData) - 273);
        m_flatTire = (data.at(4) & 0x30);
#ifdef DEBUG_CAN
        qDebug("ClusterDataBackend::handlePGN65268 - m_flatTire: %s", (m_flatTire ? "true" : "false"));
#endif // DEBUG_CAN
        emit flatTireChanged(m_flatTire);
    }
}
// Door Control 2 - DC2
void ClusterDataBackend::handlePGN64933(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        quint64 doorData((quint64)data.at(0) << 56);
        doorData |= ((quint64)data.at(1) << 48);
        doorData |= ((quint64)data.at(2) << 40);
        doorData |= ((quint64)data.at(3) << 32);
        doorData |= ((quint64)data.at(4) << 24);
        doorData |= ((quint64)data.at(5) << 16);
        doorData |= ((quint64)data.at(6) << 8);
        doorData |= (quint64)data.at(7);
        bool frontLeft(doorData & OPEN_DOOR_1);
        bool frontRight(doorData & OPEN_DOOR_2);
        bool rearLeft(doorData & OPEN_DOOR_3);
        bool rearRight(doorData & OPEN_DOOR_4);
        bool trunk(doorData & OPEN_DOOR_5);
        bool hood(doorData & OPEN_DOOR_6);
        if (m_doorOpen.value("frontLeft") != frontLeft) {
            m_doorOpen.insert("frontLeft", frontLeft);
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN64933 - doorOpenChanged, frontLeft: %s", (frontLeft ? "true" : "false"));
#endif // DEBUG_CAN
            emit doorOpenChanged(frontLeft, "frontLeft");
        }
        if (m_doorOpen.value("frontRight") != frontRight) {
            m_doorOpen.insert("frontRight", frontRight);
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN64933 - doorOpenChanged, frontRight: %s", (frontRight ? "true" : "false"));
#endif // DEBUG_CAN
            emit doorOpenChanged(frontRight, "frontRight");
        }
        if (m_doorOpen.value("rearLeft") != rearLeft) {
            m_doorOpen.insert("rearLeft", rearLeft);
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN64933 - doorOpenChanged, rearLeft: %s", (rearLeft ? "true" : "false"));
#endif // DEBUG_CAN
            emit doorOpenChanged(rearLeft, "rearLeft");
        }
        if (m_doorOpen.value("rearRight") != rearRight) {
            m_doorOpen.insert("rearRight", rearRight);
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN64933 - doorOpenChanged, rearRight: %s", (rearRight ? "true" : "false"));
#endif // DEBUG_CAN
            emit doorOpenChanged(rearRight, "rearRight");
        }
        if (m_doorOpen.value("trunk") != trunk) {
            m_doorOpen.insert("trunk", trunk);
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN64933 - doorOpenChanged, trunk: %s", (trunk ? "true" : "false"));
#endif // DEBUG_CAN
            emit doorOpenChanged(trunk, "trunk");
        }
        if (m_doorOpen.value("hood") != hood) {
            m_doorOpen.insert("hood", hood);
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN64933 - doorOpenChanged, hood: %s", (hood ? "true" : "false"));
#endif // DEBUG_CAN
            emit doorOpenChanged(hood, "hood");
        }
    }
}
// Light Control
void ClusterDataBackend::handlePGN64972(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        quint64 lightData((quint64)data.at(0) << 56);
        lightData |= ((quint64)data.at(1) << 48);
        lightData |= ((quint64)data.at(2) << 40);
        lightData |= ((quint64)data.at(3) << 32);
        lightData |= ((quint64)data.at(4) << 24);
        lightData |= ((quint64)data.at(5) << 16);
        lightData |= ((quint64)data.at(6) << 8);
        lightData |= data.at(7);
        if (m_headLight != (lightData & HEAD_LIGHT)) {
            m_headLight = (lightData & HEAD_LIGHT);
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN64972 - m_headLight: %s", (m_headLight ? "true" : "false"));
#endif // DEBUG_CAN
            emit headLightChanged(m_headLight);
        }
        if (m_parkLight != (lightData & PARK_LIGHT)) {
            m_parkLight = (lightData & PARK_LIGHT);
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN64972 - m_parkLight: %s", (m_parkLight ? "true" : "false"));
#endif // DEBUG_CAN
            emit parkLightChanged(m_parkLight);
        }
        if (m_lightFailure != (lightData & LIGHT_ERROR)) {
            m_lightFailure = (lightData & LIGHT_ERROR);
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN64972 - m_lightFailure: %s", (m_lightFailure ? "true" : "false"));
#endif // DEBUG_CAN
            emit lightFailureChanged(m_lightFailure);
        }
        if ((bool)(m_signalLights & LEFT_BLINK_ON) != (bool)(lightData & LEFT_TURN_SIGNAL)) {
            if (lightData & LEFT_TURN_SIGNAL) {
                m_signalLights |= LEFT_BLINK_ON;
            }
            else {
                m_signalLights &= ~LEFT_BLINK_ON;
            }
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN64972 - leftTurnLight: %s", ((lightData & LEFT_TURN_SIGNAL) ? "true" : "false"));
#endif // DEBUG_CAN
            emit leftTurnLightChanged(lightData & LEFT_TURN_SIGNAL);
        }
        if ((bool)(m_signalLights & RIGHT_BLINK_ON) != (bool)(lightData & RIGHT_TURN_SIGNAL)) {
            if (lightData & RIGHT_TURN_SIGNAL) {
                m_signalLights |= RIGHT_BLINK_ON;
            }
            else {
                m_signalLights &= ~RIGHT_BLINK_ON;
            }
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN64972 - rightTurnLight: %s", ((lightData & RIGHT_TURN_SIGNAL) ? "true" : "false"));
#endif // DEBUG_CAN
            emit rightTurnLightChanged(lightData & RIGHT_TURN_SIGNAL);
        }
        if ((bool)(m_signalLights & HAZARD_ON) != (bool)(lightData & HAZARD_SIGNAL)) {
            if (lightData & HAZARD_SIGNAL) {
                m_signalLights |= HAZARD_ON;
                if (!(m_signalLights & LEFT_BLINK_ON)) {
#ifdef DEBUG_CAN
                    qDebug("ClusterDataBackend::handlePGN64972 - hazard ON, leftTurnLight: true");
#endif // DEBUG_CAN
                    emit leftTurnLightChanged(true);
                }
                if (!(m_signalLights & RIGHT_BLINK_ON)) {
#ifdef DEBUG_CAN
                    qDebug("ClusterDataBackend::handlePGN64972 - hazard ON, rightTurnLight: true");
#endif // DEBUG_CAN
                    emit rightTurnLightChanged(true);
                }
            }
            else {
                m_signalLights &= ~HAZARD_ON;
                if (!(m_signalLights & LEFT_BLINK_ON)) {
#ifdef DEBUG_CAN
                    qDebug("ClusterDataBackend::handlePGN64972 - hazard OFF, leftTurnLight: false");
#endif // DEBUG_CAN
                    emit leftTurnLightChanged(false);
                }
                if (!(m_signalLights & RIGHT_BLINK_ON)) {
#ifdef DEBUG_CAN
                    qDebug("ClusterDataBackend::handlePGN64972 - hazard OFF, rightTurnLight: false");
#endif // DEBUG_CAN
                    emit rightTurnLightChanged(false);
                }
            }
        }
    }
}
// Electronic Transmission Controller 2 - ETC2
void ClusterDataBackend::handlePGN61445(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        char gear(data.at(3));
        if (gear == (char)GEAR_PARK) {
            m_gear = 0xff;
        }
        else if (m_gear == -1 && gear != (char)GEAR_REVERSE) {
            m_gear = (gear - GEAR_NEUTRAL);
            emit reverseChanged(false);
        }
        else if (m_gear != -1 && gear == (char)GEAR_REVERSE) {
            m_gear = -1;
            emit reverseChanged(true);
        }
        else {
            m_gear = (gear - GEAR_NEUTRAL);
        }
#ifdef DEBUG_CAN
        qDebug("ClusterDataBackend::handlePGN61445 - gear: %d", m_gear);
#endif // DEBUG_CAN
        emit gearChanged(m_gear);
    }
}
// Cruise Control/Vehicle Speed - CCVS
void ClusterDataBackend::handlePGN65265(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        qint16 speed((data.at(1) << 8));
        speed |= data.at(2);
        double vehicleSpeed(0.00390625 * (double)speed);
        if (m_vehicleSpeed != vehicleSpeed) {
            m_vehicleSpeed = vehicleSpeed;
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN65265 - vehicle speed: %f", m_vehicleSpeed);
#endif // DEBUG_CAN
            emit vehicleSpeedChanged(m_vehicleSpeed);
        }
        bool brakeOn(data.at(3) & 0x10);
        if (m_brake != brakeOn) {
            m_brake = brakeOn;
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN65265 - m_brake: %s", (m_brake ? "true" : "false"));
#endif // DEBUG_CAN
            emit brakeChanged(m_brake);
        }
    }
}
// vehicle position - VP
void ClusterDataBackend::handlePGN65267(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        quint32 lat((data.at(0) << 24));
        lat |= ((data.at(1) << 16));
        lat |= ((data.at(2) << 8));
        lat |= data.at(3);
        quint32 lon(data.at(4) << 24);
        lon |= (data.at(5) << 16);
        lon |= (data.at(6) << 8);
        lon |= data.at(7);
        double latitude((((double)lat) * 1e-7) - 210);
        double longitude((((double)lon) * 1e-7) - 210);
        bool positionChanged(false);
        if (m_latitude != latitude) {
            m_latitude = latitude;
            positionChanged = true;
        }
        if (m_longitude != longitude) {
            m_longitude = longitude;
            positionChanged = true;
        }
        if (positionChanged) {
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN129025 - lat: %f, lon: %f", m_latitude, m_longitude);
#endif // DEBUG_CAN
            emit latitudeChanged(m_latitude);
            emit longitudeChanged(m_longitude);
        }
    }
}
// vehicle direction/speed
void ClusterDataBackend::handlePGN65256(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        quint16 directionData((data.at(0) << 8));
        directionData |= data.at(1);
        double direction(0.0078125 * (double)directionData); // 1/128 deg/bit
        if (m_direction != direction) {
            m_direction = direction;
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN65256 - m_direction: %f", m_direction);
#endif // DEBUG_CAN
            emit directionChanged(m_direction);
        }
    }
}
// Engine Temperature 1 - ET1
void ClusterDataBackend::handlePGN65262(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        qint8 engineTemp(data.at(0) - 40);
        if (m_engineTemp != engineTemp) {
            m_engineTemp = engineTemp;
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN65262 - m_engineTemp: %d", m_engineTemp);
#endif // DEBUG_CAN
            emit engineTempChanged(m_engineTemp);
        }
        quint16 oilTempData(data.at(2) << 8);
        oilTempData |= data.at(3);
        double oilTemp(0.03125 * ((double)oilTempData) - 273.0);
        if (m_oilTemp != oilTemp) {
            m_oilTemp = oilTemp;
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN65262 - m_oilTemp: %f", m_oilTemp);
#endif // DEBUG_CAN
            emit oilTempChanged(m_oilTemp);
        }
    }
}
// Engine Fluid Level/Pressure 1 - EFL/P1
void ClusterDataBackend::handlePGN65263(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        int oilPressure(4 * data.at(3));
        if (m_oilPressure != oilPressure) {
            m_oilPressure = oilPressure;
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN65263 - m_oilPressure: %d", m_oilPressure);
#endif // DEBUG_CAN
            emit oilPressureChanged(m_oilPressure);
        }
    }
}
// Vehicle Electrical Power - VEP
void ClusterDataBackend::handlePGN65271(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        quint16 batteryPotentialData(data.at(6) << 8);
        batteryPotentialData |= data.at(7);
        double batteryPotential(0.05 * (double)batteryPotentialData);
        if (m_batteryPotential != batteryPotential) {
            m_batteryPotential = batteryPotential;
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN65271 - m_batteryPotential: %f", m_batteryPotential);
#endif // DEBUG_CAN
            emit batteryPotentialChanged(m_batteryPotential);
        }
    }
}
// Dash Display - DD
void ClusterDataBackend::handlePGN65276(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        double gasLevel(0.4 * (double)data.at(1));
        if (m_gasLevel != gasLevel) {
            m_gasLevel = gasLevel;
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN65276 - m_gasLevel: %f", m_gasLevel);
#endif // DEBUG_CAN
            emit gasLevelChanged(m_gasLevel);
        }
    }
}
// Electronic Engine Controller 1 - EEC1
void ClusterDataBackend::handlePGN61444(const QCanBusFrame& frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        quint16 rpmData(data.at(3) << 8);
        rpmData |= data.at(4);
        int rpm(0.125 * (double)rpmData);
        if (m_rpm != rpm) {
            m_rpm = rpm;
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN61444 - m_rpm: %d", m_rpm);
#endif // DEBUG_CAN
            emit rpmChanged(m_rpm);
        }
    }
}
// Address Claimed - ACL
void ClusterDataBackend::handlePGN60928(const QCanBusFrame &frame)
{
    QByteArray data(frame.payload());
    if (data.size() == 8) {
        // only 21 bits allowed (0x0 - 0x1FFFFF), pos 1.1 - 3.5 in message
        quint16 carIdData((data.at(0) & 0xff) << 13);
        carIdData |= ((data.at(1) & 0xff) << 5);
        carIdData |= ((data.at(2) & 0xf8) >> 3);
        if (m_carId != (int)carIdData) {
            m_carId = carIdData;
#ifdef DEBUG_CAN
            qDebug("ClusterDataBackend::handlePGN60928 - carId: %d", m_carId);
#endif // DEBUG_CAN
            emit carIdChanged(m_carId);
        }
    }
}
void ClusterDataBackend::canError(QCanBusDevice::CanBusError error)
{
    if (m_canDevice) {
        qWarning("ClusterDataBackend::canError - code: %d, error: %s", (int)error, qPrintable(m_canDevice->errorString()));
        m_canDevice->disconnectDevice();
    }
}
void ClusterDataBackend::canFramesReceived()
{
    while (m_canDevice && m_canDevice->framesAvailable() > 0) {
        QCanBusFrame frame(m_canDevice->readFrame());
        quint32 frameId(frame.frameId());
        switch (frameId)  {
        case PGN_65268 : handlePGN65268(frame); break; // Tire Condition - TIRE
        case PGN_64933 : handlePGN64933(frame); break; // Door Control 2 - DC2
        case PGN_64972 : handlePGN64972(frame); break; // Light Control
        case PGN_61445 : handlePGN61445(frame); break; // Electronic Transmission Controller 2 - ETC2
        case PGN_65265 : handlePGN65265(frame); break; // Cruise Control/Vehicle Speed - CCVS
        case PGN_65267 : handlePGN65267(frame); break; // vehicle position - VP
        case PGN_65256 : handlePGN65256(frame); break; // vehicle direction/speed
        case PGN_65262 : handlePGN65262(frame); break; // Engine Temperature 1 - ET1
        case PGN_65263 : handlePGN65263(frame); break; // Engine Fluid Level/Pressure 1 - EFL/P1
        case PGN_65271 : handlePGN65271(frame); break; // Vehicle Electrical Power - VEP
        case PGN_65276 : handlePGN65276(frame); break; // Dash Display - DD
        case PGN_61444 : handlePGN61444(frame); break; // Electronic Engine Controller 1 - EEC1
        case PGN_60928 : handlePGN60928(frame); break; // Address Claimed - ACL
        default: qWarning("canFramesReceived - unknown frame id: %d", frameId);
        }
#ifdef DEBUG_CAN
        QByteArray data(frame.payload());
        const char* constData(data.constData());
        int payLoadSize(data.size());
        QString dataString;
        for (int i(0); i < payLoadSize; i++) {
            if (!dataString.isEmpty()) dataString.append(".");
            dataString.append(QString::fromLatin1("%1").arg((int)constData[i], 2, 16, QChar('0')));
        }
        qDebug() << "ClusterDataBackend::canFramesReceived - id:" << frameId << "- data:" << dataString.toLatin1().constData();
#endif // DEBUG_CAN
    }
}
void ClusterDataBackend::canStateChanged(QCanBusDevice::CanBusDeviceState state)
{
#ifdef DEBUG_CAN
    qDebug() << "ClusterDataBackend::canStateChanged - state:" << state;
#else
    Q_UNUSED(state)
#endif // DEBUG_CAN
}
#endif