First implementation
This commit is contained in:
356
CanController.cpp
Normal file
356
CanController.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user