Implemented first version of Chm30Utility

This commit is contained in:
Yury Shuvakin
2025-03-17 19:12:22 +09:00
commit 6be3459a37
83 changed files with 10515 additions and 0 deletions

544
CanController.cpp Normal file
View File

@@ -0,0 +1,544 @@
#include "CanController.h"
#include <core/MeasurementSetup.h>
#include <core/MeasurementNetwork.h>
#include <core/MeasurementInterface.h>
#include <core/CanTrace.h>
#include <core/Log.h>
#include <driver/CanInterface.h>
#include <driver/SLCANDriver/SLCANDriver.h>
#include <driver/CANBlastDriver/CANBlasterDriver.h>
#if defined(__linux__)
#include <driver/SocketCanDriver/SocketCanDriver.h>
#else
#include <driver/CandleApiDriver/CandleApiDriver.h>
#endif
#include "CanStructs.h"
#include <QDateTime>
#include <QDebug>
#include <QTimer>
#include <QSettings>
#include <QRandomGenerator>
#include <bitset>
#include <functional>
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<quint8, std::function<void(const CanMessage*)>> 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<char>(message->getByte(0));
serialNumber += static_cast<char>(message->getByte(1));
serialNumber += QString::number(message->getByte(2));
serialNumber += static_cast<char>(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<quint32, QString> 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;
}