545 lines
16 KiB
C++
545 lines
16 KiB
C++
#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;
|
|
}
|