#include "CanController.h" #include #include #include #include #include #include #include #include #if defined(__linux__) #include #else #include #endif #include "CanStructs.h" #include #include #include #include #include #include #include #include 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(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> 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 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); }