#include "CanController.h" #include #include #include #include #include #include #include #include #if defined(__linux__) #include #else #include #endif #include "CanStructs.h" #include #include #include #include #include #include #include CanController::CanController(QObject* parent) : QObject{parent} , lastPackageDateTime_(QDateTime::fromMSecsSinceEpoch(0)) { setupDrivers(); QSettings settings("settings.ini", QSettings::IniFormat); auto& backend = Backend::instance(); connect(backend.getTrace(), &CanTrace::messageEnqueued, this, &CanController::handlePackage); const auto requestParametersTimer = new QTimer(this); connect(requestParametersTimer, &QTimer::timeout, this, &CanController::requestParameters); const auto requestParametersTimeout = settings.value("requestParametersTimeout", 1000).toInt(); requestParametersTimer->start(requestParametersTimeout); const auto updateCanStatusTimer = new QTimer(this); connect(updateCanStatusTimer, &QTimer::timeout, this, &CanController::updateCanStatus); updateCanStatusTimer->start(2000); const auto reconnectTimer = new QTimer(this); connect(reconnectTimer, &QTimer::timeout, this, &CanController::tryConnectCan); reconnectTimer->start(5000); const auto sendMessagesTimer = new QTimer(this); connect(sendMessagesTimer, &QTimer::timeout, this, &CanController::sendMessages); const auto sendMessagesTimeout = settings.value("sendMessagesTimeout", 30).toInt(); sendMessagesTimer->start(sendMessagesTimeout); // auto statusTimer = new QTimer(this); // statusTimer->start(1000); // statusTimer->callOnTimeout([this] // { // showStatus(QRandomGenerator::global()->generate()); // }); // for (int i = 0; i < 15; ++i) // { // showStatus(QRandomGenerator::global()->generate()); // } QTimer::singleShot(0, this, &CanController::connectCan); } CanController::~CanController() { } QObject* CanController::qmlInstance(QQmlEngine* /*engine*/, QJSEngine* /*scriptEngine*/) { return new CanController; } void CanController::connectCan() { auto& backend = Backend::instance(); backend.clearTrace(); QSettings settings("settings.ini", QSettings::IniFormat); auto bitrate = settings.value("bitrate", 125000).toInt(); for (auto* network: backend.getSetup().getNetworks()) { for (auto* mi: network->interfaces()) { mi->setBitrate(bitrate); } } isFirstCheck_ = true; backend.startMeasurement(); saveConfiguration(); requestNumberOfModules(); QTimer::singleShot(1000, this, [this]{ updateCanStatus(); }); } void CanController::disconnectCan() { Backend::instance().stopMeasurement(); isConnected_ = false; } void CanController::tryConnectCan() { if (!isConnected_) { disconnectCan(); connectCan(); } } void CanController::sendCanMessage(const CanMessage& message) { sendMessages_.push_back(message); } void CanController::sendMessages() { if (sendMessages_.isEmpty()) { return; } const auto& message = sendMessages_.takeFirst(); auto& backend = Backend::instance(); bool isSent = false; for (const auto interfaceId: backend.getInterfaceList()) { auto interface = backend.getInterfaceById(interfaceId); if (interface) { isSent = true; interface->sendMessage(message); } } if (isSent) { qDebug() << "Sent:" << message.getIdString() << message.getDataHexString(); } } void CanController::requestNumberOfModules() { CanId numberId; numberId.command = 0x2; numberId.destination = 0x3F; sendCanMessage({numberId.serialize()}); } void CanController::requestParameters() { CanId serialId; serialId.command = 0xB; serialId.destination = 0x0; sendCanMessage({serialId.serialize()}); CanId characteristicId; characteristicId.command = 0xA; characteristicId.destination = currentModule_; sendCanMessage({characteristicId.serialize()}); CanId modeId; modeId.command = 0xD; modeId.destination = currentModule_; sendCanMessage({modeId.serialize()}); CanId limitationsId; limitationsId.command = 0xC; limitationsId.destination = currentModule_; sendCanMessage({limitationsId.serialize()}); CanId inputId; inputId.command = 0x6; inputId.destination = currentModule_; sendCanMessage({inputId.serialize()}); CanId outputId; outputId.command = 0x9; outputId.destination = currentModule_; sendCanMessage({outputId.serialize()}); CanId statusId; statusId.command = 0x4; statusId.destination = currentModule_; sendCanMessage({statusId.serialize()}); } void CanController::setModuleOnOff(bool isOn) { CanId moduleId; moduleId.command = 0x1A; moduleId.destination = currentModule_; CanMessage moduleMessage(moduleId.serialize()); moduleMessage.setByte(0, (uint8_t)!isOn); sendCanMessage(moduleMessage); } void CanController::setLedOnOff(bool isOn) { CanId ledId; ledId.command = 0x14; ledId.destination = currentModule_; CanMessage ledMessage(ledId.serialize()); ledMessage.setByte(0, (uint8_t)isOn); sendCanMessage(ledMessage); } void CanController::setHighLowMode(bool isHigh) { CanId modeId; modeId.command = 0x1D; modeId.destination = currentModule_; CanMessage modeMessage(modeId.serialize()); modeMessage.setByte(0, (uint8_t)!isHigh); sendCanMessage(modeMessage); } void CanController::setOutputParameters() { CanId outputId; outputId.command = 0x1C; outputId.destination = currentModule_; CanMessage outputMessage(outputId.serialize()); uint32_t outputVoltage = setupOutputVoltage_ * 1000; outputMessage.setByte(0, (outputVoltage >> 24)); outputMessage.setByte(1, (outputVoltage >> 16)); outputMessage.setByte(2, (outputVoltage >> 8)); outputMessage.setByte(3, outputVoltage); uint32_t outputCurrent = setupOutputCurrent_ * 1000; outputMessage.setByte(4, (outputCurrent >> 24)); outputMessage.setByte(5, (outputCurrent >> 16)); outputMessage.setByte(6, (outputCurrent >> 8)); outputMessage.setByte(7, outputCurrent); sendCanMessage(outputMessage); } void CanController::handlePackage(int index) { lastPackageDateTime_ = QDateTime::currentDateTime(); if (isFirstCheck_) { updateCanStatus(); } auto& backend = Backend::instance(); auto message = backend.getTrace()->getMessage(index); if (!message) { return; } qDebug() << "Received:" << index << message->getIdString() << message->getDataHexString(); CanId canId; canId.deserialize(message->getId()); QHash> packageHandlers = { {0x2, std::bind(&CanController::handleNumberOfModules, this, std::placeholders::_1)}, {0xB, std::bind(&CanController::handleSerialNumber, this, std::placeholders::_1)}, {0xA, std::bind(&CanController::handleCharacteristics, this, std::placeholders::_1)}, {0xD, std::bind(&CanController::handleMode, this, std::placeholders::_1)}, {0xC, std::bind(&CanController::handleLimitations, this, std::placeholders::_1)}, {0x6, std::bind(&CanController::handleInputParameters, this, std::placeholders::_1)}, {0x9, std::bind(&CanController::handleOutputParameters, this, std::placeholders::_1)}, {0x4, std::bind(&CanController::handleStatus, this, std::placeholders::_1)} }; auto command = canId.command; if (packageHandlers.contains(command)) { packageHandlers[command](message); } } void CanController::handleNumberOfModules(const CanMessage* message) { setProperty("numberOfModules", message->getByte(2)); setProperty("currentModule", 0); } void CanController::handleSerialNumber(const CanMessage* message) { QString serialNumber; serialNumber += static_cast(message->getByte(0)); serialNumber += static_cast(message->getByte(1)); serialNumber += QString::number(message->getByte(2)); serialNumber += static_cast(message->getByte(3)); serialNumber += QString::number((message->getByte(4) << 24) + (message->getByte(5) << 16) + (message->getByte(6) << 8) + (message->getByte(7))); setProperty("serialNumber", serialNumber); } void CanController::handleCharacteristics(const CanMessage* message) { quint16 maxVoltage = (message->getByte(0) << 8) + message->getByte(1); quint16 minVoltage = (message->getByte(2) << 8) + message->getByte(3); double maxCurrent = ((message->getByte(4) << 8) + message->getByte(5)) / 10.0; double power = ((message->getByte(6) << 8) + message->getByte(7)) / 100.0; setProperty("characteristics", QString(tr("%1V, %2V, %3A, %4KW")).arg(minVoltage).arg(maxVoltage).arg(maxCurrent).arg(power)); } void CanController::handleMode(const CanMessage* message) { const bool isLowMode = message->getByte(0); setProperty("mode", isLowMode ? tr("Low") : tr("High")); // setProperty("highLowMode", !isLowMode); } void CanController::handleLimitations(const CanMessage* message) { double maxVoltage = ((message->getByte(0) << 8) + message->getByte(1)) / 10.0; double maxCurrent = ((message->getByte(2) << 8) + message->getByte(3)) / 10.0; setProperty("limitations", QString(tr("%1V, %2A")).arg(maxVoltage).arg(maxCurrent)); } void CanController::handleInputParameters(const CanMessage* message) { double abVoltage = ((message->getByte(0) << 8) + message->getByte(1)) / 10.0; double bcVoltage = ((message->getByte(2) << 8) + message->getByte(3)) / 10.0; double caVoltage = ((message->getByte(4) << 8) + message->getByte(5)) / 10.0; setProperty("inputVoltage", QString(tr("AB: %1V, BC: %2V, CA: %3V")).arg(abVoltage).arg(bcVoltage).arg(caVoltage)); } void CanController::handleOutputParameters(const CanMessage* message) { double outputVoltage = ((message->getByte(0) << 24) + (message->getByte(1) << 16) + (message->getByte(2) << 8) + message->getByte(3)) / 1000.0; double outputCurrent = ((message->getByte(4) << 24) + (message->getByte(5) << 16) + (message->getByte(6) << 8) + message->getByte(7)) / 1000.0; setProperty("outputVoltage", outputVoltage); setProperty("outputCurrent", outputCurrent); } void CanController::handleStatus(const CanMessage* message) { qint8 temperature = message->getByte(4); setProperty("moduleTemperature", QString::number(temperature)); quint32 status = (message->getByte(5) << 16) + (message->getByte(6) << 8) + message->getByte(7); showStatus(status); } void CanController::showStatus(quint32 status) { if (status == 0) { return; } QString statusDescription; auto appendStatus = [&statusDescription](const QString& subStatus) { if (!statusDescription.isEmpty()) { statusDescription += ", "; } statusDescription += subStatus; }; { std::bitset<8> bitsetTable2(quint8(status >> 16)); if (bitsetTable2.test(7)) { appendStatus(tr("The module PFC side is shut off state")); } if (bitsetTable2.test(6)) { appendStatus(tr("Enter the overpressure alarm")); } if (bitsetTable2.test(5)) { appendStatus(tr("Enter the undervoltage alarm")); } if (bitsetTable2.test(4)) { appendStatus(tr("Three-phase input imbalance alarm")); } if (bitsetTable2.test(3)) { appendStatus(tr("Three-phase input is missing phase alarm")); } if (bitsetTable2.test(2)) { appendStatus(tr("Serious uneven flow")); } if (bitsetTable2.test(1)) { appendStatus(tr("The module address is repeated")); } if (bitsetTable2.test(0)) { appendStatus(tr("The module is in the power limit state")); } } { std::bitset<8> bitsetTable1(quint8(status >> 8)); if (bitsetTable1.test(7)) { appendStatus(tr("CAN communication interruption alarm")); } if (bitsetTable1.test(6)) { appendStatus(tr("Output undervoltage alarm")); } if (bitsetTable1.test(5)) { appendStatus(tr("Output overpressure alarm")); } if (bitsetTable1.test(4)) { appendStatus(tr("Came to the police")); // WTF?????? } if (bitsetTable1.test(3)) { appendStatus(tr("Fan fault alarm")); } if (bitsetTable1.test(2)) { appendStatus(tr("Output overflow alarm")); } if (bitsetTable1.test(1)) { appendStatus(tr("Module fault protection (comprehensive)")); } if (bitsetTable1.test(0)) { appendStatus(tr("Module DC side is shut off state")); } } { std::bitset<8> bitsetTable0(quint8(status >> 0)); if (bitsetTable0.test(5)) { appendStatus(tr("Abnormal (discharge fault)")); } if (bitsetTable0.test(4)) { appendStatus(tr("Lock up the protection")); } if (bitsetTable0.test(3)) { appendStatus(tr("Input or bus line is abnormal")); } if (bitsetTable0.test(2)) { appendStatus(tr("The module internal communication failure")); } if (bitsetTable0.test(1)) { appendStatus(tr("Uneven flow alarm")); } if (bitsetTable0.test(0)) { appendStatus(tr("Output short circuit")); } } if (statusDescription.isEmpty()) { QMap commonStatusMap = { {0x1000000, tr("Can connected")}, {0x2000000, tr("Can disconnected")}, }; statusDescription = commonStatusMap.value(status, tr("Unknown status")); } QVariantMap statusMap; statusMap.insert("time", QDateTime::currentDateTime().toString("hh:mm:ss dd-MM-yyyy")); statusMap.insert("status", "0x" + QString::number(status, 16).toUpper().rightJustified(2, '0')); statusMap.insert("description", statusDescription); if (statuses_.isEmpty() || statuses_.first().toMap()["status"] != statusMap["status"]) { qDebug() << "Status:" << statusMap["status"].toString() << statusMap["description"].toString(); statuses_.prepend(statusMap); emit statusesChanged(); } } void CanController::updateCanStatus() { auto isConnected = lastPackageDateTime_.msecsTo(QDateTime::currentDateTime()) < 2000; if (isConnected_ != isConnected || isFirstCheck_) { isFirstCheck_ = false; setProperty("isConnected", isConnected); showStatus(isConnected ? 0x1000000 : 0x2000000); } } void CanController::setupDrivers() { auto& backend = Backend::instance(); #if defined(__linux__) backend.addCanDriver(*(new SocketCanDriver(backend))); #else backend.addCanDriver(*(new CandleApiDriver(backend))); #endif backend.addCanDriver(*(new SLCANDriver(backend))); // backend.addCanDriver(*(new CANBlasterDriver(backend))); backend.setDefaultSetup(); } void CanController::saveConfiguration() { auto& backend = Backend::instance(); QDomDocument doc; auto root = doc.createElement("configuration"); backend.getSetup().saveXML(backend, doc, root); doc.appendChild(root); const auto configuration = doc.toString(); qDebug() << "Configuration" << configuration; }