371 lines
11 KiB
C++
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 = 10; // Сопротивление резистора в КилоОмах (например, 10kΩ)
|
|
|
|
// Преобразуем значение АЦП в выходное напряжение
|
|
double vout = (adc / 4095.0) * vref;
|
|
|
|
// Проверяем, чтобы Vout не было равно Vin
|
|
if (vout >= vin)
|
|
{
|
|
return -100; // Ошибка: 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 -101;
|
|
}
|