Files
TkrMonitor/MainWindow.cpp
2024-08-06 17:11:59 +09:00

371 lines
11 KiB
C++

#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "rt8016AdcDgr.h"
#include <core/MeasurementSetup.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 <window/SetupDialog/SetupDialog.h>
#include <QTimer>
#include <QDebug>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, dateTimeTimer_(new QTimer(this))
, canStatusTimer_(new QTimer(this))
, lastPackageDateTime_(QDateTime::fromMSecsSinceEpoch(0))
{
ui->setupUi(this);
setupDrivers();
setupTables();
auto& backend = Backend::instance();
connect(backend.getTrace(), &CanTrace::messageEnqueued, this, &MainWindow::handlePackage);
dateTimeTimer_->start(1000);
connect(dateTimeTimer_, &QTimer::timeout, this, &MainWindow::updateDateTime);
canStatusTimer_->start(1000);
connect(canStatusTimer_, &QTimer::timeout, this, &MainWindow::updateCanStatus);
updateDateTime();
connectCan();
}
MainWindow::~MainWindow()
{
disconnectCan();
delete ui;
}
void MainWindow::connectCan()
{
auto& backend = Backend::instance();
backend.clearTrace();
backend.startMeasurement();
QTimer::singleShot(100, this, [this]{ updateCanStatus(); });
// for (const auto interfaceId: backend.getInterfaceList())
// {
// log_info(backend.getInterfaceById(interfaceId)->getName());
// }
// MeasurementSetup new_setup(&backend);
// new_setup.cloneFrom(backend.getSetup());
// auto setupDlg = new SetupDialog(Backend::instance(), 0);
// if (setupDlg->showSetupDialog(new_setup)) {
// auto& backend = Backend::instance();
// for (const auto interfaceId: backend.getInterfaceList())
// {
// log_info(backend.getInterfaceById(interfaceId)->getName());
// }
// backend.setSetup(new_setup);
// backend.clearTrace();
// backend.startMeasurement();
// }
}
void MainWindow::disconnectCan()
{
Backend::instance().stopMeasurement();
}
void MainWindow::handlePackage(int index)
{
log_info("Received message: " + QString::number(index));
lastPackageDateTime_ = QDateTime::currentDateTime();
auto& backend = Backend::instance();
auto message = backend.getTrace()->getMessage(index);
if (!message)
{
return;
}
if (message->getId() != 0x60)
{
return;
}
quint16 frequency = ((message->getByte(1) << 8) + message->getByte(0)) & 0x7FFF;
quint8 sign = ((message->getByte(1) & 0x80) >> 7);
double frequencyValue = (sign ? -1 : 1) * frequency;
frequencyValue /= 128;
parameterModel_->setItem(1, 1, new QStandardItem(QString::number(frequencyValue, 'f', 2)));
quint16 inputVoltage = (message->getByte(2) << 2) + ((message->getByte(7) & 0x03) >> 0);
inputVoltage /= 1.137;
parameterModel_->setItem(0, 1, new QStandardItem(QString::number(inputVoltage)));
quint16 coolantTemperatureIndex = (message->getByte(4) << 2) + ((message->getByte(7) & 0x30) >> 4);
if (coolantTemperatureIndex > 1023)
{
coolantTemperatureIndex = 0;
}
coolantTemperatureIndex = coolantTemperatureIndex << 2;
qint16 coolantTemperature = adcToTemperature(coolantTemperatureIndex);
parameterModel_->setItem(5, 1, new QStandardItem(QString::number(coolantTemperature)));
quint16 outputPhaseCurrent = (message->getByte(3) << 2) + ((message->getByte(7) & 0x0C) >> 2);
outputPhaseCurrent /= 3.1;
quint8 phaseNumber = ((message->getByte(7) & 0xC0) >> 6);
switch (phaseNumber)
{
case 0x00:
parameterModel_->setItem(2, 1, new QStandardItem(QString::number(outputPhaseCurrent)));
break;
case 0x01:
parameterModel_->setItem(3, 1, new QStandardItem(QString::number(outputPhaseCurrent)));
break;
case 0x02:
parameterModel_->setItem(4, 1, new QStandardItem(QString::number(outputPhaseCurrent)));
break;
default:
break;
}
auto status = message->getByte(5);
if (status != lastStatus_)
{
lastStatus_ = status;
statusModel_->appendRow({new QStandardItem(message->getDateTime().toString("dd.MM.yyyy hh:mm:ss")),
new QStandardItem(statusToString(status)),
new QStandardItem(statusToDescription(status))});
}
}
void MainWindow::updateDateTime()
{
const auto currentDateTime = QDateTime::currentDateTime();
ui->dateLabel->setText(currentDateTime.toString("dd.MM.yyyy"));
ui->timeLabel->setText(currentDateTime.toString("hh:mm:ss"));
}
void MainWindow::updateCanStatus()
{
// auto isConnected = false;
// auto& backend = Backend::instance();
// for (const auto interfaceId: backend.getInterfaceList())
// {
// isConnected |= backend.getInterfaceById(interfaceId)->isOpen();
// }
auto isConnected = lastPackageDateTime_.msecsTo(QDateTime::currentDateTime()) < 5000;
auto pallette = QPalette();
auto color = QColor(isConnected ? Qt::darkGreen : Qt::darkRed);
color.setAlphaF(0.7);
pallette.setColor(QPalette::Window, color);
ui->titleLabel->setAutoFillBackground(true);
ui->titleLabel->setPalette(pallette);
}
void MainWindow::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 MainWindow::setupTables()
{
parameterModel_ = new QStandardItemModel(6, 2, this);
statusModel_ = new QStandardItemModel(0, 3, this);
parameterModel_->setHorizontalHeaderLabels({tr("Parameter"), tr("Value")});
statusModel_->setHorizontalHeaderLabels({tr("Time"), tr("Module status"), tr("Module status description")});
parameterModel_->setItem(0, 0, new QStandardItem(tr("Input voltage, V")));
parameterModel_->setItem(1, 0, new QStandardItem(tr("Input voltage frequency, Hz")));
parameterModel_->setItem(2, 0, new QStandardItem(tr("Phase A input current, A")));
parameterModel_->setItem(3, 0, new QStandardItem(tr("Phase B input current, A")));
parameterModel_->setItem(4, 0, new QStandardItem(tr("Phase C input current, A")));
parameterModel_->setItem(5, 0, new QStandardItem(tr("Coolant temperature, °C")));
ui->parameterTableView->setModel(parameterModel_);
ui->statusTableView->setModel(statusModel_);
ui->parameterTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->statusTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
ui->parameterTableView->horizontalHeader()->setStretchLastSection(true);
ui->statusTableView->horizontalHeader()->setStretchLastSection(true);
ui->parameterTableView->verticalHeader()->hide();
ui->parameterTableView->setColumnWidth(0, 280);
ui->statusTableView->setColumnWidth(0, 150);
}
QString MainWindow::statusToString(quint8 status)
{
return "0x" + QString::number(status, 16).rightJustified(2, '0');
}
QString MainWindow::statusToDescription(quint8 status)
{
QString description;
status &= 0x7F;
switch (status)
{
case 0x00:
description = tr("Supply voltage 110[V] to the control circuit");
break;
case 0x01:
description = tr("Pause after blocking");
break;
case 0x03:
description = tr("Waiting for the required DC link voltage level. 670[V]. Input voltage");
break;
case 0x04:
description = tr("Waiting for a CAN command to start work");
break;
case 0x07:
description = tr("Normal operation");
break;
case 0x08:
description = tr("Normal operation with output current limitation");
break;
case 0x10:
description = tr("DC link overvoltage. Input voltage");
break;
case 0x11:
description = tr("Fault half bridge 1");
break;
case 0x12:
description = tr("Fault half bridge 2");
break;
case 0x14:
description = tr("Fault half bridge 3");
break;
case 0x18:
description = tr("Cooler overheating");
break;
case 0x21:
description = tr("Instantaneous total current fault (270 A)");
break;
case 0x22:
description = tr("Large total current for 15 seconds (200 A)");
break;
case 0x27:
description = tr("Instant current fault in one of the phases (396 A)");
break;
case 0x25:
description = tr("No incoming messages via CAN");
break;
default:
description = tr("Unknown status");
break;
}
return description;
}
qint16 MainWindow::adcToTemperature(quint16 adc)
{
const double vref = 2.5; // Напряжение опорное
const double vin = 5.0; // Входное напряжение
const double r = 10000; // Сопротивление резистора в Омах (например, 10kΩ)
// Преобразуем значение АЦП в выходное напряжение
double vout = (adc / 4095.0) * vref;
// Проверяем, чтобы Vout не было равно Vin
if (vout >= vin)
{
return 0; // Ошибка: Vout не может быть больше или равно Vin
}
// Вычисляем сопротивление термистора
double r_ntc = r * (vout / (vin - vout));
QList<QPair<qint16, double>> temp_table =
{
{-55, 96.3},
{-50, 67.01},
{-45, 47.17},
{-40, 33.65},
{-35, 24.26},
{-30, 17.7},
{-25, 13.04},
{-20, 9.707},
{-15, 7.293},
{-10, 5.533},
{-5, 4.232},
{0, 3.265},
{5, 2.539},
{10, 1.99},
{15, 1.571},
{20, 1.249},
{25, 1.0},
{30, 0.8057},
{35, 0.6531},
{40, 0.5327},
{45, 0.4369},
{50, 0.3603},
{55, 0.2986},
{60, 0.2488},
{65, 0.2083},
{70, 0.1752},
{75, 0.1481},
{80, 0.1258},
{85, 0.1072},
{90, 0.09177},
{95, 0.07885},
{100, 0.068},
{105, 0.05886},
{110, 0.05112},
{115, 0.04454},
{120, 0.03893},
{125, 0.03417},
{130, 0.03009},
{135, 0.02654},
{140, 0.02348},
{145, 0.02083},
{150, 0.01853},
{155, 0.01653},
};
for (qsizetype i = 0; i < temp_table.size() - 1; i++)
{
if (r_ntc <= temp_table[i].second && r_ntc >= temp_table[i + 1].second)
{
return temp_table[i].first + (temp_table[i + 1].first - temp_table[i].first) *
(r_ntc - temp_table[i].second) / (temp_table[i + 1].second - temp_table[i].second);
}
}
return 0;
}