357 lines
9.1 KiB
C++
357 lines
9.1 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(parameterToVariant(capacity));
|
|
|
|
quint16 temperature = 0;
|
|
stream >> temperature;
|
|
batteries_.at(batteryIndex)->setControlTemperature(parameterToVariant(temperature));
|
|
|
|
quint16 current = 0;
|
|
stream >> current;
|
|
batteries_.at(batteryIndex)->setControlCurrent(parameterToVariant(current));
|
|
|
|
quint16 voltage = 0;
|
|
stream >> voltage;
|
|
batteries_.at(batteryIndex)->setControlVoltage(parameterToVariant(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(parameterToVariant(capacity));
|
|
|
|
quint16 temperature = 0;
|
|
stream >> temperature;
|
|
batteries_.at(batteryIndex)->setMeasuredTemperature(parameterToVariant(temperature));
|
|
|
|
quint16 current = 0;
|
|
stream >> current;
|
|
batteries_.at(batteryIndex)->setMeasuredCurrent(parameterToVariant(current));
|
|
|
|
quint16 voltage = 0;
|
|
stream >> voltage;
|
|
batteries_.at(batteryIndex)->setMeasuredVoltage(parameterToVariant(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);
|
|
|
|
quint16 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};
|
|
}
|
|
|
|
QVariant CanController::parameterToVariant(quint16 parameter) const
|
|
{
|
|
return parameter == 0xFFFF ? QVariant() : QVariant::fromValue(parameter / 10.0);
|
|
}
|