Files
StandBatteryView/CanController.cpp
2025-08-15 18:53:38 +09:00

353 lines
8.9 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 <QDataStream>
#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", 500).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);
initializeBatteries();
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();
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, bool highPriority)
{
if (highPriority)
{
sendMessages_.prepend(message);
}
else
{
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::requestParameters()
{
for (auto address: registerAddresses())
{
CanId parametersId;
parametersId.address = address;
parametersId.type = 0x1;
CanMessage parametersMessage(parametersId.serialize());
parametersMessage.setLength(1);
parametersMessage.setByte(0, 24);
sendCanMessage(parametersMessage);
}
}
void CanController::setBatteryAction()
{
auto batteryController = qobject_cast<BatteryController*>(sender());
if (!batteryController)
{
return;
}
CanId actionId;
actionId.address = registerAddresses().at(batteryController->batteryIndex()) + 0x12;
actionId.type = 0x0;
CanMessage actionMessage(actionId.serialize());
actionMessage.setLength(1);
actionMessage.setByte(0, 1);
sendCanMessage(actionMessage, true);
}
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());
if (canId.destination != 0x1 || canId.source != 0x0)
{
qDebug() << "Ignoring message with destination" << canId.destination << "and source" << canId.source;
return;
}
auto batteryIndex = qsizetype((canId.address - 0x200) / 128);
if (batteryIndex < 0 || batteryIndex >= batteries_.size())
{
qDebug() << "Invalid battery index:" << batteryIndex << "for address" << canId.address;
return;
}
QHash<quint16, std::function<void(const CanMessage*, qsizetype)>> packageHandlers =
{
{0x0, std::bind(&CanController::handleControlParameters, this, std::placeholders::_1, std::placeholders::_2)},
{0x8, std::bind(&CanController::handleMeasuredParameters, this, std::placeholders::_1, std::placeholders::_2)},
{0x10, std::bind(&CanController::handleStatusParameters, this, std::placeholders::_1, std::placeholders::_2)}
};
auto offset = canId.address - quint16(0x200 + batteryIndex * 128);
qDebug() << "Address:" << canId.address << "Index:" << batteryIndex << "Offset:" << offset;
if (packageHandlers.contains(offset))
{
packageHandlers[offset](message, batteryIndex);
}
}
void CanController::handleControlParameters(const CanMessage* message, qsizetype batteryIndex)
{
QDataStream stream(message->getData());
stream.setByteOrder(QDataStream::LittleEndian);
quint16 capacity = 0;
stream >> capacity;
batteries_.at(batteryIndex)->setControlCapacity(doubleParameterToVariant(capacity));
quint16 temperature = 0;
stream >> temperature;
batteries_.at(batteryIndex)->setControlTemperature(intParameterToVariant(temperature));
qint16 current = 0;
stream >> current;
batteries_.at(batteryIndex)->setControlCurrent(intParameterToVariant(current));
quint16 voltage = 0;
stream >> voltage;
batteries_.at(batteryIndex)->setControlVoltage(doubleParameterToVariant(voltage));
}
void CanController::handleMeasuredParameters(const CanMessage* message, qsizetype batteryIndex)
{
QDataStream stream(message->getData());
stream.setByteOrder(QDataStream::LittleEndian);
quint16 capacity = 0;
stream >> capacity;
batteries_.at(batteryIndex)->setMeasuredCapacity(doubleParameterToVariant(capacity));
quint16 temperature = 0;
stream >> temperature;
batteries_.at(batteryIndex)->setMeasuredTemperature(intParameterToVariant(temperature));
qint16 current = 0;
stream >> current;
batteries_.at(batteryIndex)->setMeasuredCurrent(intParameterToVariant(current));
quint16 voltage = 0;
stream >> voltage;
batteries_.at(batteryIndex)->setMeasuredVoltage(doubleParameterToVariant(voltage));
}
void CanController::handleStatusParameters(const CanMessage* message, qsizetype batteryIndex)
{
QDataStream stream(message->getData());
stream.setByteOrder(QDataStream::LittleEndian);
quint8 chargeType = 0;
stream >> chargeType;
batteries_.at(batteryIndex)->setChargeType(chargeType);
quint8 workingMode = 0;
stream >> workingMode;
batteries_.at(batteryIndex)->setWorkingMode(workingMode);
stream.skipRawData(1);
quint32 status = 0;
stream >> status;
batteries_.at(batteryIndex)->addStatus(status);
}
void CanController::updateCanStatus()
{
auto isConnected = lastPackageDateTime_.msecsTo(QDateTime::currentDateTime()) < 2000;
if (isConnected_ != isConnected || isFirstCheck_)
{
isFirstCheck_ = false;
setProperty("isConnected", isConnected);
}
}
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;
}
void CanController::initializeBatteries()
{
for (int i = 0; i < 8; ++i)
{
auto batteryContoller = new BatteryController(this);
connect(batteryContoller, &BatteryController::actionButtonClicked, this, &CanController::setBatteryAction);
batteries_.append(batteryContoller);
emit batteriesChanged();
}
}
QList<quint16> CanController::registerAddresses() const
{
return {0x200, 0x280, 0x300, 0x380, 0x400, 0x480, 0x500, 0x580};
}