Compare commits
2 Commits
3f1cd53e19
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba0506caf7 | ||
|
|
f10f3c8927 |
@@ -26,36 +26,31 @@ AdcGraphDialog::AdcGraphDialog(QModbusClient *modbusDevice, QWidget *parent) :
|
||||
m_teNumber(-1),
|
||||
m_adcZero(0),
|
||||
m_adcOneVolt(4096),
|
||||
m_series(new QLineSeries()),
|
||||
m_chart(new QChart()),
|
||||
m_stableStartLine(new QLineSeries()),
|
||||
m_stableEndLine(new QLineSeries()),
|
||||
m_stableStartIndex(-1),
|
||||
m_stableEndIndex(-1),
|
||||
m_series(new QLineSeries()),
|
||||
m_chart(new QChart()),
|
||||
m_startAddress(0),
|
||||
m_registerCount(100)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Настройка основного графика
|
||||
m_series->setName("АЦП данные");
|
||||
m_chart->addSeries(m_series);
|
||||
|
||||
// Настройка линий стабильного участка
|
||||
m_stableStartLine->setName("Начало стаб. участка");
|
||||
m_stableStartLine->setColor(Qt::red);
|
||||
m_stableStartLine->setPen(QPen(Qt::red, 2, Qt::DashLine));
|
||||
m_chart->addSeries(m_stableStartLine);
|
||||
|
||||
m_stableEndLine->setName("Конец стаб. участка");
|
||||
m_stableEndLine->setColor(Qt::green);
|
||||
m_stableEndLine->setPen(QPen(Qt::green, 2, Qt::DashLine));
|
||||
m_chart->addSeries(m_stableEndLine);
|
||||
|
||||
m_chart->setTitle("График АЦП");
|
||||
m_chart->legend()->setVisible(true);
|
||||
m_chart->legend()->setAlignment(Qt::AlignTop);
|
||||
|
||||
m_axisX = new QValueAxis();
|
||||
m_axisX->setTitleText("Отсчеты АЦП");
|
||||
m_axisX->setRange(0, m_registerCount);
|
||||
@@ -63,7 +58,6 @@ AdcGraphDialog::AdcGraphDialog(QModbusClient *modbusDevice, QWidget *parent) :
|
||||
m_series->attachAxis(m_axisX);
|
||||
m_stableStartLine->attachAxis(m_axisX);
|
||||
m_stableEndLine->attachAxis(m_axisX);
|
||||
|
||||
m_axisY = new QValueAxis();
|
||||
m_axisY->setTitleText("Напряжение, В");
|
||||
m_axisY->setRange(-1.3, 1.3);
|
||||
@@ -71,13 +65,10 @@ AdcGraphDialog::AdcGraphDialog(QModbusClient *modbusDevice, QWidget *parent) :
|
||||
m_series->attachAxis(m_axisY);
|
||||
m_stableStartLine->attachAxis(m_axisY);
|
||||
m_stableEndLine->attachAxis(m_axisY);
|
||||
|
||||
QChartView *chartView = new QChartView(m_chart);
|
||||
chartView->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(ui->plotWidget);
|
||||
layout->addWidget(chartView);
|
||||
|
||||
// Подключаем сигналы элементов управления диапазоном
|
||||
connect(ui->registerCountSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
|
||||
this, &AdcGraphDialog::on_registerCountChanged);
|
||||
@@ -85,21 +76,16 @@ AdcGraphDialog::AdcGraphDialog(QModbusClient *modbusDevice, QWidget *parent) :
|
||||
this, &AdcGraphDialog::on_rangeApplyClicked);
|
||||
connect(ui->teNumberSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
|
||||
this, &AdcGraphDialog::on_teNumberChanged);
|
||||
|
||||
connect(m_updateTimer, &QTimer::timeout, this, &AdcGraphDialog::onUpdateTimer);
|
||||
connect(ui->closeBtn, &QPushButton::clicked, this, &AdcGraphDialog::on_closeBtn_clicked);
|
||||
}
|
||||
|
||||
|
||||
|
||||
AdcGraphDialog::~AdcGraphDialog()
|
||||
{
|
||||
stopGraph();
|
||||
delete ui;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AdcGraphDialog::setModbusDevice(QModbusClient *device)
|
||||
{
|
||||
m_modbusDevice = device;
|
||||
@@ -113,26 +99,23 @@ void AdcGraphDialog::on_teNumberChanged(int value)
|
||||
|
||||
void AdcGraphDialog::setTENumber(int boardID, int teNumber)
|
||||
{
|
||||
if (m_boardAddress == -1) return;
|
||||
|
||||
if(m_boardAddress == -1)
|
||||
return;
|
||||
m_teNumber = teNumber;
|
||||
m_boardId = boardID;
|
||||
m_boardAddress = m_boardId + 1;
|
||||
|
||||
// Обновляем заголовок окна
|
||||
setWindowTitle(QString("График АЦП - Плата %1, ТЭ %2 (адр %3-%4)")
|
||||
.arg(m_boardId + 1)
|
||||
.arg(m_teNumber)
|
||||
.arg(m_startAddress)
|
||||
.arg(m_startAddress + m_registerCount - 1));
|
||||
|
||||
// Записываем новый номер ТЭ в устройство
|
||||
if(m_modbusDevice) {
|
||||
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, REG_TE_NUMBER, 1);
|
||||
unit.setValue(0, teNumber);
|
||||
|
||||
if(auto *reply = m_modbusDevice->sendWriteRequest(unit, m_boardAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
if(!reply->isFinished())
|
||||
connect(reply, &QModbusReply::finished, this, [this, reply, teNumber]() {
|
||||
if(reply->error() == QModbusDevice::NoError) {
|
||||
qDebug() << "TE number changed to:" << teNumber;
|
||||
@@ -143,78 +126,65 @@ void AdcGraphDialog::setTENumber(int boardID, int teNumber)
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
} else {
|
||||
else
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
ui->teNumberSpinBox->setValue(teNumber);
|
||||
}
|
||||
|
||||
void AdcGraphDialog::readCalibrationValues()
|
||||
{
|
||||
if (!m_modbusDevice || m_boardAddress == -1) return;
|
||||
|
||||
if(!m_modbusDevice || m_boardAddress == -1)
|
||||
return;
|
||||
// Читаем калибровочные значения (регистры 555 и 556)
|
||||
QModbusDataUnit unit(QModbusDataUnit::InputRegisters, REG_ADC_ZERO, 2);
|
||||
|
||||
if(auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
if(!reply->isFinished())
|
||||
connect(reply, &QModbusReply::finished, this, [this, reply]() {
|
||||
if(reply->error() == QModbusDevice::NoError) {
|
||||
const QModbusDataUnit result = reply->result();
|
||||
if(result.valueCount() >= 2) {
|
||||
m_adcZero = result.value(0);
|
||||
m_adcOneVolt = result.value(1);
|
||||
//qDebug() << "Calibration values - Zero:" << m_adcZero << "1V:" << m_adcOneVolt;
|
||||
}
|
||||
} else {
|
||||
//qDebug() << "Error reading calibration:" << reply->errorString();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
} else {
|
||||
else
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdcGraphDialog::readStableIndices()
|
||||
{
|
||||
if (!m_modbusDevice || m_boardAddress == -1) return;
|
||||
|
||||
if(!m_modbusDevice || m_boardAddress == -1)
|
||||
return;
|
||||
// Читаем индексы стабильного участка (регистры 557 и 558)
|
||||
QModbusDataUnit unit(QModbusDataUnit::InputRegisters, REG_STABLE_START, 2);
|
||||
|
||||
if(auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
if(!reply->isFinished())
|
||||
connect(reply, &QModbusReply::finished, this, &AdcGraphDialog::onStableIndicesReady);
|
||||
} else {
|
||||
else
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdcGraphDialog::onStableIndicesReady()
|
||||
{
|
||||
auto *reply = qobject_cast<QModbusReply*>(sender());
|
||||
if (!reply) return;
|
||||
|
||||
if(!reply)
|
||||
return;
|
||||
if(reply->error() == QModbusDevice::NoError) {
|
||||
const QModbusDataUnit result = reply->result();
|
||||
if(result.valueCount() >= 2) {
|
||||
m_stableStartIndex = result.value(0);
|
||||
m_stableEndIndex = result.value(1);
|
||||
//qDebug() << "Stable indices updated - Start:" << m_stableStartIndex << "End:" << m_stableEndIndex;
|
||||
updateStableLines();
|
||||
|
||||
// Обновляем статистику с новыми индексами
|
||||
updateStatisticsWithStableInfo();
|
||||
}
|
||||
} else {
|
||||
//qDebug() << "Error reading stable indices:" << reply->errorString();
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
@@ -224,26 +194,24 @@ void AdcGraphDialog::updateStatistics()
|
||||
double min = 1000, max = -1000, sum = 0;
|
||||
for(const QPointF &point : m_series->points()) {
|
||||
double y = point.y();
|
||||
if (y < min) min = y;
|
||||
if (y > max) max = y;
|
||||
if(y < min)
|
||||
min = y;
|
||||
if(y > max)
|
||||
max = y;
|
||||
sum += y;
|
||||
}
|
||||
double avg = sum / m_series->count();
|
||||
|
||||
ui->minLabel->setText(QString::number(min, 'f', 3) + " В");
|
||||
ui->maxLabel->setText(QString::number(max, 'f', 3) + " В");
|
||||
ui->avgLabel->setText(QString::number(avg, 'f', 3) + " В");
|
||||
|
||||
// Обновляем информацию о стабильном участке
|
||||
updateStatisticsWithStableInfo();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AdcGraphDialog::updateStatisticsWithStableInfo()
|
||||
{
|
||||
if (m_series->count() > 0) {
|
||||
if(m_series->count() > 0)
|
||||
// Используем абсолютные индексы в формате "начальный-конечный"
|
||||
ui->samplesLabel->setText(
|
||||
QString("%1 отсч. (адр %2-%3) [стаб: %4-%5]")
|
||||
@@ -254,19 +222,15 @@ void AdcGraphDialog::updateStatisticsWithStableInfo()
|
||||
.arg(m_stableEndIndex) // Абсолютный конечный индекс
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AdcGraphDialog::updateStableLines()
|
||||
{
|
||||
// Очищаем предыдущие линии
|
||||
m_stableStartLine->clear();
|
||||
m_stableEndLine->clear();
|
||||
|
||||
// Получаем текущий диапазон оси Y
|
||||
double yMin = m_axisY->min();
|
||||
double yMax = m_axisY->max();
|
||||
|
||||
// Добавляем вертикальную линию для начала стабильного участка
|
||||
// Учитываем текущий диапазон отображения
|
||||
if(m_stableStartIndex >= m_startAddress && m_stableStartIndex < m_startAddress + m_registerCount) {
|
||||
@@ -274,7 +238,6 @@ void AdcGraphDialog::updateStableLines()
|
||||
m_stableStartLine->append(relativeStartIndex, yMin);
|
||||
m_stableStartLine->append(relativeStartIndex, yMax);
|
||||
}
|
||||
|
||||
// Добавляем вертикальную линию для конца стабильного участка
|
||||
if(m_stableEndIndex >= m_startAddress && m_stableEndIndex < m_startAddress + m_registerCount) {
|
||||
int relativeEndIndex = m_stableEndIndex - m_startAddress;
|
||||
@@ -288,112 +251,84 @@ void AdcGraphDialog::updateGraphRange()
|
||||
// Обновляем диапазон оси X
|
||||
m_axisX->setRange(0, m_registerCount);
|
||||
m_axisX->setTitleText(QString("Отсчеты АЦП (%1-%2)").arg(m_startAddress).arg(m_startAddress + m_registerCount - 1));
|
||||
|
||||
// Сбрасываем ось Y к разумному диапазону по умолчанию
|
||||
m_axisY->setRange(-1.2, 1.2);
|
||||
|
||||
// Обновляем линии стабильного участка с учетом нового диапазона
|
||||
updateStableLines();
|
||||
}
|
||||
|
||||
void AdcGraphDialog::readAdcDataAndIndices()
|
||||
{
|
||||
if (!m_modbusDevice || m_boardAddress == -1) return;
|
||||
|
||||
if(!m_modbusDevice || m_boardAddress == -1)
|
||||
return;
|
||||
// Создаем один запрос для данных АЦП + индексов стабильности
|
||||
// Адреса: данные АЦП (571+), индексы (557-558)
|
||||
int start = m_startAddress + REG_ADC_BUFFER_START;
|
||||
int count = m_registerCount;
|
||||
|
||||
// Читаем индексы стабильности (557-558) и данные АЦП одним запросом
|
||||
QModbusDataUnit unit(QModbusDataUnit::InputRegisters, REG_STABLE_START,
|
||||
count + (start - REG_STABLE_START));
|
||||
|
||||
//qDebug() << "Reading combined data from address" << REG_STABLE_START << "count:" << unit.valueCount();
|
||||
|
||||
if(auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
if(!reply->isFinished())
|
||||
connect(reply, &QModbusReply::finished, this, &AdcGraphDialog::onCombinedDataReady);
|
||||
} else {
|
||||
else
|
||||
reply->deleteLater();
|
||||
}
|
||||
} else {
|
||||
//qDebug() << "Failed to send combined data read request";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AdcGraphDialog::onCombinedDataReady()
|
||||
{
|
||||
auto *reply = qobject_cast<QModbusReply*>(sender());
|
||||
if (!reply) return;
|
||||
|
||||
if((m_adcZero == 0) || (m_adcOneVolt == 0))
|
||||
{
|
||||
if(!reply)
|
||||
return;
|
||||
if((m_adcZero == 0) || (m_adcOneVolt == 0)) {
|
||||
readCalibrationValues();
|
||||
return;
|
||||
}
|
||||
|
||||
if(reply->error() == QModbusDevice::NoError) {
|
||||
const QModbusDataUnit result = reply->result();
|
||||
|
||||
// Обрабатываем индексы стабильности (первые 2 регистра)
|
||||
if(result.valueCount() >= 2) {
|
||||
m_stableStartIndex = result.value(0);
|
||||
m_stableEndIndex = result.value(1);
|
||||
//qDebug() << "Stable indices - Start:" << m_stableStartIndex << "End:" << m_stableEndIndex;
|
||||
}
|
||||
|
||||
// Обрабатываем данные АЦП (остальные регистры)
|
||||
int adcDataStartIndex = (m_startAddress + REG_ADC_BUFFER_START) - REG_STABLE_START;
|
||||
|
||||
uint adcDataStartIndex = (m_startAddress + REG_ADC_BUFFER_START) - REG_STABLE_START;
|
||||
// Очищаем предыдущие данные
|
||||
m_series->clear();
|
||||
|
||||
// Добавляем новые точки и определяем диапазон
|
||||
double minVoltage = 1000, maxVoltage = -1000;
|
||||
for(int i = 0; i < m_registerCount; ++i) {
|
||||
if(adcDataStartIndex + i < result.valueCount()) {
|
||||
double voltage = convertAdcToVoltage(result.value(adcDataStartIndex + i));
|
||||
m_series->append(i, voltage);
|
||||
|
||||
// Обновляем мин/макс для автоматического масштабирования
|
||||
if (voltage < minVoltage) minVoltage = voltage;
|
||||
if (voltage > maxVoltage) maxVoltage = voltage;
|
||||
if(voltage < minVoltage)
|
||||
minVoltage = voltage;
|
||||
if(voltage > maxVoltage)
|
||||
maxVoltage = voltage;
|
||||
}
|
||||
}
|
||||
|
||||
// Обновляем график и статистику
|
||||
updateYAxisRange(minVoltage, maxVoltage);
|
||||
updateStableLines();
|
||||
updateStatistics();
|
||||
|
||||
} else {
|
||||
// qDebug() << "Error reading combined data:" << reply->errorString();
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
|
||||
void AdcGraphDialog::readAdcBuffer()
|
||||
{
|
||||
if (!m_modbusDevice || m_boardAddress == -1) return;
|
||||
|
||||
if(!m_modbusDevice || m_boardAddress == -1)
|
||||
return;
|
||||
// Читаем выбранный диапазон буфера АЦП
|
||||
QModbusDataUnit unit(QModbusDataUnit::InputRegisters, m_startAddress+REG_ADC_BUFFER_START, m_registerCount);
|
||||
|
||||
// qDebug() << "Reading ADC buffer from address" << m_startAddress << "count:" << m_registerCount;
|
||||
|
||||
if(auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
if(!reply->isFinished())
|
||||
connect(reply, &QModbusReply::finished, this, &AdcGraphDialog::onReadReady);
|
||||
} else {
|
||||
else
|
||||
reply->deleteLater();
|
||||
}
|
||||
} else {
|
||||
// qDebug() << "Failed to send ADC buffer read request";
|
||||
}
|
||||
}
|
||||
|
||||
void AdcGraphDialog::startGraph(int boardId, int teNumber)
|
||||
@@ -401,34 +336,25 @@ void AdcGraphDialog::startGraph(int boardId, int teNumber)
|
||||
m_boardId = boardId;
|
||||
m_teNumber = teNumber;
|
||||
m_boardAddress = boardId + 1;
|
||||
|
||||
// Устанавливаем начальное значение в спинбокс
|
||||
ui->teNumberSpinBox->setValue(teNumber);
|
||||
|
||||
setWindowTitle(QString("График АЦП - Плата %1, ТЭ %2 (адр %3-%4)")
|
||||
.arg(boardId + 1)
|
||||
.arg(teNumber)
|
||||
.arg(m_startAddress)
|
||||
.arg(m_startAddress + m_registerCount - 1));
|
||||
|
||||
// Очищаем предыдущие данные
|
||||
m_series->clear();
|
||||
m_stableStartLine->clear();
|
||||
m_stableEndLine->clear();
|
||||
|
||||
// Обновляем диапазон графика
|
||||
updateGraphRange();
|
||||
|
||||
readCalibrationValues();
|
||||
|
||||
// Записываем начальный номер ТЭ
|
||||
setTENumber(m_boardAddress, teNumber);
|
||||
|
||||
m_updateTimer->start(m_timeout);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AdcGraphDialog::setTimeout(int timeout)
|
||||
{
|
||||
m_timeout = timeout;
|
||||
@@ -439,17 +365,15 @@ void AdcGraphDialog::stopGraph()
|
||||
m_updateTimer->stop();
|
||||
m_boardId = -1;
|
||||
m_boardAddress = -1;
|
||||
|
||||
// Отменяем все pending запросы Modbus
|
||||
if (m_modbusDevice) {
|
||||
if(m_modbusDevice)
|
||||
m_modbusDevice->disconnect(this); // Отключаем все сигналы
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AdcGraphDialog::onUpdateTimer()
|
||||
{
|
||||
if (m_boardAddress == -1) return;
|
||||
if(m_boardAddress == -1)
|
||||
return;
|
||||
// Читаем и буфер АЦП, и индексы стабильности каждый период
|
||||
readAdcDataAndIndices();
|
||||
}
|
||||
@@ -457,54 +381,46 @@ void AdcGraphDialog::onUpdateTimer()
|
||||
void AdcGraphDialog::onReadReady()
|
||||
{
|
||||
auto *reply = qobject_cast<QModbusReply*>(sender());
|
||||
if (!reply) return;
|
||||
|
||||
if(!reply)
|
||||
return;
|
||||
if(reply->error() == QModbusDevice::NoError) {
|
||||
const QModbusDataUnit result = reply->result();
|
||||
|
||||
// Очищаем предыдущие данные
|
||||
m_series->clear();
|
||||
|
||||
// Добавляем новые точки и определяем диапазон
|
||||
double minVoltage = 1000, maxVoltage = -1000;
|
||||
for (int i = 0; i < result.valueCount(); ++i) {
|
||||
for(uint i = 0; i < result.valueCount(); ++i) {
|
||||
double voltage = convertAdcToVoltage(result.value(i));
|
||||
m_series->append(i, voltage);
|
||||
|
||||
// Обновляем мин/макс для автоматического масштабирования
|
||||
if (voltage < minVoltage) minVoltage = voltage;
|
||||
if (voltage > maxVoltage) maxVoltage = voltage;
|
||||
if(voltage < minVoltage)
|
||||
minVoltage = voltage;
|
||||
if(voltage > maxVoltage)
|
||||
maxVoltage = voltage;
|
||||
}
|
||||
|
||||
// Автоматически настраиваем диапазон оси Y
|
||||
updateYAxisRange(minVoltage, maxVoltage);
|
||||
|
||||
// Обновляем линии стабильного участка
|
||||
updateStableLines();
|
||||
|
||||
// Обновляем статистику
|
||||
if(m_series->count() > 0) {
|
||||
double min = 1000, max = -1000, sum = 0;
|
||||
for(const QPointF &point : m_series->points()) {
|
||||
double y = point.y();
|
||||
if (y < min) min = y;
|
||||
if (y > max) max = y;
|
||||
if(y < min)
|
||||
min = y;
|
||||
if(y > max)
|
||||
max = y;
|
||||
sum += y;
|
||||
}
|
||||
double avg = sum / m_series->count();
|
||||
|
||||
ui->minLabel->setText(QString::number(min, 'f', 3) + " В");
|
||||
ui->maxLabel->setText(QString::number(max, 'f', 3) + " В");
|
||||
ui->avgLabel->setText(QString::number(avg, 'f', 3) + " В");
|
||||
|
||||
// Обновляем информацию о стабильном участке
|
||||
updateStatisticsWithStableInfo();
|
||||
}
|
||||
|
||||
} else {
|
||||
// qDebug() << "Error reading ADC buffer:" << reply->errorString();
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
@@ -513,66 +429,39 @@ void AdcGraphDialog::updateYAxisRange(double minVoltage, double maxVoltage)
|
||||
// Добавляем запас 10% к диапазону
|
||||
double range = maxVoltage - minVoltage;
|
||||
double margin = range * 0.1;
|
||||
|
||||
double yMin = minVoltage - margin;
|
||||
double yMax = maxVoltage + margin;
|
||||
|
||||
// Если диапазон слишком маленький или слишком большой, устанавливаем разумные пределы
|
||||
// if ((range < 0.1) || ((maxVoltage > 0.5) && (minVoltage < -0.5)))
|
||||
// {
|
||||
// yMin = -1.5;
|
||||
// yMax = 1.5;
|
||||
// }
|
||||
// else if(maxVoltage > 0.5) {
|
||||
// yMin = -0.1;
|
||||
// yMax = 1.5;
|
||||
// }
|
||||
// else if(minVoltage < -0.5)
|
||||
// {
|
||||
// yMin = -1.5;
|
||||
// yMax =0.1;
|
||||
// }
|
||||
// else
|
||||
{
|
||||
yMin = -1.5;
|
||||
yMax = 1.5;
|
||||
}
|
||||
|
||||
// Ограничиваем разумными пределами
|
||||
yMin = qMax(yMin, -5.0); // Не ниже -5В
|
||||
yMax = qMin(yMax, 5.0); // Не выше 5В
|
||||
|
||||
// Устанавливаем новый диапазон
|
||||
m_axisY->setRange(yMin, yMax);
|
||||
|
||||
// Обновляем линии стабильного участка с новым диапазоном
|
||||
updateStableLines();
|
||||
}
|
||||
|
||||
|
||||
double AdcGraphDialog::convertAdcToVoltage(quint16 adcValue)
|
||||
{
|
||||
if (m_adcOneVolt == m_adcZero) return 0;
|
||||
if(m_adcOneVolt == m_adcZero)
|
||||
return 0;
|
||||
return (adcValue - m_adcZero) * 1.1 / (m_adcOneVolt - m_adcZero);
|
||||
}
|
||||
|
||||
|
||||
void AdcGraphDialog::on_registerCountChanged(int value)
|
||||
{
|
||||
m_registerCount = value;
|
||||
// qDebug() << "Register count changed to:" << value;
|
||||
}
|
||||
|
||||
void AdcGraphDialog::on_rangeApplyClicked()
|
||||
{
|
||||
// qDebug() << "Applying new range - Start:" << m_startAddress << "Count:" << m_registerCount;
|
||||
updateGraphRange();
|
||||
|
||||
// Немедленно обновляем данные
|
||||
if (m_boardAddress != -1) {
|
||||
if(m_boardAddress != -1)
|
||||
readAdcBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void AdcGraphDialog::on_closeBtn_clicked()
|
||||
{
|
||||
|
||||
@@ -19,21 +19,17 @@ class AdcGraphDialog;
|
||||
class AdcGraphDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AdcGraphDialog(QModbusClient *modbusDevice, QWidget *parent = nullptr);
|
||||
~AdcGraphDialog();
|
||||
|
||||
void setTENumber(int boardID, int teNumber);
|
||||
void setModbusDevice(QModbusClient *device);
|
||||
void startGraph(int boardId, int teNumber);
|
||||
void stopGraph();
|
||||
void setTimeout(int timeout);
|
||||
void readyClose();
|
||||
|
||||
signals:
|
||||
void dialogClosed();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override {
|
||||
stopGraph();
|
||||
@@ -41,7 +37,6 @@ protected:
|
||||
emit dialogClosed();
|
||||
QDialog::closeEvent(event);
|
||||
}
|
||||
|
||||
private slots:
|
||||
void on_teNumberChanged(int value);
|
||||
void onUpdateTimer();
|
||||
@@ -50,7 +45,6 @@ private slots:
|
||||
void on_closeBtn_clicked();
|
||||
void on_registerCountChanged(int value);
|
||||
void on_rangeApplyClicked();
|
||||
|
||||
private:
|
||||
Ui::AdcGraphDialog *ui;
|
||||
QModbusClient *m_modbusDevice;
|
||||
@@ -59,27 +53,22 @@ private:
|
||||
int m_boardAddress;
|
||||
int m_teNumber;
|
||||
int m_timeout;
|
||||
|
||||
// Калибровочные значения
|
||||
double m_adcZero;
|
||||
double m_adcOneVolt;
|
||||
|
||||
// Для отображения стабильного участка
|
||||
QLineSeries *m_stableStartLine;
|
||||
QLineSeries *m_stableEndLine;
|
||||
int m_stableStartIndex;
|
||||
int m_stableEndIndex;
|
||||
|
||||
// Данные графика
|
||||
QLineSeries *m_series;
|
||||
QValueAxis *m_axisX;
|
||||
QValueAxis *m_axisY;
|
||||
QChart *m_chart; // Добавить указатель на chart
|
||||
|
||||
// Управление диапазоном регистров
|
||||
int m_startAddress;
|
||||
int m_registerCount;
|
||||
|
||||
void updateStatistics();
|
||||
void readAdcDataAndIndices();
|
||||
void onCombinedDataReady();
|
||||
|
||||
@@ -17,23 +17,19 @@ DebugTerminalDialog::DebugTerminalDialog(QWidget *parent) :
|
||||
boards[0].error5V = ui->discErr5TestChkBox_1;
|
||||
boards[0].error5VSCI = ui->discErr5VsciTestChkBox_1;
|
||||
boards[0].error5VA = ui->discErr5VATestChkBox_1;
|
||||
|
||||
boards[1].error24V = ui->discErr24TestChkBox_2;
|
||||
boards[1].error5V = ui->discErr5TestChkBox_2;
|
||||
boards[1].error5VSCI = ui->discErr5VsciTestChkBox_2;
|
||||
boards[1].error5VA = ui->discErr5VATestChkBox_2;
|
||||
|
||||
boards[2].error24V = ui->discErr24TestChkBox_3;
|
||||
boards[2].error5V = ui->discErr5TestChkBox_3;
|
||||
boards[2].error5VSCI = ui->discErr5VsciTestChkBox_3;
|
||||
boards[2].error5VA = ui->discErr5VATestChkBox_3;
|
||||
|
||||
boards[3].error24V = ui->discErr24TestChkBox_4;
|
||||
boards[3].error5V = ui->discErr5TestChkBox_4;
|
||||
boards[3].error5VSCI = ui->discErr5VsciTestChkBox_4;
|
||||
boards[3].error5VA = ui->discErr5VATestChkBox_4;
|
||||
initializeConnections();
|
||||
|
||||
// Создаем AdcGraphDialog с nullptr
|
||||
m_adcGraphDialog = new AdcGraphDialog(nullptr, this);
|
||||
}
|
||||
@@ -47,14 +43,30 @@ void DebugTerminalDialog::setMainTerm(M3KTE* term)
|
||||
mainTerm = term;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Устанавливает указатель на объект QModbusClient для отладочного терминала.
|
||||
*
|
||||
* Этот метод присваивает указатель на Modbus-устройство и, при наличии графического диалога
|
||||
* для отображения данных АЦП, передает ему тот же объект для синхронизации.
|
||||
*
|
||||
* @param device Указатель на объект QModbusClient, представляющий Modbus-устройство.
|
||||
*/
|
||||
void DebugTerminalDialog::setModbusDevice(QModbusClient *device)
|
||||
{
|
||||
m_modbusDevice = device;
|
||||
if (m_adcGraphDialog && device) {
|
||||
if(m_adcGraphDialog && device)
|
||||
m_adcGraphDialog->setModbusDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Устанавливает режим отладки, активируя или деактивируя коили.
|
||||
*
|
||||
* Этот метод вызывает функцию writeCoil для нескольких адресов (0, 1, 2, 3),
|
||||
* устанавливая значение enable для каждого из них. Предполагается, что эти коили
|
||||
* управляют режимом отладки устройства или системы.
|
||||
*
|
||||
* @param enable Целое значение (например, 0 или 1), указывающее, включать или выключать режим отладки.
|
||||
*/
|
||||
void DebugTerminalDialog::setDebugTerminalCoil(int enable)
|
||||
{
|
||||
writeCoil(0, COIL_DEBUG_MODE, enable);
|
||||
@@ -63,16 +75,32 @@ void DebugTerminalDialog::setDebugTerminalCoil(int enable)
|
||||
writeCoil(3, COIL_DEBUG_MODE, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает событие отображения окна диалога.
|
||||
*
|
||||
* Этот метод вызывается при показе окна и сначала вызывает базовую реализацию showEvent.
|
||||
* Затем происходит сброс всех настроек или состояний через resetAll(), а после этого
|
||||
* устанавливается значение 1 для коили, отвечающих за режим отладки, с помощью setDebugTerminalCoil(1).
|
||||
*
|
||||
* @param event Указатель на объект QShowEvent, содержащий информацию о событии отображения.
|
||||
*/
|
||||
void DebugTerminalDialog::showEvent(QShowEvent *event)
|
||||
{
|
||||
QDialog::showEvent(event);
|
||||
// При открытии окна записываем в коил 555 значение "1"
|
||||
|
||||
resetAll();
|
||||
|
||||
setDebugTerminalCoil(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает событие закрытия диалогового окна.
|
||||
*
|
||||
* Перед закрытием окна вызывается метод setDebugTerminalCoil(0),
|
||||
* который отключает режим отладки, устанавливая значение коил в 0.
|
||||
* Затем вызывается базовая обработка closeEvent.
|
||||
*
|
||||
* @param event Указатель на объект QCloseEvent, содержащий информацию о событии закрытия.
|
||||
*/
|
||||
void DebugTerminalDialog::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
// При закрытии окна записываем в коил 555 значение "0"
|
||||
@@ -80,76 +108,7 @@ void DebugTerminalDialog::closeEvent(QCloseEvent *event)
|
||||
QDialog::closeEvent(event);
|
||||
}
|
||||
|
||||
void DebugTerminalDialog::initializeConnections()
|
||||
{
|
||||
// // Подключаем кнопки OK и RestoreDefaults
|
||||
// connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &DebugTerminalDialog::on_buttonBox_clicked);
|
||||
|
||||
// // Подключаем все чекбоксы для платы 1
|
||||
// connect(ui->continiusCallChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_continiusCallChkBox_1_stateChanged);
|
||||
// connect(ui->calibrateCallChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_calibrateCallChkBox_1_stateChanged);
|
||||
// connect(ui->pollTECallChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_pollTECallChkBox_1_stateChanged);
|
||||
// connect(ui->resetKeyCallChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_resetKeyCallChkBox_1_stateChanged);
|
||||
// connect(ui->resetDefaultCallChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_resetDefaultCallChkBox_1_stateChanged);
|
||||
// connect(ui->getHardfaultCallChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_getHardfaultCallChkBox_1_stateChanged);
|
||||
|
||||
|
||||
// connect(ui->enableLedTestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::enableLedTestChkBox_1_stateChanged);
|
||||
|
||||
// connect(ui->discWorkTestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discWorkTestChkBox_1_stateChanged);
|
||||
// connect(ui->discWarnTestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discWarnTestChkBox_1_stateChanged);
|
||||
// connect(ui->discErrTestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discErrTestChkBox_1_stateChanged);
|
||||
|
||||
|
||||
// connect(ui->discErr24TestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discErr24TestChkBox_1_stateChanged);
|
||||
// ui->discErr24TestChkBox_1->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
// connect(ui->discErr5TestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discErr5TestChkBox_1_stateChanged);
|
||||
// ui->discErr5TestChkBox_1->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
// connect(ui->discErr5VsciTestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discErr5VsciTestChkBox_1_stateChanged);
|
||||
// ui->discErr5VsciTestChkBox_1->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
// connect(ui->discErr5VATestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discErr5VATestChkBox_1_stateChanged);
|
||||
// ui->discErr5VATestChkBox_1->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
|
||||
// connect(ui->ledWorkTestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledWorkTestChkBox_1_stateChanged);
|
||||
// connect(ui->ledWarnTestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledWarnTestChkBox_1_stateChanged);
|
||||
// connect(ui->ledErrTestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledErrTestChkBox_1_stateChanged);
|
||||
// connect(ui->ledConnectTestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledConnectTestChkBox_1_stateChanged);
|
||||
// connect(ui->ledVH1TestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledVH1TestChkBox_1_stateChanged);
|
||||
// connect(ui->ledVH2TestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledVH2TestChkBox_1_stateChanged);
|
||||
// connect(ui->ledVH3TestChkBox_1, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledVH3TestChkBox_1_stateChanged);
|
||||
|
||||
// // Подключаем все чекбоксы для платы 2
|
||||
// connect(ui->continiusCallChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_continiusCallChkBox_2_stateChanged);
|
||||
// connect(ui->calibrateCallChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_calibrateCallChkBox_2_stateChanged);
|
||||
// connect(ui->pollTECallChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_pollTECallChkBox_2_stateChanged);
|
||||
// connect(ui->resetKeyCallChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_resetKeyCallChkBox_2_stateChanged);
|
||||
// connect(ui->resetDefaultCallChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_resetDefaultCallChkBox_2_stateChanged);
|
||||
// connect(ui->getHardfaultCallChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_getHardfaultCallChkBox_2_stateChanged);
|
||||
|
||||
// connect(ui->enableLedTestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::enableLedTestChkBox_2_stateChanged);
|
||||
|
||||
// connect(ui->discWorkTestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discWorkTestChkBox_2_stateChanged);
|
||||
// connect(ui->discWarnTestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discWarnTestChkBox_2_stateChanged);
|
||||
// connect(ui->discErrTestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discErrTestChkBox_2_stateChanged);
|
||||
|
||||
// connect(ui->discErr24TestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discErr24TestChkBox_2_stateChanged);
|
||||
// ui->discErr24TestChkBox_2->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
// connect(ui->discErr5TestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discErr5TestChkBox_2_stateChanged);
|
||||
// ui->discErr5TestChkBox_2->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
// connect(ui->discErr5VsciTestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discErr5VsciTestChkBox_2_stateChanged);
|
||||
// ui->discErr5VsciTestChkBox_2->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
// connect(ui->discErr5VATestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_discErr5VATestChkBox_2_stateChanged);
|
||||
// ui->discErr5VATestChkBox_2->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
|
||||
// connect(ui->ledWorkTestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledWorkTestChkBox_2_stateChanged);
|
||||
// connect(ui->ledWarnTestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledWarnTestChkBox_2_stateChanged);
|
||||
// connect(ui->ledErrTestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledErrTestChkBox_2_stateChanged);
|
||||
// connect(ui->ledConnectTestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledConnectTestChkBox_2_stateChanged);
|
||||
// connect(ui->ledVH1TestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledVH1TestChkBox_2_stateChanged);
|
||||
// connect(ui->ledVH2TestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledVH2TestChkBox_2_stateChanged);
|
||||
// connect(ui->ledVH3TestChkBox_2, &QCheckBox::stateChanged, this, &DebugTerminalDialog::on_ledVH3TestChkBox_2_stateChanged);
|
||||
|
||||
}
|
||||
void DebugTerminalDialog::initializeConnections(){}
|
||||
|
||||
void DebugTerminalDialog::updateConnectionStatus(int boardID, bool connected)
|
||||
{
|
||||
@@ -160,6 +119,15 @@ void DebugTerminalDialog::updateConnectionStatus(int boardID, bool connected)
|
||||
// Реализация по необходимости
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработка клика по кнопкам в диалоговом окне.
|
||||
*
|
||||
* В зависимости от роли нажатой кнопки, выполняются соответствующие действия:
|
||||
* - Если роль Reset, вызывается resetAll(), сбрасывающий настройки.
|
||||
* - Если роль Accept, вызывается accept(), подтверждающий изменения.
|
||||
*
|
||||
* @param button Указатель на нажатую кнопку.
|
||||
*/
|
||||
void DebugTerminalDialog::on_buttonBox_clicked(QAbstractButton *button)
|
||||
{
|
||||
switch (ui->buttonBox->buttonRole(button)) {
|
||||
@@ -565,6 +533,16 @@ void DebugTerminalDialog::on_ledVH3TestChkBox_4_stateChanged(int state)
|
||||
writeCoil(3, COIL_LED_VH3_TEST, state == Qt::Checked ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Открывает окно графика для отображения данных АЦП с указанной платы и канала.
|
||||
*
|
||||
* Этот метод уничтожает предыдущий экземпляр диалога AdcGraphDialog, если он есть,
|
||||
* создает новый, связывает сигнал закрытия с установкой режима отладки в 0,
|
||||
* задает интервал обновления графика, запускает отображение данных и показывает окно.
|
||||
*
|
||||
* @param boardID Идентификатор платы.
|
||||
* @param teNumber Номер канала (или технологической единицы).
|
||||
*/
|
||||
void DebugTerminalDialog::openAdc(int boardID, int teNumber)
|
||||
{
|
||||
// Удаляем старый диалог и создаем новый
|
||||
@@ -572,12 +550,10 @@ void DebugTerminalDialog::openAdc(int boardID, int teNumber)
|
||||
m_adcGraphDialog->deleteLater();
|
||||
m_adcGraphDialog = nullptr;
|
||||
}
|
||||
|
||||
m_adcGraphDialog = new AdcGraphDialog(m_modbusDevice, this);
|
||||
connect(m_adcGraphDialog, &AdcGraphDialog::dialogClosed, this, [this]() {
|
||||
setDebugTerminalCoil(0);
|
||||
});
|
||||
|
||||
setGraphUpdateInterval(1000);
|
||||
m_adcGraphDialog->startGraph(boardID, teNumber);
|
||||
m_adcGraphDialog->show();
|
||||
@@ -585,13 +561,31 @@ void DebugTerminalDialog::openAdc(int boardID, int teNumber)
|
||||
m_adcGraphDialog->activateWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Устанавливает интервал обновления графика в миллисекундах.
|
||||
*
|
||||
* Этот метод задает период времени (в миллисекундах), с которым график в диалоге
|
||||
* AdcGraphDialog обновляется. Если диалог открыт, вызывает его метод setTimeout.
|
||||
*
|
||||
* @param milliseconds Интервал обновления графика в миллисекундах.
|
||||
*/
|
||||
void DebugTerminalDialog::setGraphUpdateInterval(int milliseconds)
|
||||
{
|
||||
if (m_adcGraphDialog) {
|
||||
if(m_adcGraphDialog)
|
||||
m_adcGraphDialog->setTimeout(milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Записывает значение коила для указанной платы.
|
||||
*
|
||||
* Этот метод выбирает группу элементов в интерфейсе в зависимости от идентификатора платы (boardID),
|
||||
* проверяет, что выбранная плата активна (enabled), и затем сообщает о смене значения коила через сигнал.
|
||||
* Перед этим выводит диагностическое сообщение в лог.
|
||||
*
|
||||
* @param boardID Идентификатор платы (0-3).
|
||||
* @param coil Номер коила, который необходимо изменить.
|
||||
* @param value Новое значение для коила.
|
||||
*/
|
||||
void DebugTerminalDialog::writeCoil(int boardID, int coil, int value)
|
||||
{
|
||||
QGroupBox* boardGroup = nullptr;
|
||||
@@ -613,6 +607,16 @@ void DebugTerminalDialog::writeTENumber(int boardId, int teNumber)
|
||||
m_adcGraphDialog->setTENumber(boardId, teNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Устанавливает доступность (активность) для выбранной платы.
|
||||
*
|
||||
* Этот метод выбирает соответствующий QGroupBox для указанной платы (boardID),
|
||||
* включает или отключает его в зависимости от булевого флага active.
|
||||
* Также при деактивации меняет цвет текста на серый для визуального представления.
|
||||
*
|
||||
* @param boardID Идентификатор платы (0-3).
|
||||
* @param active Булевое значение, определяющее активность (true — активировать, false — деактивировать).
|
||||
*/
|
||||
void DebugTerminalDialog::setBoardActive(int boardID, bool active)
|
||||
{
|
||||
// Получаем групбокс для указанной платы
|
||||
@@ -627,20 +631,18 @@ void DebugTerminalDialog::setBoardActive(int boardID, bool active)
|
||||
if(boardGroup) {
|
||||
boardGroup->setEnabled(active);
|
||||
// Можно добавить визуальное отличие неактивных плат
|
||||
if (!active) {
|
||||
if(!active)
|
||||
boardGroup->setStyleSheet("QGroupBox { color: gray; }");
|
||||
} else {
|
||||
else
|
||||
boardGroup->setStyleSheet(""); // Сброс стиля
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTerminalDialog::updateBoardStates(bool activeBoards[4])
|
||||
{
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for(int i = 0; i < 4; i++)
|
||||
setBoardActive(i, activeBoards[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void DebugTerminalDialog::resetAll()
|
||||
{
|
||||
@@ -651,17 +653,14 @@ void DebugTerminalDialog::resetAll()
|
||||
ui->resetKeyCallChkBox_1->setChecked(false);
|
||||
ui->resetDefaultCallChkBox_1->setChecked(false);
|
||||
ui->getHardfaultCallChkBox_1->setChecked(false);
|
||||
|
||||
ui->enableLedTestChkBox_1->setChecked(false);
|
||||
ui->leds_1->setEnabled(false);
|
||||
ui->discs_1->setEnabled(false);
|
||||
|
||||
ui->discWorkTestChkBox_1->setChecked(false);
|
||||
// Сброс всех чекбоксов теста дискретных сигналов
|
||||
ui->discWorkTestChkBox_1->setChecked(false);
|
||||
ui->discWarnTestChkBox_1->setChecked(false);
|
||||
ui->discErrTestChkBox_1->setChecked(false);
|
||||
|
||||
// Сброс всех чекбоксов теста светодиодов
|
||||
ui->ledWorkTestChkBox_1->setChecked(false);
|
||||
ui->ledWarnTestChkBox_1->setChecked(false);
|
||||
@@ -670,7 +669,6 @@ void DebugTerminalDialog::resetAll()
|
||||
ui->ledVH1TestChkBox_1->setChecked(false);
|
||||
ui->ledVH2TestChkBox_1->setChecked(false);
|
||||
ui->ledVH3TestChkBox_1->setChecked(false);
|
||||
|
||||
// Сброс всех чекбоксов вызова функций
|
||||
ui->continiusCallChkBox_2->setChecked(false);
|
||||
ui->calibrateCallChkBox_2->setChecked(false);
|
||||
@@ -678,17 +676,14 @@ void DebugTerminalDialog::resetAll()
|
||||
ui->resetKeyCallChkBox_2->setChecked(false);
|
||||
ui->resetDefaultCallChkBox_2->setChecked(false);
|
||||
ui->getHardfaultCallChkBox_2->setChecked(false);
|
||||
|
||||
ui->enableLedTestChkBox_2->setChecked(false);
|
||||
ui->leds_2->setEnabled(false);
|
||||
ui->discs_2->setEnabled(false);
|
||||
|
||||
ui->discWorkTestChkBox_2->setChecked(false);
|
||||
// Сброс всех чекбоксов теста дискретных сигналов
|
||||
ui->discWorkTestChkBox_2->setChecked(false);
|
||||
ui->discWarnTestChkBox_2->setChecked(false);
|
||||
ui->discErrTestChkBox_2->setChecked(false);
|
||||
|
||||
// Сброс всех чекбоксов теста светодиодов
|
||||
ui->ledWorkTestChkBox_2->setChecked(false);
|
||||
ui->ledWarnTestChkBox_2->setChecked(false);
|
||||
@@ -697,7 +692,6 @@ void DebugTerminalDialog::resetAll()
|
||||
ui->ledVH1TestChkBox_2->setChecked(false);
|
||||
ui->ledVH2TestChkBox_2->setChecked(false);
|
||||
ui->ledVH3TestChkBox_2->setChecked(false);
|
||||
|
||||
// Сброс всех чекбоксов вызова функций
|
||||
ui->continiusCallChkBox_3->setChecked(false);
|
||||
ui->calibrateCallChkBox_3->setChecked(false);
|
||||
@@ -705,17 +699,14 @@ void DebugTerminalDialog::resetAll()
|
||||
ui->resetKeyCallChkBox_3->setChecked(false);
|
||||
ui->resetDefaultCallChkBox_3->setChecked(false);
|
||||
ui->getHardfaultCallChkBox_3->setChecked(false);
|
||||
|
||||
ui->enableLedTestChkBox_3->setChecked(false);
|
||||
ui->leds_3->setEnabled(false);
|
||||
ui->discs_3->setEnabled(false);
|
||||
|
||||
ui->discWorkTestChkBox_3->setChecked(false);
|
||||
// Сброс всех чекбоксов теста дискретных сигналов
|
||||
ui->discWorkTestChkBox_3->setChecked(false);
|
||||
ui->discWarnTestChkBox_3->setChecked(false);
|
||||
ui->discErrTestChkBox_3->setChecked(false);
|
||||
|
||||
// Сброс всех чекбоксов теста светодиодов
|
||||
ui->ledWorkTestChkBox_3->setChecked(false);
|
||||
ui->ledWarnTestChkBox_3->setChecked(false);
|
||||
@@ -724,7 +715,6 @@ void DebugTerminalDialog::resetAll()
|
||||
ui->ledVH1TestChkBox_3->setChecked(false);
|
||||
ui->ledVH2TestChkBox_3->setChecked(false);
|
||||
ui->ledVH3TestChkBox_3->setChecked(false);
|
||||
|
||||
// Сброс всех чекбоксов вызова функций
|
||||
ui->continiusCallChkBox_4->setChecked(false);
|
||||
ui->calibrateCallChkBox_4->setChecked(false);
|
||||
@@ -732,17 +722,14 @@ void DebugTerminalDialog::resetAll()
|
||||
ui->resetKeyCallChkBox_4->setChecked(false);
|
||||
ui->resetDefaultCallChkBox_4->setChecked(false);
|
||||
ui->getHardfaultCallChkBox_4->setChecked(false);
|
||||
|
||||
ui->enableLedTestChkBox_4->setChecked(false);
|
||||
ui->leds_4->setEnabled(false);
|
||||
ui->discs_4->setEnabled(false);
|
||||
|
||||
ui->discWorkTestChkBox_4->setChecked(false);
|
||||
// Сброс всех чекбоксов теста дискретных сигналов
|
||||
ui->discWorkTestChkBox_4->setChecked(false);
|
||||
ui->discWarnTestChkBox_4->setChecked(false);
|
||||
ui->discErrTestChkBox_4->setChecked(false);
|
||||
|
||||
// Сброс всех чекбоксов теста светодиодов
|
||||
ui->ledWorkTestChkBox_4->setChecked(false);
|
||||
ui->ledWarnTestChkBox_4->setChecked(false);
|
||||
@@ -757,13 +744,10 @@ void DebugTerminalDialog::boardDebugReading(int boardID)
|
||||
{
|
||||
if(mainTerm == nullptr)
|
||||
return;
|
||||
|
||||
if(!boards[boardID].isActive)
|
||||
return;
|
||||
|
||||
if(!this->isVisible())
|
||||
return;
|
||||
|
||||
QModbusReply *_24V = mainTerm->readSingleCoil(boardID, 603);
|
||||
if(_24V != nullptr)
|
||||
connect(_24V, &QModbusReply::finished, this, [this, boardID, _24V]() {
|
||||
@@ -771,7 +755,6 @@ void DebugTerminalDialog::boardDebugReading(int boardID)
|
||||
boards[boardID].error24V->setChecked(_24V->result().value(0));
|
||||
_24V->deleteLater();
|
||||
});
|
||||
|
||||
QModbusReply *_5V = mainTerm->readSingleCoil(boardID, 604);
|
||||
if(_5V != nullptr)
|
||||
connect(_5V, &QModbusReply::finished, this, [this, boardID, _5V]() {
|
||||
@@ -779,7 +762,6 @@ void DebugTerminalDialog::boardDebugReading(int boardID)
|
||||
boards[boardID].error5V->setChecked(_5V->result().value(0));
|
||||
_5V->deleteLater();
|
||||
});
|
||||
|
||||
QModbusReply *_5VSCI = mainTerm->readSingleCoil(boardID, 605);
|
||||
if(_5VSCI != nullptr)
|
||||
connect(_5VSCI, &QModbusReply::finished, this, [this, boardID, _5VSCI]() {
|
||||
@@ -787,7 +769,6 @@ void DebugTerminalDialog::boardDebugReading(int boardID)
|
||||
boards[boardID].error5VSCI->setChecked(_5VSCI->result().value(0));
|
||||
_5VSCI->deleteLater();
|
||||
});
|
||||
|
||||
QModbusReply *_5VA = mainTerm->readSingleCoil(boardID, 606);
|
||||
if(_5VA != nullptr)
|
||||
connect(_5VA, &QModbusReply::finished, this, [this, boardID, _5VA]() {
|
||||
@@ -805,7 +786,5 @@ void DebugTerminalDialog::setScanBoardActive(bool flag, int boardID)
|
||||
void DebugTerminalDialog::offAllBoard()
|
||||
{
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
boards[i].isActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,6 @@ class AdcGraphDialog;
|
||||
|
||||
#define REGISTER_TE_NUMB 564
|
||||
|
||||
|
||||
|
||||
namespace Ui {
|
||||
class DebugTerminalDialog;
|
||||
}
|
||||
@@ -54,7 +52,6 @@ class DebugTerminalDialog;
|
||||
class DebugTerminalDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DebugTerminalDialog(QWidget *parent = nullptr);
|
||||
~DebugTerminalDialog();
|
||||
@@ -68,21 +65,15 @@ public:
|
||||
void writeTENumber(int boardId, int teNumber);
|
||||
void openAdc(int boardID, int teNumber);
|
||||
void setMainTerm(M3KTE* term);
|
||||
|
||||
public slots:
|
||||
void boardDebugReading(int boardID);
|
||||
void setScanBoardActive(bool flag, int boardID);
|
||||
void offAllBoard();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
private slots:
|
||||
|
||||
|
||||
void on_buttonBox_clicked(QAbstractButton *button);
|
||||
|
||||
// Плата 1
|
||||
void on_continiusCallChkBox_1_stateChanged(int state);
|
||||
void on_calibrateCallChkBox_1_stateChanged(int state);
|
||||
@@ -90,9 +81,7 @@ private slots:
|
||||
void on_resetKeyCallChkBox_1_stateChanged(int state);
|
||||
void on_resetDefaultCallChkBox_1_stateChanged(int state);
|
||||
void on_getHardfaultCallChkBox_1_stateChanged(int state);
|
||||
|
||||
void on_enableLedTestChkBox_1_stateChanged(int state);
|
||||
|
||||
void on_discWorkTestChkBox_1_stateChanged(int state);
|
||||
void on_discWarnTestChkBox_1_stateChanged(int state);
|
||||
void on_discErrTestChkBox_1_stateChanged(int state);
|
||||
@@ -100,7 +89,6 @@ private slots:
|
||||
void on_discErr5TestChkBox_1_stateChanged(int state);
|
||||
void on_discErr5VsciTestChkBox_1_stateChanged(int state);
|
||||
void on_discErr5VATestChkBox_1_stateChanged(int state);
|
||||
|
||||
void on_ledWorkTestChkBox_1_stateChanged(int state);
|
||||
void on_ledWarnTestChkBox_1_stateChanged(int state);
|
||||
void on_ledErrTestChkBox_1_stateChanged(int state);
|
||||
@@ -108,7 +96,6 @@ private slots:
|
||||
void on_ledVH1TestChkBox_1_stateChanged(int state);
|
||||
void on_ledVH2TestChkBox_1_stateChanged(int state);
|
||||
void on_ledVH3TestChkBox_1_stateChanged(int state);
|
||||
|
||||
// Плата 2
|
||||
void on_continiusCallChkBox_2_stateChanged(int state);
|
||||
void on_calibrateCallChkBox_2_stateChanged(int state);
|
||||
@@ -116,9 +103,7 @@ private slots:
|
||||
void on_resetKeyCallChkBox_2_stateChanged(int state);
|
||||
void on_resetDefaultCallChkBox_2_stateChanged(int state);
|
||||
void on_getHardfaultCallChkBox_2_stateChanged(int state);
|
||||
|
||||
void on_enableLedTestChkBox_2_stateChanged(int state);
|
||||
|
||||
void on_discWorkTestChkBox_2_stateChanged(int state);
|
||||
void on_discWarnTestChkBox_2_stateChanged(int state);
|
||||
void on_discErrTestChkBox_2_stateChanged(int state);
|
||||
@@ -126,7 +111,6 @@ private slots:
|
||||
void on_discErr5TestChkBox_2_stateChanged(int state);
|
||||
void on_discErr5VsciTestChkBox_2_stateChanged(int state);
|
||||
void on_discErr5VATestChkBox_2_stateChanged(int state);
|
||||
|
||||
void on_ledWorkTestChkBox_2_stateChanged(int state);
|
||||
void on_ledWarnTestChkBox_2_stateChanged(int state);
|
||||
void on_ledErrTestChkBox_2_stateChanged(int state);
|
||||
@@ -134,8 +118,6 @@ private slots:
|
||||
void on_ledVH1TestChkBox_2_stateChanged(int state);
|
||||
void on_ledVH2TestChkBox_2_stateChanged(int state);
|
||||
void on_ledVH3TestChkBox_2_stateChanged(int state);
|
||||
|
||||
|
||||
// Плата 3
|
||||
void on_continiusCallChkBox_3_stateChanged(int state);
|
||||
void on_calibrateCallChkBox_3_stateChanged(int state);
|
||||
@@ -143,9 +125,7 @@ private slots:
|
||||
void on_resetKeyCallChkBox_3_stateChanged(int state);
|
||||
void on_resetDefaultCallChkBox_3_stateChanged(int state);
|
||||
void on_getHardfaultCallChkBox_3_stateChanged(int state);
|
||||
|
||||
void on_enableLedTestChkBox_3_stateChanged(int state);
|
||||
|
||||
void on_discWorkTestChkBox_3_stateChanged(int state);
|
||||
void on_discWarnTestChkBox_3_stateChanged(int state);
|
||||
void on_discErrTestChkBox_3_stateChanged(int state);
|
||||
@@ -153,7 +133,6 @@ private slots:
|
||||
void on_discErr5TestChkBox_3_stateChanged(int state);
|
||||
void on_discErr5VsciTestChkBox_3_stateChanged(int state);
|
||||
void on_discErr5VATestChkBox_3_stateChanged(int state);
|
||||
|
||||
void on_ledWorkTestChkBox_3_stateChanged(int state);
|
||||
void on_ledWarnTestChkBox_3_stateChanged(int state);
|
||||
void on_ledErrTestChkBox_3_stateChanged(int state);
|
||||
@@ -161,7 +140,6 @@ private slots:
|
||||
void on_ledVH1TestChkBox_3_stateChanged(int state);
|
||||
void on_ledVH2TestChkBox_3_stateChanged(int state);
|
||||
void on_ledVH3TestChkBox_3_stateChanged(int state);
|
||||
|
||||
// Плата 4
|
||||
void on_continiusCallChkBox_4_stateChanged(int state);
|
||||
void on_calibrateCallChkBox_4_stateChanged(int state);
|
||||
@@ -169,9 +147,7 @@ private slots:
|
||||
void on_resetKeyCallChkBox_4_stateChanged(int state);
|
||||
void on_resetDefaultCallChkBox_4_stateChanged(int state);
|
||||
void on_getHardfaultCallChkBox_4_stateChanged(int state);
|
||||
|
||||
void on_enableLedTestChkBox_4_stateChanged(int state);
|
||||
|
||||
void on_discWorkTestChkBox_4_stateChanged(int state);
|
||||
void on_discWarnTestChkBox_4_stateChanged(int state);
|
||||
void on_discErrTestChkBox_4_stateChanged(int state);
|
||||
@@ -179,7 +155,6 @@ private slots:
|
||||
void on_discErr5TestChkBox_4_stateChanged(int state);
|
||||
void on_discErr5VsciTestChkBox_4_stateChanged(int state);
|
||||
void on_discErr5VATestChkBox_4_stateChanged(int state);
|
||||
|
||||
void on_ledWorkTestChkBox_4_stateChanged(int state);
|
||||
void on_ledWarnTestChkBox_4_stateChanged(int state);
|
||||
void on_ledErrTestChkBox_4_stateChanged(int state);
|
||||
@@ -187,25 +162,20 @@ private slots:
|
||||
void on_ledVH1TestChkBox_4_stateChanged(int state);
|
||||
void on_ledVH2TestChkBox_4_stateChanged(int state);
|
||||
void on_ledVH3TestChkBox_4_stateChanged(int state);
|
||||
|
||||
signals:
|
||||
void coilValueChanged(int boardID, int coil, int value);
|
||||
void writeRegister(int boardID, int reg, int value);
|
||||
void readCoil(int boardID, int coil, QModbusReply *reply);
|
||||
|
||||
private:
|
||||
Ui::DebugTerminalDialog *ui;
|
||||
QModbusClient *m_modbusDevice; // Храним указатель здесь
|
||||
M3KTE* mainTerm = nullptr;
|
||||
|
||||
// Карты для хранения состояний
|
||||
QMap<int, bool> m_functionCalls; // boardId -> coil -> state
|
||||
QMap<int, bool> m_discreteTests; // boardId -> coil -> state
|
||||
QMap<int, bool> m_ledTests; // boardId -> coil -> state
|
||||
|
||||
// Номера ТЭ для каждой платы
|
||||
int m_teNumbers[4] = {0, 0, 0, 0};
|
||||
|
||||
struct boardErrorLinks{
|
||||
bool isActive = false;
|
||||
QCheckBox* error24V = nullptr;
|
||||
@@ -214,9 +184,7 @@ private:
|
||||
QCheckBox* error5VA = nullptr;
|
||||
};
|
||||
boardErrorLinks boards[4];
|
||||
|
||||
void initializeConnections();
|
||||
|
||||
void writeCoil(int boardID, int coil, int value);
|
||||
//void readCoil(int coil);
|
||||
void resetAll();
|
||||
|
||||
@@ -10,18 +10,15 @@ DeviceSettingsDialog::DeviceSettingsDialog(QWidget *parent) :
|
||||
{
|
||||
ui->setupUi(this);
|
||||
on_buttonApplyChangeTimer_clicked();
|
||||
{
|
||||
_m_timer[0] = ui->spinTimerBoard_1;
|
||||
_m_timer[1] = ui->spinTimerBoard_2;
|
||||
_m_timer[2] = ui->spinTimerBoard_3;
|
||||
_m_timer[3] = ui->spinTimerBoard_4;
|
||||
}
|
||||
_currentSpeed = ui->speedBox->currentText().toUInt();
|
||||
_currentParity = ui->parityBox->currentIndex();
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int i = 0; i < 4; i++)
|
||||
_currentAdrs[i] = i+1;
|
||||
}
|
||||
}
|
||||
|
||||
DeviceSettingsDialog::~DeviceSettingsDialog()
|
||||
{
|
||||
@@ -82,10 +79,9 @@ void DeviceSettingsDialog::updateSettingsAfterConnection(unsigned tmp_speed, uns
|
||||
_m_timer[i]->setEnabled(true);
|
||||
ui->idComboBox->addItem(QString::number(i));
|
||||
_currentAdrs[i] = tmp_adr[i];
|
||||
} else {
|
||||
} else
|
||||
_m_timer[i]->setEnabled(false);
|
||||
}
|
||||
}
|
||||
on_idComboBox_currentIndexChanged(ui->idComboBox->currentIndex());
|
||||
}
|
||||
|
||||
@@ -107,9 +103,8 @@ void DeviceSettingsDialog::on_buttonBox_clicked(QAbstractButton *button)
|
||||
_currentSpeed = ui->speedBox->currentText().toUInt();
|
||||
ui->parityBox->setCurrentIndex(0);
|
||||
_currentParity = ui->parityBox->currentIndex();
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int i = 0; i < 4; i++)
|
||||
_currentAdrs[i] = i + 1;
|
||||
}
|
||||
ui->adrSpinBox->setValue(_currentAdrs[ui->idComboBox->currentIndex()]);
|
||||
break;
|
||||
case QDialogButtonBox::AcceptRole:
|
||||
|
||||
@@ -12,10 +12,8 @@ class BoardIdHasBeenChanged : public QEvent
|
||||
public:
|
||||
BoardIdHasBeenChanged(const short num, const short newId) : QEvent(QEvent::User) {_BoardNum = num; _BoardNewID = newId;}
|
||||
~BoardIdHasBeenChanged() {}
|
||||
|
||||
short BoardNum() const {return _BoardNum;}
|
||||
short BoardNewID() const {return _BoardNewID;}
|
||||
|
||||
private:
|
||||
short _BoardNum;
|
||||
short _BoardNewID;
|
||||
@@ -26,10 +24,8 @@ class pollStatusChange : public QEvent
|
||||
public:
|
||||
pollStatusChange(unsigned BoardID, bool Stat) : QEvent((QEvent::Type)1001) {_BoardID = BoardID; _Status = Stat;}
|
||||
~pollStatusChange() {}
|
||||
|
||||
unsigned BoardID() const {return _BoardID;}
|
||||
bool Status() const {return _Status;}
|
||||
|
||||
private:
|
||||
unsigned _BoardID;
|
||||
bool _Status;
|
||||
@@ -42,11 +38,9 @@ class DeviceSettingsDialog;
|
||||
class DeviceSettingsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeviceSettingsDialog(QWidget *parent = nullptr);
|
||||
~DeviceSettingsDialog();
|
||||
|
||||
unsigned currentBoardTimer(unsigned short _ID);
|
||||
unsigned currentSpeed();
|
||||
unsigned short currentParity();
|
||||
@@ -58,30 +52,19 @@ public:
|
||||
signals:
|
||||
void parityChanged();
|
||||
void speedChanged();
|
||||
|
||||
void firstBoardAdrHasBeenChanged();
|
||||
void secondBoardAdrHasBeenChanged();
|
||||
void thirdBoardAdrHasBeenChanged();
|
||||
void fourthBoardAdrHasBeenChanged();
|
||||
|
||||
private slots:
|
||||
|
||||
void on_buttonApplyChangeTimer_clicked();
|
||||
|
||||
void on_buttonApplyChangeSpeed_clicked();
|
||||
|
||||
void on_buttonApplyChangeParity_clicked();
|
||||
|
||||
void on_buttonApplyChangeAdr_clicked();
|
||||
|
||||
void on_idComboBox_currentIndexChanged(int index);
|
||||
|
||||
void on_buttonBox_clicked(QAbstractButton *button);
|
||||
|
||||
void on_buttonApplyChangePoll_clicked();
|
||||
|
||||
void on_idPollComboBox_currentIndexChanged(int index);
|
||||
|
||||
private:
|
||||
QSpinBox *_m_timer[4];
|
||||
unsigned _currentBoardTimers[4];
|
||||
@@ -89,7 +72,6 @@ private:
|
||||
unsigned short _currentParity;
|
||||
unsigned _currentAdrs[4];
|
||||
bool _currentPollStatus[4];
|
||||
|
||||
Ui::DeviceSettingsDialog *ui;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,11 +6,10 @@ LineRinger::LineRinger(QWidget *parent) :
|
||||
ui(new Ui::LineRinger)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
const auto listPorts = QSerialPortInfo::availablePorts();
|
||||
for (const auto& port: listPorts) {
|
||||
for(const auto& port: listPorts)
|
||||
ui->comBox->addItem(QString(port.portName() + ": " + port.manufacturer()), QVariant(port.portName()));
|
||||
}
|
||||
//чётность
|
||||
{
|
||||
ui->parityControlBox->addItem("No", QVariant(QSerialPort::NoParity));
|
||||
ui->parityControlBox->addItem("Even", QVariant(QSerialPort::EvenParity));
|
||||
@@ -18,6 +17,7 @@ LineRinger::LineRinger(QWidget *parent) :
|
||||
ui->parityControlBox->addItem("Space", QVariant(QSerialPort::SpaceParity));
|
||||
ui->parityControlBox->addItem("Mark", QVariant(QSerialPort::MarkParity));
|
||||
}
|
||||
//данные
|
||||
{
|
||||
ui->dataBox->addItem("Data5", QVariant(QSerialPort::Data5));
|
||||
ui->dataBox->addItem("Data6", QVariant(QSerialPort::Data6));
|
||||
@@ -25,6 +25,7 @@ LineRinger::LineRinger(QWidget *parent) :
|
||||
ui->dataBox->addItem("Data8", QVariant(QSerialPort::Data8));
|
||||
ui->dataBox->setCurrentIndex(3);
|
||||
}
|
||||
//стопбиты
|
||||
{
|
||||
ui->stopBox->addItem("One", QVariant(QSerialPort::OneStop));
|
||||
ui->stopBox->addItem("OneAndHalf", QVariant(QSerialPort::OneAndHalfStop));
|
||||
@@ -33,9 +34,7 @@ LineRinger::LineRinger(QWidget *parent) :
|
||||
ui->deviceOnlineView->horizontalHeader()->setVisible(true);
|
||||
syncColumnHeaders();
|
||||
ui->deviceOnlineView->setColumnHidden(1, true);
|
||||
|
||||
ui->ringButton->setEnabled(false);
|
||||
|
||||
modbusDevice = new QModbusRtuSerialMaster(this);
|
||||
}
|
||||
|
||||
@@ -46,14 +45,41 @@ LineRinger::~LineRinger()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обновляет заголовки столбцов таблицы устройств.
|
||||
*
|
||||
* Этот метод устанавливает список заголовков для таблицы отображения устройств
|
||||
* в интерфейсе и затем автоматически изменяет ширину колонок под содержимое.
|
||||
*/
|
||||
void LineRinger::syncColumnHeaders()
|
||||
{
|
||||
// Создаём список заголовков колонок
|
||||
QStringList headers;
|
||||
headers << "ID" << "BaudRate" << "Vendor Name" << "Product Code" << "Major Minor Revision" << "Vendor Url" << "Product Name" << "Model Name" << "User Application Name" << "Примечание";
|
||||
headers << "ID"
|
||||
<< "BaudRate"
|
||||
<< "Vendor Name"
|
||||
<< "Product Code"
|
||||
<< "Major Minor Revision"
|
||||
<< "Vendor Url"
|
||||
<< "Product Name"
|
||||
<< "Model Name"
|
||||
<< "User Application Name"
|
||||
<< "Примечание";
|
||||
|
||||
// Устанавливаем заголовки для таблицы
|
||||
ui->deviceOnlineView->setHorizontalHeaderLabels(headers);
|
||||
// Автоматически подгоняем ширину колонок под содержимое
|
||||
ui->deviceOnlineView->resizeColumnsToContents();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработчик нажатия кнопки "Подключить/Отключить".
|
||||
*
|
||||
* Этот слот управляет подключением или отключением модбус-устройства.
|
||||
* При отсутствии подключения он выполняет настройку параметров соединения,
|
||||
* инициирует подключение и обновляет интерфейс в зависимости от результата.
|
||||
* Если устройство уже подключено, происходит отключение и обновление интерфейса.
|
||||
*/
|
||||
void LineRinger::on_connectButton_clicked()
|
||||
{
|
||||
if(!modbusDevice)
|
||||
@@ -73,9 +99,9 @@ void LineRinger::on_connectButton_clicked()
|
||||
#endif
|
||||
modbusDevice->setTimeout(50);
|
||||
modbusDevice->setNumberOfRetries(0);
|
||||
if(!modbusDevice->connectDevice()) {
|
||||
if(!modbusDevice->connectDevice())
|
||||
QMessageBox::warning(this, "Ошибка", "Произошла ошибка при попытке подключения.");
|
||||
} else {
|
||||
else {
|
||||
ui->connectButton->setText(tr("Отключить"));
|
||||
ui->ringButton->setEnabled(true);
|
||||
currentBaudRate = ui->baudRateBox->currentText().toUInt(nullptr, 10);
|
||||
@@ -143,17 +169,16 @@ LineRinger::callStatus LineRinger::lineCall()
|
||||
for(int tmp_obj = 0; tmp_obj < numOfObject; tmp_obj++) {
|
||||
uint8_t objectID = result.at(0);
|
||||
uint8_t lengthOfObject = result.at(1);
|
||||
if(lengthOfObject>0) {
|
||||
if(lengthOfObject > 0)
|
||||
currentDevice.fields[objectID].clear();
|
||||
}
|
||||
for(int i = 0; i < lengthOfObject; i++) {
|
||||
for(int i = 0; i < lengthOfObject; i++)
|
||||
currentDevice.fields[objectID] += QString(result.at(2 + i));
|
||||
}
|
||||
result.remove(0, lengthOfObject + 2);
|
||||
}
|
||||
auto *regularReply = modbusDevice->sendRawRequest(readDeviceRegularIdentification, tmp_adr);
|
||||
if(regularReply == nullptr) {
|
||||
QMessageBox::warning(this, "Ошибка при сканировании.", QString("%1: %2").arg(modbusDevice->error()).arg(modbusDevice->errorString()));
|
||||
QMessageBox::warning(this, "Ошибка при сканировании.",
|
||||
QString("%1: %2").arg(modbusDevice->error()).arg(modbusDevice->errorString()));
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
return callStatus::ERROR;
|
||||
@@ -183,12 +208,10 @@ LineRinger::callStatus LineRinger::lineCall()
|
||||
if(objectID > 0x06)
|
||||
continue;
|
||||
uint8_t lengthOfObject = regularResult.at(1);
|
||||
if(lengthOfObject>0) {
|
||||
if(lengthOfObject > 0)
|
||||
currentDevice.fields[objectID].clear();
|
||||
}
|
||||
for (int i = 0; i < lengthOfObject; i++) {
|
||||
for(int i = 0; i < lengthOfObject; i++)
|
||||
currentDevice.fields[objectID] += QString(regularResult.at(2 + i));
|
||||
}
|
||||
regularResult.remove(0, lengthOfObject + 2);
|
||||
}
|
||||
}
|
||||
@@ -199,14 +222,12 @@ LineRinger::callStatus LineRinger::lineCall()
|
||||
ui->deviceOnlineView->insertRow(newRow);
|
||||
ui->deviceOnlineView->setItem(newRow, 0, new QTableWidgetItem(QString::number(currentDevice.adr)));
|
||||
ui->deviceOnlineView->setItem(newRow, 1, new QTableWidgetItem(QString::number(currentDevice.baudRate)));
|
||||
for (int i = 0; i < 7; i++) {
|
||||
for(int i = 0; i < 7; i++)
|
||||
ui->deviceOnlineView->setItem(newRow, i + 2, new QTableWidgetItem(currentDevice.fields[i]));
|
||||
}
|
||||
if(reply->error()!=QModbusDevice::NoError) {
|
||||
if(reply->error()!=QModbusDevice::NoError)
|
||||
ui->deviceOnlineView->setItem(newRow, 9, new QTableWidgetItem(QString("%1: %2").arg(reply->error()).arg(reply->errorString())));
|
||||
} else if(regularReplyError) {
|
||||
else if(regularReplyError)
|
||||
ui->deviceOnlineView->setItem(newRow, 9, new QTableWidgetItem(regularReplyErrorString));
|
||||
}
|
||||
ui->deviceOnlineView->resizeColumnsToContents();
|
||||
syncColumnHeaders();
|
||||
}
|
||||
@@ -216,6 +237,16 @@ LineRinger::callStatus LineRinger::lineCall()
|
||||
return callStatus::NOERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработчик нажатия кнопки "Опросить" для устройства LineRinger.
|
||||
*
|
||||
* Этот слот очищает текущий список устройств, обновляет интерфейс, а затем,
|
||||
* в случае автоматического определения скорости передачи (isAutoBaud),
|
||||
* последовательно пытается подключиться к устройствам с различными скоростями.
|
||||
* В процессе поиска отображается прогресс-бар, который можно отменить.
|
||||
* Если обнаружен вызываемый ответ или возникает ошибка, происходит завершение поиска.
|
||||
* В случае ручного режима поиска, выполняется однократная попытка сканирования.
|
||||
*/
|
||||
void LineRinger::on_ringButton_clicked()
|
||||
{
|
||||
ui->deviceOnlineView->clear();
|
||||
@@ -248,7 +279,8 @@ void LineRinger::on_ringButton_clicked()
|
||||
currentBaudRate = ui->baudRateBox->itemText(i).toUInt(nullptr, 10);
|
||||
bar->setLabelText(tr("Поиск устройств... Текущая скорость: %1").arg(currentBaudRate));
|
||||
if(lineCall() == callStatus::INTERRUPT) {
|
||||
QMessageBox::warning(this, "Уведомление", QString("Досрочное завершение опроса. Найдено %1 устройств.").arg(devicesList.count()));
|
||||
QMessageBox::warning(this, "Уведомление",
|
||||
QString("Досрочное завершение опроса. Найдено %1 устройств.").arg(devicesList.count()));
|
||||
modbusDevice->disconnectDevice();
|
||||
on_connectButton_clicked();
|
||||
bar->close();
|
||||
@@ -262,9 +294,9 @@ void LineRinger::on_ringButton_clicked()
|
||||
on_connectButton_clicked();
|
||||
} else {
|
||||
ui->deviceOnlineView->setColumnHidden(1, true);
|
||||
if(lineCall() == callStatus::INTERRUPT) {
|
||||
QMessageBox::warning(this, "Уведомление", QString("Досрочное завершение опроса. Найдено %1 устройств.").arg(devicesList.count()));
|
||||
}
|
||||
if(lineCall() == callStatus::INTERRUPT)
|
||||
QMessageBox::warning(this, "Уведомление",
|
||||
QString("Досрочное завершение опроса. Найдено %1 устройств.").arg(devicesList.count()));
|
||||
}
|
||||
ui->timer->setTime(QTime::currentTime());
|
||||
ui->timer->setDate(QDate::currentDate());
|
||||
|
||||
@@ -16,35 +16,25 @@ class LineRinger;
|
||||
class LineRinger : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum callStatus{
|
||||
NOERROR = 0,
|
||||
ERROR = 1,
|
||||
INTERRUPT = 2
|
||||
};
|
||||
|
||||
explicit LineRinger(QWidget *parent = nullptr);
|
||||
~LineRinger();
|
||||
callStatus lineCall();
|
||||
|
||||
signals:
|
||||
void stopLineCall();
|
||||
|
||||
private slots:
|
||||
void on_connectButton_clicked();
|
||||
|
||||
void on_ringButton_clicked();
|
||||
|
||||
void on_checkAutoBaud_stateChanged(int arg1);
|
||||
|
||||
private:
|
||||
Ui::LineRinger *ui;
|
||||
|
||||
void syncColumnHeaders();
|
||||
|
||||
struct deviceOnLine
|
||||
{
|
||||
struct deviceOnLine{
|
||||
uint8_t adr;
|
||||
unsigned baudRate;
|
||||
QString fields[7] = {"Undefined", "Undefined", "Undefined", "Undefined", "Undefined", "Undefined", "Undefined"};
|
||||
@@ -52,7 +42,6 @@ private:
|
||||
QVector<deviceOnLine>devicesList;
|
||||
bool isAutoBaud = false;
|
||||
unsigned currentBaudRate;
|
||||
|
||||
QModbusClient *modbusDevice = nullptr;
|
||||
};
|
||||
|
||||
|
||||
@@ -396,7 +396,6 @@ M3KTE::M3KTE(QWidget *parent)
|
||||
}
|
||||
bool activeBoards[4] = {false, false, false, false};
|
||||
m_debugTerminalDialog->updateBoardStates(activeBoards);
|
||||
|
||||
ui->M3kteRegSettings->setEnabled(false);
|
||||
ui->BSM_Warning->setEnabled(false);
|
||||
ui->BSM_Accident->setEnabled(false);
|
||||
@@ -437,7 +436,6 @@ M3KTE::M3KTE(QWidget *parent)
|
||||
Boards[2].localState[LOCAL_STATE_POLL] = ui->localPollChkBox_3;
|
||||
Boards[3].localState[LOCAL_STATE_POLL] = ui->localPollChkBox_4;
|
||||
|
||||
|
||||
Boards[0].localState[LOCAL_STATE_WARN] = ui->localWarnChkBox_1;
|
||||
Boards[1].localState[LOCAL_STATE_WARN] = ui->localWarnChkBox_2;
|
||||
Boards[2].localState[LOCAL_STATE_WARN] = ui->localWarnChkBox_3;
|
||||
@@ -447,11 +445,9 @@ M3KTE::M3KTE(QWidget *parent)
|
||||
Boards[1].localState[LOCAL_STATE_ERR] = ui->localErrChkBox_2;
|
||||
Boards[2].localState[LOCAL_STATE_ERR] = ui->localErrChkBox_3;
|
||||
Boards[3].localState[LOCAL_STATE_ERR] = ui->localErrChkBox_4;
|
||||
|
||||
}
|
||||
{ // не кликабельные чекбоксы и радиобоксы
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
for(int i = 0; i < 4; i++) {
|
||||
connect(Boards[i].localState[LOCAL_STATE_POLL], &QCheckBox::clicked,
|
||||
this, [i, this](bool checked) {m_deviceSettingsDialog->sendPollCommand(i, checked);});
|
||||
Boards[i].localState[LOCAL_STATE_WARN]->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
@@ -471,9 +467,8 @@ M3KTE::M3KTE(QWidget *parent)
|
||||
Boards[i].timerStatus->setText(" ");;
|
||||
Boards[i].localError->setText(" ");;
|
||||
}
|
||||
for(int i = 0; i < 5; i++) {
|
||||
for(int i = 0; i < 5; i++)
|
||||
ui->writeValueTable->resizeColumnToContents(i);
|
||||
}
|
||||
QBrush tb(Qt::transparent); // Transparent brush, solid pattern
|
||||
for(int i = 0; i < 320; i++) {
|
||||
m_ProgressBar[i]->setTextVisible(true);
|
||||
@@ -507,12 +502,17 @@ M3KTE::M3KTE(QWidget *parent)
|
||||
|
||||
M3KTE::~M3KTE()
|
||||
{
|
||||
if(modbusDevice->state() == QModbusDevice::ConnectedState) {
|
||||
if(modbusDevice->state() == QModbusDevice::ConnectedState)
|
||||
onConnectClicked();
|
||||
}
|
||||
delete ui;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Инициализация действий и сигналов интерфейса пользователя.
|
||||
*
|
||||
* Этот метод настраивает начальные состояния элементов интерфейса и соединяет
|
||||
* сигналы пользовательских действий с соответствующими слотами для обработки.
|
||||
*/
|
||||
void M3KTE::initActions()
|
||||
{
|
||||
ui->ConnectionMenuConnect->setEnabled(true);
|
||||
@@ -537,12 +537,10 @@ void M3KTE::initActions()
|
||||
m_debugTerminalDialog->setDebugTerminalCoil(1);
|
||||
m_debugTerminalDialog->openAdc(0, 1);
|
||||
});
|
||||
|
||||
connect(m_debugTerminalDialog, &DebugTerminalDialog::coilValueChanged,
|
||||
this, &M3KTE::writeSingleCoil);
|
||||
connect(m_debugTerminalDialog, &DebugTerminalDialog::writeRegister,
|
||||
this, &M3KTE::writeSingleRegister);
|
||||
|
||||
connect(ui->LineCall, &QAction::triggered, m_lineRinger, &QWidget::show);
|
||||
connect(ui->M3kteRegSettings, &QAction::triggered, m_regMultipleSettings, &QDialog::show);
|
||||
connect(m_regMultipleSettings, &MultipleSettings::write, this, &M3KTE::slotmultipleRegWrite);
|
||||
@@ -552,6 +550,17 @@ void M3KTE::initActions()
|
||||
connect(ui->ParameterScan, &QAction::triggered, m_parameterWorkspace, &QWidget::show);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Записывает сообщение об ошибке в таблицу логирования.
|
||||
*
|
||||
* Этот метод добавляет новую строку в таблицу loggerTable с информацией об ошибке,
|
||||
* включая время, место возникновения, описание ошибки, счётчик ошибок и дополнительную информацию.
|
||||
*
|
||||
* @param errorPlace Строка, указывающая место возникновения ошибки.
|
||||
* @param errorString Строка с описанием самой ошибки.
|
||||
* @param errorCount Общее число ошибок этого типа (или другое релевантное значение).
|
||||
* @param description Дополнительное описание ошибки.
|
||||
*/
|
||||
void M3KTE::logError(const QString &errorPlace, const QString &errorString, unsigned errorCount, const QString &description)
|
||||
{
|
||||
unsigned newRow = loggerTable->rowCount();
|
||||
@@ -565,12 +574,19 @@ void M3KTE::logError(const QString &errorPlace, const QString &errorString, unsi
|
||||
if(!loggerTable->verticalScrollBar()->isSliderDown())
|
||||
loggerTable->verticalScrollBar()->setSliderPosition(loggerTable->verticalScrollBar()->maximum());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Очищает содержимое таблицы логирования.
|
||||
*
|
||||
* Этот метод удаляет все строки из таблицы loggerTable. Перед удалением
|
||||
* обновление пользовательского интерфейса временно отключается для повышения производительности.
|
||||
* Если таблица не инициализирована, метод просто завершает работу.
|
||||
*/
|
||||
void M3KTE::clearLogger()
|
||||
{
|
||||
// Проверяем, что таблица инициализирована
|
||||
if (!loggerTable) {
|
||||
if(!loggerTable)
|
||||
return;
|
||||
}
|
||||
// Отключаем обновление UI для повышения производительности
|
||||
loggerTable->setUpdatesEnabled(false);
|
||||
// Очищаем все строки таблицы
|
||||
@@ -579,6 +595,24 @@ void M3KTE::clearLogger()
|
||||
loggerTable->setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработчик нажатия кнопки "Подключиться".
|
||||
*
|
||||
* Эта функция устанавливает соединение с устройством Modbus, если оно не подключено,
|
||||
* или разрывает соединение, если уже подключено.
|
||||
* В процессе подключения происходит настройка параметров соединения (порт, параллельность,
|
||||
* скорость передачи и др.), а также опрос устройств для определения их состояния.
|
||||
* В случае успешного подключения включаются соответствующие элементы интерфейса и
|
||||
* происходит подготовка данных для дальнейшей работы.
|
||||
* В случае разрыва соединения происходит сброс настроек интерфейса и отключение устройств.
|
||||
*
|
||||
* Основные действия:
|
||||
* - Проверка актуальности объекта modbusDevice.
|
||||
* - Настройка параметров соединения и попытка подключения.
|
||||
* - В случае успеха — обновление интерфейса и активация элементов.
|
||||
* - В случае неудачи — отображение сообщения об ошибке.
|
||||
* - При уже активном соединении — отключение устройства, сброс данных и интерфейса.
|
||||
*/
|
||||
void M3KTE::onConnectClicked()
|
||||
{
|
||||
if(!modbusDevice)
|
||||
@@ -599,14 +633,13 @@ void M3KTE::onConnectClicked()
|
||||
#endif
|
||||
modbusDevice->setTimeout(m_settingsDialog->settings().responseTime);
|
||||
modbusDevice->setNumberOfRetries(m_settingsDialog->settings().numberOfRetries);
|
||||
if(!modbusDevice->connectDevice()) {
|
||||
if(!modbusDevice->connectDevice())
|
||||
statusBar()->showMessage(tr("Connect failed: ") + modbusDevice->errorString(), 5000);
|
||||
} else {
|
||||
else {
|
||||
ui->ConnectionMenuConnect->setEnabled(false);
|
||||
ui->ConnectionMenuDisconnect->setEnabled(true);
|
||||
if(pingNetworkDevices()) //Опрос устройст
|
||||
{
|
||||
//m_parameterWorkspace->setDeviceCount(1);
|
||||
//Опрос устройст
|
||||
if(pingNetworkDevices()) {
|
||||
unsigned tmp_adr[4];
|
||||
bool ActiveDevices[4];
|
||||
for(int i = 0; i < 4; i++) {
|
||||
@@ -614,7 +647,10 @@ void M3KTE::onConnectClicked()
|
||||
ActiveDevices[i] = Boards[i].isActive;
|
||||
}
|
||||
ui->M3kteRegSettings->setEnabled(true);
|
||||
m_deviceSettingsDialog->updateSettingsAfterConnection(m_settingsDialog->settings().baud, m_settingsDialog->settings().parity, tmp_adr, ActiveDevices);
|
||||
m_deviceSettingsDialog->updateSettingsAfterConnection(m_settingsDialog->settings().baud,
|
||||
m_settingsDialog->settings().parity,
|
||||
tmp_adr,
|
||||
ActiveDevices);
|
||||
m_debugTerminalDialog->updateBoardStates(ActiveDevices);
|
||||
ui->boardSelectBox->setCurrentIndex(0);
|
||||
ui->writeTable->setCurrentIndex(0);
|
||||
@@ -678,6 +714,13 @@ void M3KTE::onConnectClicked()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработчик нажатия кнопки чтения.
|
||||
*
|
||||
* Этот слот вызывается при нажатии кнопки для чтения данных по протоколу Modbus.
|
||||
* В случае успешного создания запроса, подключает обработчик завершения запроса.
|
||||
* Если произошла ошибка при отправке запроса, выводит сообщение об ошибке в статус-бар.
|
||||
*/
|
||||
void M3KTE::onReadButtonClicked()
|
||||
{
|
||||
if(!modbusDevice)
|
||||
@@ -688,26 +731,31 @@ void M3KTE::onReadButtonClicked()
|
||||
connect(reply, &QModbusReply::finished, this, &M3KTE::onReadReady);
|
||||
else
|
||||
delete reply; // broadcast replies return immediately
|
||||
} else {
|
||||
} else
|
||||
statusBar()->showMessage(tr("Read error: ") + modbusDevice->errorString(), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает завершение ответа на запрос чтения Modbus.
|
||||
*
|
||||
* Этот слот вызывается, когда ответ на запрос Modbus готов. Он получает указатель
|
||||
* на объект QModbusReply, обрабатывает успешный ответ, обновляет состояние устройств
|
||||
* и модели данных, а также выводит сообщения об ошибках при необходимости.
|
||||
*/
|
||||
void M3KTE::onReadReady()
|
||||
{
|
||||
auto reply = qobject_cast<QModbusReply *>(sender());
|
||||
if(!reply)
|
||||
return;
|
||||
int Adr = 255;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int i = 0; i < 4; i++)
|
||||
if(Boards[i].isActive && Boards[i].adr == reply->serverAddress()) {
|
||||
Adr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(reply->error() == QModbusDevice::NoError) {
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
for(int i = 0, total = int(unit.valueCount()); i < total; ++i) {
|
||||
for(int i = 0, total = int(unit.valueCount()); i < total; ++i)
|
||||
if(unit.registerType() == QModbusDataUnit::Coils) {
|
||||
Boards[Adr].coil[i + unit.startAddress()] = unit.value(i);
|
||||
if(unit.value(i) == 1)
|
||||
@@ -718,7 +766,6 @@ void M3KTE::onReadReady()
|
||||
Boards[Adr].HR[i + unit.startAddress()] = unit.value(i);
|
||||
Boards[Adr].ModbusModelHoldingReg->setData(ui->writeValueTable->model()->index(i + unit.startAddress(), 3), QString::number(unit.value(i), 10), Qt::EditRole);
|
||||
}
|
||||
}
|
||||
switch(unit.registerType()) {
|
||||
case QModbusDataUnit::Coils:
|
||||
Boards[Adr].ModbusModelCoil->dataChanged(ui->writeValueTable->model()->index(unit.startAddress(), 2),
|
||||
@@ -749,6 +796,13 @@ void M3KTE::onReadReady()
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает событие нажатия кнопки записи.
|
||||
*
|
||||
* Этот слот собирает данные из моделей, формирует запрос на запись по протоколу Modbus
|
||||
* и отправляет его на устройство. Обрабатывает завершение запроса и отображает
|
||||
* сообщения об ошибках при необходимости.
|
||||
*/
|
||||
void M3KTE::onWriteButtonClicked()
|
||||
{
|
||||
if(!modbusDevice)
|
||||
@@ -786,34 +840,58 @@ void M3KTE::onWriteButtonClicked()
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
} else {
|
||||
} else
|
||||
// broadcast replies return immediately
|
||||
reply->deleteLater();
|
||||
}
|
||||
} else {
|
||||
statusBar()->showMessage(tr("Write error: ") + modbusDevice->errorString(), 5000);
|
||||
logError(tr("Терминал"), modbusDevice->errorString(), 0, "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает изменение выбранной платы.
|
||||
*
|
||||
* Этот метод вызывается при изменении выбора платы пользователем.
|
||||
* Он вызывает changeTable с текущими индексами платы и таблицы.
|
||||
*
|
||||
* @param index Индекс выбранной платы.
|
||||
*/
|
||||
void M3KTE::onSelectedBoardChanged(int index)
|
||||
{
|
||||
changeTable(index, ui->writeTable->currentIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает изменение выбранной таблицы.
|
||||
*
|
||||
* Этот метод вызывается при переключении таблицы пользователем.
|
||||
* Он вызывает changeTable с текущими индексами выбранной платы и таблицы.
|
||||
*
|
||||
* @param index Индекс выбранной таблицы.
|
||||
*/
|
||||
void M3KTE::onWriteTableChanged(int index)
|
||||
{
|
||||
changeTable(ui->boardSelectBox->currentIndex(), index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Частично меняет отображаемую таблицу в зависимости от типа.
|
||||
*
|
||||
* В зависимости от типа таблицы (например, "Реле" или "Регистры") устанавливает
|
||||
* соответствующую модель данных, скрывает или показывает строки и колонки,
|
||||
* а также подгоняет ширину колонок.
|
||||
*
|
||||
* @param board Индекс выбранной платы.
|
||||
* @param tabletype Тип таблицы (0 — Coil, 1 или другой — Holding Registers).
|
||||
*/
|
||||
void M3KTE::changeTable(int board, int tabletype)
|
||||
{
|
||||
if(tabletype == 0) {
|
||||
ui->writeValueTable->setModel(Boards[board].ModbusModelCoil);
|
||||
int i = 0;
|
||||
for(;i<Boards[board].ModbusModelHoldingReg->rowCount();i++) {
|
||||
for(; i < Boards[board].ModbusModelHoldingReg->rowCount(); i++)
|
||||
ui->writeValueTable->setRowHidden(i, ui->writeValueTable->model()->index(i, 0).parent(), false);
|
||||
}
|
||||
ui->writeValueTable->hideColumn(3);
|
||||
ui->writeValueTable->showColumn(2);
|
||||
} else {
|
||||
@@ -821,30 +899,35 @@ void M3KTE::changeTable(int board, int tabletype)
|
||||
if(tabletype == 1) {
|
||||
Boards[board].ModbusModelHoldingReg->setStartAddress(0);
|
||||
int i = 0;
|
||||
for(;i<Boards[board].ModbusModelHoldingReg->rowCount()/2;i++) {
|
||||
for(; i < Boards[board].ModbusModelHoldingReg->rowCount() / 2; i++)
|
||||
ui->writeValueTable->setRowHidden(i, ui->writeValueTable->model()->index(i, 0).parent(), false);
|
||||
}
|
||||
for(;i<Boards[board].ModbusModelHoldingReg->rowCount();i++) {
|
||||
for(; i < Boards[board].ModbusModelHoldingReg->rowCount(); i++)
|
||||
ui->writeValueTable->setRowHidden(i, ui->writeValueTable->model()->index(i, 0).parent(), true);
|
||||
}
|
||||
} else {
|
||||
Boards[board].ModbusModelHoldingReg->setStartAddress(Boards[board].ModbusModelHoldingReg->rowCount() / 2);
|
||||
int i = 0;
|
||||
for(;i<Boards[board].ModbusModelHoldingReg->rowCount()/2;i++) {
|
||||
for(; i < Boards[board].ModbusModelHoldingReg->rowCount() / 2; i++)
|
||||
ui->writeValueTable->setRowHidden(i, ui->writeValueTable->model()->index(i, 0).parent(), true);
|
||||
}
|
||||
for(;i<Boards[board].ModbusModelHoldingReg->rowCount();i++) {
|
||||
for(; i < Boards[board].ModbusModelHoldingReg->rowCount(); i++)
|
||||
ui->writeValueTable->setRowHidden(i, ui->writeValueTable->model()->index(i, 0).parent(), false);
|
||||
}
|
||||
}
|
||||
ui->writeValueTable->hideColumn(2);
|
||||
ui->writeValueTable->showColumn(3);
|
||||
}
|
||||
for(int i = 0; i < 5; i++) {
|
||||
for(int i = 0; i < 5; i++)
|
||||
ui->writeValueTable->resizeColumnToContents(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Создает и возвращает объект запроса чтения для Modbus.
|
||||
*
|
||||
* Этот метод формирует объект QModbusDataUnit в соответствии с текущими настройками интерфейса:
|
||||
* - определяет тип данных (коэли или регистры) в зависимости от выбранной таблицы;
|
||||
* - вычисляет стартовый адрес на основе текущего индекса таблицы;
|
||||
* - определяет число записей для чтения, ограниченное диапазоном доступных данных.
|
||||
*
|
||||
* @return Объект QModbusDataUnit, готовый для отправки запроса чтения.
|
||||
*/
|
||||
QModbusDataUnit M3KTE::readRequest() const
|
||||
{
|
||||
const auto table =
|
||||
@@ -855,6 +938,17 @@ QModbusDataUnit M3KTE::readRequest() const
|
||||
return QModbusDataUnit(table, startAddress, numberOfEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Создает и возвращает объект запроса записи для Modbus.
|
||||
*
|
||||
* Этот метод формирует объект QModbusDataUnit для отправки данных на устройство,
|
||||
* основываясь на текущих настройках интерфейса:
|
||||
* - определяет тип данных (например, кони или регистры) в зависимости от выбранной таблицы;
|
||||
* - вычисляет стартовый адрес для записи, исходя из текущего индекса таблицы;
|
||||
* - задает количество записей для записи, учитывая ограничение диапазона данных.
|
||||
*
|
||||
* @return Объект QModbusDataUnit, подготовленный для отправки запроса записи.
|
||||
*/
|
||||
QModbusDataUnit M3KTE::writeRequest() const
|
||||
{
|
||||
const auto table =
|
||||
@@ -865,43 +959,65 @@ QModbusDataUnit M3KTE::writeRequest() const
|
||||
return QModbusDataUnit(table, startAddress, numberOfEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает пользовательские события, связанные с изменением ID платы и её статуса.
|
||||
*
|
||||
* Метод переопределяет обработку событий QWidget. В зависимости от типа события:
|
||||
* - При `QEvent::User` выполняется изменение ID платы через Modbus и последующая проверка.
|
||||
* - При типе 1001 (кастомный тип) происходит обновление состояния платы.
|
||||
*
|
||||
* Логируются ошибки при неудачных операциях, осуществляется управление асинхронными запросами Modbus.
|
||||
*
|
||||
* @param event Указатель на событие
|
||||
* @return true, если событие обработано; иначе вызывает базовый обработчик
|
||||
*/
|
||||
bool M3KTE::event(QEvent *event)
|
||||
{
|
||||
if(event->type() == QEvent::User) {
|
||||
BoardIdHasBeenChanged* _event = static_cast<BoardIdHasBeenChanged*>(event);
|
||||
QModbusDataUnit* _unit = new QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 172, 1);
|
||||
// Обработка пользовательского события изменения ID платы
|
||||
auto _event = static_cast<BoardIdHasBeenChanged*>(event);
|
||||
// Создаем запрос на запись нового ID в регистр
|
||||
auto *_unit = new QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 172, 1);
|
||||
_unit->setValue(0, _event->BoardNewID());
|
||||
// Обновляем локально временный адрес платы
|
||||
Boards[_event->BoardNum()]._tmp_adr = _event->BoardNewID();
|
||||
// Отправка запроса на изменение адреса через Modbus
|
||||
if(auto *reply = modbusDevice->sendWriteRequest(*_unit, Boards[_event->BoardNum()].adr)) {
|
||||
if(!reply->isFinished())
|
||||
if(!reply->isFinished()) {
|
||||
// Обработка завершения ответа через сигнал finished
|
||||
connect(reply, &QModbusReply::finished, this, [reply, this, _event, _unit]() {
|
||||
if(reply->error() == QModbusDevice::TimeoutError) {
|
||||
// В случае тайм-аута, отправляем запрос на чтение адреса
|
||||
if(auto *subreply = modbusDevice->sendReadRequest(*_unit, Boards[_event->BoardNum()]._tmp_adr)) {
|
||||
if(!subreply->isFinished()) {
|
||||
connect(subreply, &QModbusReply::finished, this, [subreply, this, _event]() {
|
||||
checkAdrChange(subreply, _event->BoardNum());
|
||||
});
|
||||
} else {
|
||||
// Обработка ошибки чтения адреса
|
||||
logError(tr("Плата %1 (ID %2)").arg(_event->BoardNum() + 1).arg(Boards[_event->BoardNum()].adr),
|
||||
subreply->errorString(), ++Boards[_event->BoardNum()].error_adr_change,
|
||||
"Не удалось изменить адрес устройства. [1]");
|
||||
reply->deleteLater();
|
||||
delete subreply; // broadcast replies return immediately
|
||||
delete subreply;
|
||||
}
|
||||
} else {
|
||||
// Ошибка при создании запроса на чтение
|
||||
logError(tr("Плата %1 (ID %2)").arg(_event->BoardNum() + 1).arg(Boards[_event->BoardNum()].adr),
|
||||
modbusDevice->errorString(), ++Boards[_event->BoardNum()].error_adr_change,
|
||||
"Не удалось изменить адрес устройства. [2]");
|
||||
reply->deleteLater();
|
||||
}
|
||||
} else {
|
||||
// Ошибка при ответе на запись
|
||||
logError(tr("Плата %1 (ID %2)").arg(_event->BoardNum() + 1).arg(Boards[_event->BoardNum()].adr),
|
||||
reply->errorString(), ++Boards[_event->BoardNum()].error_adr_change,
|
||||
"Не удалось изменить адрес устройства. [3]");
|
||||
reply->deleteLater();
|
||||
}
|
||||
});
|
||||
else {
|
||||
} else {
|
||||
// Запрос завершился сразу — ошибка
|
||||
logError(tr("Плата %1 (ID %2)").arg(_event->BoardNum() + 1).arg(Boards[_event->BoardNum()].adr),
|
||||
reply->errorString(), ++Boards[_event->BoardNum()].error_adr_change,
|
||||
"Не удалось изменить адрес устройства. [4]");
|
||||
@@ -909,36 +1025,54 @@ bool M3KTE::event(QEvent *event)
|
||||
delete reply;
|
||||
}
|
||||
} else {
|
||||
// Не удалось отправить запрос
|
||||
logError(tr("Плата %1 (ID %2)").arg(_event->BoardNum() + 1).arg(Boards[_event->BoardNum()].adr),
|
||||
modbusDevice->errorString(), ++Boards[_event->BoardNum()].error_adr_change,
|
||||
"Не удалось изменить адрес устройства. [5]");
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
m_deviceSettingsDialog->show();
|
||||
return true;
|
||||
} else if(event->type() == (QEvent::Type)1001) {
|
||||
pollStatusChange* _event = static_cast<pollStatusChange*>(event);
|
||||
QModbusDataUnit* _unit = new QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 170, 1);
|
||||
// Обработка смены статуса плат
|
||||
auto _event = static_cast<pollStatusChange*>(event);
|
||||
auto *_unit = new QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 170, 1);
|
||||
_unit->setValue(0, _event->Status());
|
||||
|
||||
// Отправка команды на изменение статуса
|
||||
if(auto *reply = modbusDevice->sendWriteRequest(*_unit, Boards[_event->BoardID()].adr)) {
|
||||
if(!reply->isFinished())
|
||||
if(!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, this, _event]() {
|
||||
if(reply->error() != QModbusDevice::NoError) {
|
||||
// Логирование ошибок
|
||||
logError(tr("Плата %1 (ID %2)").arg(_event->BoardID() + 1).arg(Boards[_event->BoardID()].adr),
|
||||
reply->errorString(), ++Boards[_event->BoardID()].error_cmd_change, "");
|
||||
reply->deleteLater();
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Не удалось отправить команду
|
||||
logError(tr("Плата %1 (ID %2)").arg(_event->BoardID() + 1).arg(Boards[_event->BoardID()].adr),
|
||||
modbusDevice->errorString(), ++Boards[_event->BoardID()].error_cmd_change, "");
|
||||
reply->deleteLater();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// В случаене распознанного события вызываем дефолтный обработчик
|
||||
return QWidget::event(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Проверяет результат изменения адреса устройства после Modbus-запроса.
|
||||
*
|
||||
* Анализирует ответ `reply`, обновляет текущий адрес в структуре `Boards`
|
||||
* при успешной проверке. В случае ошибки логирует сообщение и увеличивает счетчик ошибок.
|
||||
*
|
||||
* @param reply Указатель на объект QModbusReply, содержащий ответ на запрос изменения адреса.
|
||||
* @param boardNum Индекс устройства в массиве `Boards`.
|
||||
*/
|
||||
void M3KTE::checkAdrChange(QModbusReply *reply, unsigned boardNum)
|
||||
{
|
||||
if(!reply) {
|
||||
@@ -960,6 +1094,20 @@ void M3KTE::checkAdrChange(QModbusReply *reply, unsigned boardNum)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Проверяет состояние всех активных плат через Modbus.
|
||||
*
|
||||
* Этот метод инициирует последовательные асинхронные чтения входных регистров (адрес 85)
|
||||
* для всех активных плат, используя `QSharedPointer` для безопасного управления счётчиками и наборами.
|
||||
* После завершения всех запросов генерируются сигналы:
|
||||
* - `successAtCheckBoards()`, если все платы успешно подтвердили состояние,
|
||||
* - `errorAtCheckBoards()`, если произошла ошибка или не все платы подтвердили.
|
||||
*
|
||||
* В случае неуспешной отправки запроса по плате, вызывается сигнал `errorAtCheckBoards()`,
|
||||
* и выполнение метода завершается.
|
||||
*
|
||||
* Если активных плат не обнаружено, также генерируется сигнал `errorAtCheckBoards()`.
|
||||
*/
|
||||
void M3KTE::checkBoards()
|
||||
{
|
||||
QModbusDataUnit unitCheck(QModbusDataUnit::InputRegisters, 85, 1);
|
||||
@@ -974,36 +1122,53 @@ void M3KTE::checkBoards()
|
||||
int slaveAddress = Boards[i].adr;
|
||||
QModbusReply *reply = modbusDevice->sendReadRequest(unitCheck, slaveAddress);
|
||||
if(!reply) {
|
||||
revertToOldSpeedAndRestart();
|
||||
emit errorAtCheckBoards();
|
||||
return;
|
||||
}
|
||||
pendingBoards->insert(slaveAddress);
|
||||
connect(reply, &QModbusReply::finished, this,
|
||||
[this, i, reply, slaveAddress, totalActiveBoards, confirmedBoards, pendingBoards]() {
|
||||
if(reply->error() == QModbusDevice::NoError) {
|
||||
if(reply->error() == QModbusDevice::NoError)
|
||||
(*confirmedBoards)++;
|
||||
} else {
|
||||
else
|
||||
logError(tr("Плата %1 (ID %2)").arg(i + 1).arg(slaveAddress),
|
||||
reply->errorString(), ++Boards[i].error_baud_change,
|
||||
"Ошибка при подтверждении изменения скорости обмена.");
|
||||
}
|
||||
pendingBoards->remove(slaveAddress);
|
||||
reply->deleteLater();
|
||||
if(pendingBoards->isEmpty()) {
|
||||
if(*confirmedBoards != *totalActiveBoards) {
|
||||
if(*confirmedBoards != *totalActiveBoards)
|
||||
emit errorAtCheckBoards();
|
||||
} else {
|
||||
else
|
||||
emit successAtCheckBoards();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Если нет ни одной активной платы
|
||||
if(*totalActiveBoards == 0) {
|
||||
if(*totalActiveBoards == 0)
|
||||
emit errorAtCheckBoards();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обновляет скорость обмена данными и проверяет ее подтверждение для устройств.
|
||||
*
|
||||
* Этот метод выполняет изменение скорости обмена, отправляя команды через Modbus,
|
||||
* и после подтверждения повторно подключается с новой скоростью или восстанавливает предыдущие настройки при ошибках.
|
||||
*
|
||||
* Основные шаги:
|
||||
* 1. Останавливает сканирование плат.
|
||||
* 2. Устанавливает таймаут для модбас-устройства.
|
||||
* 3. Определяет новую скорость исходя из текущих настроек.
|
||||
* 4. Проверяет допустимость выбранной скорости.
|
||||
* 5. Отправляет команды записи скорости на все активные платы.
|
||||
* 6. После подтверждения всех плат — обновляет настройки соединения и перезапускает сканирование.
|
||||
* 7. В случае ошибок — восстанавливает предыдущую скорость и перезапускает скан.
|
||||
*
|
||||
* Для синхронизации используем `QSharedPointer` для счетчиков активных и подтвержденных плат,
|
||||
* а для списков активных плат — `QSharedPointer<QSet<int>>`.
|
||||
*
|
||||
* Лямбда `processResult` вызывается после обработки всех плат, чтобы завершить или повторить операцию в случае ошибок.
|
||||
*/
|
||||
void M3KTE::onSpeedUpdate()
|
||||
{
|
||||
stopScanBoard();
|
||||
@@ -1019,23 +1184,19 @@ void M3KTE::onSpeedUpdate()
|
||||
case 6: tmp_speed = 57600; break;
|
||||
case 7: tmp_speed = 115200; break;
|
||||
}
|
||||
|
||||
if(tmp_speed == 0) {
|
||||
logError(tr("Программная ошибка"), "Неожиданное значение скорости", 0,
|
||||
"Ошибка при изменении скорости обмена.");
|
||||
beginScanBoards();
|
||||
return;
|
||||
}
|
||||
|
||||
// Используем shared pointers вместо ссылок на стековые переменные
|
||||
auto totalActiveBoards = QSharedPointer<int>::create(0);
|
||||
auto confirmedBoards = QSharedPointer<int>::create(0);
|
||||
auto pendingBoards = QSharedPointer<QSet<int>>::create();
|
||||
auto newSpeed = tmp_speed; // копируем для захвата
|
||||
|
||||
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, 173, 1);
|
||||
unit.setValue(0, m_deviceSettingsDialog->currentSpeed());
|
||||
|
||||
// Лямбда для обработки результата
|
||||
auto processResult = [this, totalActiveBoards, confirmedBoards, newSpeed]() {
|
||||
if(*confirmedBoards != *totalActiveBoards) {
|
||||
@@ -1046,10 +1207,8 @@ void M3KTE::onSpeedUpdate()
|
||||
modbusDevice->disconnectDevice();
|
||||
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, newSpeed);
|
||||
modbusDevice->connectDevice();
|
||||
|
||||
// Используем QPointer для безопасного доступа к this
|
||||
QPointer<M3KTE> safeThis(this);
|
||||
|
||||
connect(this, &M3KTE::errorAtCheckBoards, this, [safeThis]() {
|
||||
if(!safeThis)
|
||||
return;
|
||||
@@ -1058,7 +1217,6 @@ void M3KTE::onSpeedUpdate()
|
||||
safeThis->revertToOldSpeedAndRestart();
|
||||
safeThis->beginScanBoards();
|
||||
});
|
||||
|
||||
connect(this, &M3KTE::successAtCheckBoards, this, [safeThis]() {
|
||||
if(!safeThis)
|
||||
return;
|
||||
@@ -1067,11 +1225,9 @@ void M3KTE::onSpeedUpdate()
|
||||
safeThis->modbusDevice->setTimeout(safeThis->m_settingsDialog->settings().responseTime);
|
||||
safeThis->beginScanBoards();
|
||||
});
|
||||
|
||||
checkBoards();
|
||||
}
|
||||
};
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(!Boards[i].isActive)
|
||||
continue;
|
||||
@@ -1080,33 +1236,35 @@ void M3KTE::onSpeedUpdate()
|
||||
if(reply) {
|
||||
(*totalActiveBoards)++;
|
||||
pendingBoards->insert(slaveAdress);
|
||||
|
||||
// Захватываем shared pointers по значению - это безопасно
|
||||
connect(reply, &QModbusReply::finished, this,
|
||||
[this, i, reply, slaveAdress, totalActiveBoards, confirmedBoards, pendingBoards, processResult]() {
|
||||
if(reply->error() == QModbusDevice::TimeoutError) {
|
||||
if(reply->error() == QModbusDevice::TimeoutError)
|
||||
(*confirmedBoards)++;
|
||||
} else if (reply->error() == QModbusDevice::NoError) {
|
||||
else if(reply->error() == QModbusDevice::NoError)
|
||||
logError(tr("Плата %1 (ID %2)").arg(i + 1).arg(slaveAdress),
|
||||
tr("Неожиданный ответ."), ++Boards[i].error_baud_change,
|
||||
"Ошибка при изменении скорости обмена.");
|
||||
} else {
|
||||
else
|
||||
logError(tr("Плата %1 (ID %2)").arg(i + 1).arg(slaveAdress),
|
||||
reply->errorString(), ++Boards[i].error_baud_change,
|
||||
"Ошибка при изменении скорости обмена.");
|
||||
}
|
||||
pendingBoards->remove(slaveAdress);
|
||||
reply->deleteLater();
|
||||
|
||||
if(pendingBoards->isEmpty()) {
|
||||
if(pendingBoards->isEmpty())
|
||||
processResult();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Вспомогательная функция восстановления старой скорости
|
||||
/**
|
||||
* @brief Восстанавливает предыдущую скорость обмена и перезапускает соединение.
|
||||
*
|
||||
* Этот метод отключает текущий модбас-устройник, устанавливает скорость соединения
|
||||
* в значение, сохраненное в настройках (`curBaud()`), и затем переподключается.
|
||||
* Используется для возврата к исходной скорости обмена при ошибках или отмене операции.
|
||||
*/
|
||||
void M3KTE::revertToOldSpeedAndRestart()
|
||||
{
|
||||
modbusDevice->disconnectDevice();
|
||||
@@ -1116,31 +1274,45 @@ void M3KTE::revertToOldSpeedAndRestart()
|
||||
modbusDevice->connectDevice();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает изменение параметра четности (parity) для устройств.
|
||||
*
|
||||
* Отправляет запросы на изменение параметра четности на активных платах,
|
||||
* затем обрабатывает результаты, при необходимости откатывая изменения.
|
||||
*/
|
||||
void M3KTE::onParityUpdate()
|
||||
{
|
||||
// Останавливаем сканирование плат
|
||||
stopScanBoard();
|
||||
modbusDevice->setTimeout(500);
|
||||
// Настройка регистра для установки режима четности
|
||||
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, 174, 1);
|
||||
switch(m_deviceSettingsDialog->currentParity()) {
|
||||
case 0: unit.setValue(0, 0x000); break; //Нет контроля
|
||||
case 0: unit.setValue(0, 0x0000); break; // Нет контроля
|
||||
case 1: unit.setValue(0, 0x0400); break; // Четный
|
||||
case 2: unit.setValue(0, 0x0600); break; // Нечетный
|
||||
}
|
||||
// Используем shared pointers вместо ссылок на стековые переменные
|
||||
// Переменные для подсчёта прогресса
|
||||
auto totalActiveBoards = QSharedPointer<int>::create(0);
|
||||
auto confirmedBoards = QSharedPointer<int>::create(0);
|
||||
auto pendingBoards = QSharedPointer<QSet<int>>::create();
|
||||
auto oldParity = m_settingsDialog->curParity(); // сохраняем старую четность для отката
|
||||
// Сохраняем текущий и новый параметры четности для отката при ошибках
|
||||
auto oldParity = m_settingsDialog->curParity();
|
||||
auto newParity = m_deviceSettingsDialog->currentParity();
|
||||
// Лямбда для обработки результата
|
||||
/**
|
||||
* Лямбда-функция, вызываемая по завершении всех запросов.
|
||||
* Обрабатывает успешное изменение или ошибку.
|
||||
*/
|
||||
auto processResult = [this, totalActiveBoards, confirmedBoards, oldParity, newParity]() {
|
||||
if(*confirmedBoards != *totalActiveBoards) {
|
||||
// Не все подтвердили — повторно запускаем скан
|
||||
modbusDevice->setTimeout(m_settingsDialog->settings().responseTime);
|
||||
beginScanBoards();
|
||||
} else {
|
||||
// Все подтвердили — меняем параметры соединения
|
||||
modbusDevice->disconnectDevice();
|
||||
modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, newParity);
|
||||
modbusDevice->connectDevice();
|
||||
// Обработка ошибок и успеха при смене паритета
|
||||
auto errorHandler = [this, oldParity]() {
|
||||
disconnect(this, &M3KTE::errorAtCheckBoards, this, nullptr);
|
||||
modbusDevice->setTimeout(m_settingsDialog->settings().responseTime);
|
||||
@@ -1155,43 +1327,82 @@ void M3KTE::onParityUpdate()
|
||||
modbusDevice->setTimeout(m_settingsDialog->settings().responseTime);
|
||||
beginScanBoards();
|
||||
};
|
||||
// Подписка на события успеха и ошибки
|
||||
connect(this, &M3KTE::errorAtCheckBoards, this, errorHandler);
|
||||
connect(this, &M3KTE::successAtCheckBoards, this, successHandler);
|
||||
// Запускаем повторную проверку плат
|
||||
checkBoards();
|
||||
}
|
||||
};
|
||||
// Отправляем запросы на каждую активную плату
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(!Boards[i].isActive)
|
||||
continue;
|
||||
int slaveAdress = Boards[i].adr;
|
||||
auto *reply = modbusDevice->sendWriteRequest(unit, slaveAdress);
|
||||
int slaveAddress = Boards[i].adr;
|
||||
auto *reply = modbusDevice->sendWriteRequest(unit, slaveAddress);
|
||||
if(reply) {
|
||||
(*totalActiveBoards)++;
|
||||
pendingBoards->insert(slaveAdress);
|
||||
pendingBoards->insert(slaveAddress);
|
||||
// Обработка завершения ответа
|
||||
connect(reply, &QModbusReply::finished, this,
|
||||
[this, i, reply, slaveAdress, totalActiveBoards, confirmedBoards, pendingBoards, processResult]() {
|
||||
if(reply->error() == QModbusDevice::TimeoutError) {
|
||||
[this, i, reply, slaveAddress, totalActiveBoards, confirmedBoards, pendingBoards, processResult]() {
|
||||
if(reply->error() == QModbusDevice::TimeoutError)
|
||||
(*confirmedBoards)++;
|
||||
} else if (reply->error() == QModbusDevice::NoError) {
|
||||
logError(tr("Плата %1 (ID %2)").arg(i+1).arg(slaveAdress),
|
||||
else if(reply->error() == QModbusDevice::NoError)
|
||||
logError(tr("Плата %1 (ID %2)").arg(i + 1).arg(slaveAddress),
|
||||
tr("Неожиданный ответ."), ++Boards[i].error_baud_change,
|
||||
"Ошибка при изменении чётности.");
|
||||
} else {
|
||||
logError(tr("Плата %1 (ID %2)").arg(i+1).arg(slaveAdress),
|
||||
else
|
||||
logError(tr("Плата %1 (ID %2)").arg(i + 1).arg(slaveAddress),
|
||||
reply->errorString(), ++Boards[i].error_baud_change,
|
||||
"Ошибка при изменении чётности.");
|
||||
}
|
||||
pendingBoards->remove(slaveAdress);
|
||||
// Удаляем складной ответ
|
||||
pendingBoards->remove(slaveAddress);
|
||||
reply->deleteLater();
|
||||
|
||||
if(pendingBoards->isEmpty()) {
|
||||
// Когда все ответы получены, обрабатываем результат
|
||||
if(pendingBoards->isEmpty())
|
||||
processResult();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Функция реакция на ошибку при инициализации устройств.
|
||||
*
|
||||
* Инициирует отключение, закрывает прогресс-бар, и настраивает параметры
|
||||
* таймаута и количества повторных попыток.
|
||||
*
|
||||
* @param bar Указатель на прогресс-бар для отображения процесса.
|
||||
* @return false, так как функция, в текущем виде, всегда возвращает false.
|
||||
*/
|
||||
bool M3KTE::deadPing(QProgressDialog *bar)
|
||||
{
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
modbusDevice->setTimeout(m_settingsDialog->settings().responseTime);
|
||||
modbusDevice->setNumberOfRetries(m_settingsDialog->settings().numberOfRetries);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Выполняет сканирование сети устройств (плат) по Modbus-адресам.
|
||||
*
|
||||
* Эта функция последовательно посылает запросы на определения типа устройства и идентификатора платы
|
||||
* по адресу от 1 до 246. Если устройство отвечает ожидаемым ответом, оно считается найденным и
|
||||
* добавляется в список активных плат. После сканирования отображается окно с результатами, и при необходимости
|
||||
* выполняется считывание текущих настроек каждой активной платы.
|
||||
*
|
||||
* Основные шаги:
|
||||
* - Открытие прогресс-диалога и настройка обработки отмены.
|
||||
* - Посылка запросов для определения типа и ID платы.
|
||||
* - Обработка ответов, обновление информации о платах.
|
||||
* - Взаимодействие с пользователем: сообщение о завершении сканирования и опрос настроек.
|
||||
* - Обновление интерфейса и состояния устройств.
|
||||
*
|
||||
* @return true, если сканирование прошло успешно и завершено; false — при ошибках или отмене.
|
||||
*/
|
||||
bool M3KTE::pingNetworkDevices()
|
||||
{
|
||||
CurrentConnectedDevice = 0;
|
||||
@@ -1214,80 +1425,53 @@ bool M3KTE::pingNetworkDevices()
|
||||
for(CurrentConnectedDevice = 0; CurrentConnectedDevice < 4;) {
|
||||
auto *reply = modbusDevice->sendRawRequest(requestOfDeviceType, tmp_adr);
|
||||
//Запрос типа устройства.
|
||||
if(reply == nullptr) {
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
return false;
|
||||
}
|
||||
if(reply == nullptr)
|
||||
return deadPing(bar);
|
||||
while(!reply->isFinished()) {
|
||||
if(isRun && CurrentConnectedDevice < 1) {
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
return false;
|
||||
} else if(isRun) {
|
||||
if(isRun && CurrentConnectedDevice < 1)
|
||||
return deadPing(bar);
|
||||
else if(isRun)
|
||||
break;
|
||||
}
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
if(isRun && CurrentConnectedDevice < 1) {
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
return false;
|
||||
} else if(isRun) {
|
||||
if(isRun && CurrentConnectedDevice < 1)
|
||||
return deadPing(bar);
|
||||
else if(isRun)
|
||||
break;
|
||||
} else if(!isRun) {
|
||||
else if(!isRun) {
|
||||
//Нужна проверка типа устройства
|
||||
if(reply->error() == QModbusDevice::NoError) {
|
||||
QModbusResponse resp = reply->rawResult();
|
||||
QString result = QString(resp.data().remove(0, MODBUS_REQUEST_PROTOCOL_INFO_LENGTH));
|
||||
if(result == QString("KTE")) {
|
||||
//modbusDevice->setTimeout(1000);
|
||||
auto *subreply = modbusDevice->sendRawRequest(requestOfBoardID, tmp_adr);
|
||||
|
||||
while(!subreply->isFinished()) {
|
||||
if(isRun && CurrentConnectedDevice < 1) {
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
return false;
|
||||
} else if(isRun) {
|
||||
if(isRun && CurrentConnectedDevice < 1)
|
||||
return deadPing(bar);
|
||||
else if(isRun)
|
||||
break;
|
||||
}
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
int plata_ind = 0;
|
||||
if(subreply->rawResult().data().size() >= MODBUS_REQUEST_PROTOCOL_INFO_LENGTH) // ответ принят
|
||||
plata_ind = subreply->rawResult().data().at(MODBUS_REQUEST_PROTOCOL_INFO_LENGTH) - 0x30; // парс ответа
|
||||
|
||||
if(plata_ind < 1 || plata_ind > 4) {
|
||||
QMessageBox::warning(this, "Ошибка при сканировании сети.",
|
||||
QString("Не удалось получить порядковый номер платы по адресу %1").arg(tmp_adr));
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
return false;
|
||||
return deadPing(bar);
|
||||
}
|
||||
int board_ind = plata_ind - 1;
|
||||
if(isRun && CurrentConnectedDevice < 1) {
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
return false;
|
||||
} else if(isRun) {
|
||||
if(isRun && CurrentConnectedDevice < 1)
|
||||
return deadPing(bar);
|
||||
else if(isRun)
|
||||
break;
|
||||
} else {
|
||||
else {
|
||||
statusBar()->showMessage(tr("Плата %1 найдена по адресу %2.").arg(board_ind).arg(tmp_adr),
|
||||
m_settingsDialog->settings().responseTime);
|
||||
if(Boards[board_ind].isActive) {
|
||||
QMessageBox::warning(this, "Ошибка при сканировании сети.",
|
||||
QString("Платы по адресам %1 и %2 имеют одинаковый порядковый номер %3").arg(Boards[board_ind].adr).arg(tmp_adr).arg(plata_ind));
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
return false;
|
||||
return deadPing(bar);
|
||||
}
|
||||
CurrentConnectedDevice++;
|
||||
Boards[board_ind].adr = Boards[board_ind]._tmp_adr = tmp_adr;
|
||||
@@ -1305,128 +1489,125 @@ bool M3KTE::pingNetworkDevices()
|
||||
QMessageBox::warning(this, "Ошибка при сканировании сети.",
|
||||
QString("Выход за пределы допустимых адресов. Найдено %1 плат.").arg(CurrentConnectedDevice));
|
||||
bar->setValue(4);
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
onConnectClicked();
|
||||
return false;
|
||||
} else if(tmp_adr>=247) {
|
||||
return deadPing(bar);
|
||||
} else if(tmp_adr >= 247)
|
||||
break;
|
||||
}
|
||||
}
|
||||
isRun = false;
|
||||
QMessageBox::warning(this, "Сканирование сети завершено.", tr("Найдено плат: %1 из 4.").arg(CurrentConnectedDevice));
|
||||
if(isRun) {
|
||||
QMessageBox::warning(this, "Ошибка при получении текущих настроек.", QString("Прерывание по запросу пользователя."));
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
return false;
|
||||
}
|
||||
// Создаем QMessageBox
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Сканирование сети завершено.");
|
||||
msgBox.setText(QString("Найдено плат: %1 из 4.").arg(CurrentConnectedDevice));
|
||||
// Добавляем две кнопки
|
||||
QPushButton *requestSettingsBtn = msgBox.addButton("Запросить настройки", QMessageBox::AcceptRole);
|
||||
QPushButton *cancelBtn = msgBox.addButton("Прервать подключение", QMessageBox::RejectRole);
|
||||
// Выполняем модальное окно и ожидаем ответ
|
||||
msgBox.exec();
|
||||
// Обработка нажатия
|
||||
if(msgBox.clickedButton() == cancelBtn)
|
||||
return deadPing(bar);
|
||||
modbusDevice->setTimeout(m_settingsDialog->settings().responseTime);
|
||||
bar->setLabelText(tr("Считывание текущих настроек..."));
|
||||
bar->setRange(0, CurrentConnectedDevice * 3);
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(Boards[i].isActive) {
|
||||
QModbusDataUnit* _unit_settings[3];
|
||||
_unit_settings[0] = new QModbusDataUnit(QModbusDataUnit::Coils, 0, 85-(i/3*20));
|
||||
_unit_settings[1] = new QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 0, 85-(i/3*20));
|
||||
_unit_settings[2] = new QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 85, 85-(i/3*20));
|
||||
QModbusDataUnit cCoils(QModbusDataUnit::Coils, 0, 85 - (i / 3 * 20));
|
||||
QModbusDataUnit wHR(QModbusDataUnit::HoldingRegisters, 0, 85 - (i / 3 * 20));
|
||||
QModbusDataUnit aHR(QModbusDataUnit::HoldingRegisters, 85, 85 - (i / 3 * 20));
|
||||
_unit_settings[0] = &cCoils;
|
||||
_unit_settings[1] = &wHR;
|
||||
_unit_settings[2] = &aHR;
|
||||
Boards_Fields[i]->setEnabled(true);
|
||||
for(int j = 0; j < 3; j++) {
|
||||
bar->setValue(i * 3 + j);
|
||||
if(isRun) {
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
delete _unit_settings[0];
|
||||
delete _unit_settings[1];
|
||||
delete _unit_settings[2];
|
||||
return false;
|
||||
}
|
||||
if(isRun)
|
||||
return deadPing(bar);
|
||||
auto *reply = modbusDevice->sendReadRequest(*_unit_settings[j], Boards[i].adr);
|
||||
if(!reply) {
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
delete _unit_settings[0];
|
||||
delete _unit_settings[1];
|
||||
delete _unit_settings[2];
|
||||
return false;
|
||||
}
|
||||
if(!reply)
|
||||
return deadPing(bar);
|
||||
while(!reply->isFinished()) {
|
||||
if(isRun) {
|
||||
QMessageBox::warning(this, "Ошибка при получении текущих настроек.",
|
||||
QString("Прерывание по запросу пользователя."));
|
||||
onConnectClicked();
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
delete _unit_settings[0];
|
||||
delete _unit_settings[1];
|
||||
delete _unit_settings[2];
|
||||
return false;
|
||||
return deadPing(bar);
|
||||
}
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
if(reply->error()==QModbusDevice::NoError) {
|
||||
if(reply->error()==QModbusDevice::NoError)
|
||||
applySettingsFromScan(reply);
|
||||
} else {
|
||||
else {
|
||||
QMessageBox::warning(this, "Ошибка при получении текущих настроек.",
|
||||
QString("Таймаут при опросе устройства %1 по адресу %2").arg(i + 1).arg(Boards[i].adr));
|
||||
bar->setValue(CurrentConnectedDevice * 3);
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
onConnectClicked();
|
||||
delete _unit_settings[0];
|
||||
delete _unit_settings[1];
|
||||
delete _unit_settings[2];
|
||||
return false;
|
||||
return deadPing(bar);
|
||||
}
|
||||
}
|
||||
Boards_Fields[i]->setTitle(QString("Плата №%1 (ID %2)").arg(i + 1).arg(Boards[i].adr));
|
||||
delete _unit_settings[0];
|
||||
delete _unit_settings[1];
|
||||
delete _unit_settings[2];
|
||||
}
|
||||
}
|
||||
int totalBoardsActive = 0;
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
if(Boards[i].isActive)
|
||||
totalBoardsActive++;
|
||||
}
|
||||
m_parameterWorkspace->setDeviceCount(totalBoardsActive);
|
||||
|
||||
modbusDevice->setNumberOfRetries(m_settingsDialog->settings().numberOfRetries);
|
||||
beginScanBoards();
|
||||
bar->deleteLater();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Запускает процесс сканирования активных плат в системе.
|
||||
*
|
||||
* Эта функция перебирает все возможные платы (от 0 до 3), проверяет их активность,
|
||||
* и для каждой активной платы инициализирует её сканирование:
|
||||
* - Обновляет состояние интерфейса для отображения активных плат.
|
||||
* - Инициализирует опрос настроек устройства по адресу.
|
||||
* - Запускает процедуру сканирования платы.
|
||||
* - Обновляет рабочее пространство параметров с информацией об активной плате.
|
||||
*
|
||||
* После завершения процедуры все активные платы будут обработаны, и пользователь увидит актуальное состояние.
|
||||
*/
|
||||
void M3KTE::beginScanBoards()
|
||||
{
|
||||
int totalBoardsActive = 0;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int i = 0; i < 4; i++)
|
||||
if(Boards[i].isActive) {
|
||||
m_debugTerminalDialog->setScanBoardActive(true, i);
|
||||
m_deviceSettingsDialog->initPollForBoard(i, Boards[i].adr);
|
||||
boardScan(i);
|
||||
m_parameterWorkspace->updateDevice(totalBoardsActive++, Boards[i].isActive, Boards[i].adr);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Выполняет сканирование данных платы по Modbus.
|
||||
*
|
||||
* Эта функция инициирует последовательные запросы к плате с идентификатором `boardID` для получения
|
||||
* статуса и полного набора данных через Modbus. Обрабатывает завершения запросов асинхронно, используя сигналы и слоты,
|
||||
* измеряет время отклика, логирует ошибки и запусквает следующий цикл сканирования.
|
||||
*
|
||||
* Основные шаги:
|
||||
* - Проверяет наличие активного Modbus-устройства.
|
||||
* - Генерирует запрос чтения регистра статуса (регистры 85).
|
||||
* - Обрабатывает ответ по таймеру; при успешном ответе:
|
||||
* - извлекает статус регистра;
|
||||
* - инициирует запрос полного набора данных (с учётом идентификатора платы);
|
||||
* - обрабатывает ответ данных, вызывает функцию отображения результата,
|
||||
* и перезапускает таймер следующего сканирования.
|
||||
* - В случае ошибок или завершения запросов мгновенно, логирует ошибку и перезапускает цикл.
|
||||
*
|
||||
* @param boardID Идентификатор платы (индекс в массиве).
|
||||
*/
|
||||
void M3KTE::boardScan(unsigned boardID)
|
||||
{
|
||||
if (!modbusDevice) {
|
||||
if(!modbusDevice)
|
||||
return;
|
||||
}
|
||||
emit boardReading(boardID);
|
||||
|
||||
statusBar()->clearMessage();
|
||||
|
||||
QModbusDataUnit statusUnit(QModbusDataUnit::InputRegisters, 85, 1);
|
||||
|
||||
if(auto *reply = modbusDevice->sendReadRequest(statusUnit, Boards[boardID].adr)) {
|
||||
Boards[boardID].timerToStatusResponse.start();
|
||||
if(!reply->isFinished()) {
|
||||
@@ -1456,29 +1637,26 @@ void M3KTE::boardScan(unsigned boardID)
|
||||
// Мгновенно завершенный запрос данных
|
||||
Boards[boardID].timerToDataResponse.elapsed();
|
||||
// shouldLog для dataReply должен определяться здесь, но используем тот же принцип
|
||||
if (Boards[boardID].isActive && !(reply->error() == QModbusDevice::ReplyAbortedError)) {
|
||||
if(Boards[boardID].isActive && !(reply->error() == QModbusDevice::ReplyAbortedError))
|
||||
logError(tr("Плата %1 (ID %2)").arg(boardID + 1).arg(Boards[boardID].adr),
|
||||
modbusDevice->errorString(), ++Boards[boardID].error_TX, "");
|
||||
}
|
||||
dataReply->deleteLater();
|
||||
unsigned timerInterval = m_deviceSettingsDialog->currentBoardTimer(boardID);
|
||||
Boards[boardID].boardScanners->start(timerInterval);
|
||||
}
|
||||
} else {
|
||||
// Ошибка отправки запроса данных
|
||||
if (Boards[boardID].isActive && !(reply->error() == QModbusDevice::ReplyAbortedError)) {
|
||||
if(Boards[boardID].isActive && !(reply->error() == QModbusDevice::ReplyAbortedError))
|
||||
logError(tr("Плата %1 (ID %2)").arg(boardID + 1).arg(Boards[boardID].adr),
|
||||
modbusDevice->errorString(), ++Boards[boardID].error_TX, "");
|
||||
}
|
||||
unsigned timerInterval = m_deviceSettingsDialog->currentBoardTimer(boardID);
|
||||
Boards[boardID].boardScanners->start(timerInterval);
|
||||
}
|
||||
} else {
|
||||
// Ошибка в ответе статуса
|
||||
if (Boards[boardID].isActive && !(reply->error() == QModbusDevice::ReplyAbortedError)) {
|
||||
if(Boards[boardID].isActive && !(reply->error() == QModbusDevice::ReplyAbortedError))
|
||||
logError(tr("Плата %1 (ID %2)").arg(boardID + 1).arg(Boards[boardID].adr),
|
||||
reply->errorString(), ++Boards[boardID].error_TX, "");
|
||||
}
|
||||
unsigned timerInterval = m_deviceSettingsDialog->currentBoardTimer(boardID);
|
||||
Boards[boardID].boardScanners->start(timerInterval);
|
||||
}
|
||||
@@ -1488,25 +1666,30 @@ void M3KTE::boardScan(unsigned boardID)
|
||||
// Мгновенно завершенный запрос статуса
|
||||
Boards[boardID].timerToStatusResponse.elapsed();
|
||||
// Для мгновенно завершенных запросов определяем shouldLog здесь
|
||||
if (Boards[boardID].isActive && !(reply->error() == QModbusDevice::ReplyAbortedError)) {
|
||||
if(Boards[boardID].isActive && !(reply->error() == QModbusDevice::ReplyAbortedError))
|
||||
logError(tr("Плата %1 (ID %2)").arg(boardID + 1).arg(Boards[boardID].adr),
|
||||
modbusDevice->errorString(), ++Boards[boardID].error_TX, "");
|
||||
}
|
||||
reply->deleteLater();
|
||||
unsigned timerInterval = m_deviceSettingsDialog->currentBoardTimer(boardID);
|
||||
Boards[boardID].boardScanners->start(timerInterval);
|
||||
}
|
||||
} else {
|
||||
} else
|
||||
// Ошибка отправки запроса статуса
|
||||
if (Boards[boardID].isActive) {
|
||||
if(Boards[boardID].isActive)
|
||||
logError(tr("Плата %1 (ID %2)").arg(boardID + 1).arg(Boards[boardID].adr),
|
||||
modbusDevice->errorString(), ++Boards[boardID].error_TX, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает и отображает результат сканирования Modbus-ответа.
|
||||
*
|
||||
* Эта функция анализирует ответ, обновляет состояние элементов пользовательского интерфейса
|
||||
* и регистров, а также регистрирует ошибки и предупреждения.
|
||||
*
|
||||
* @param reply Указатель на объект QModbusReply, содержащий ответ.
|
||||
* @param boardID Индекс платы, которую обрабатываем.
|
||||
* @param status Статус регистра, полученный из устройства.
|
||||
*/
|
||||
void M3KTE::displayResultOfScan(QModbusReply *reply, int boardID, int status)
|
||||
{
|
||||
if(!reply)
|
||||
@@ -1528,13 +1711,12 @@ void M3KTE::displayResultOfScan(QModbusReply *reply, int boardID, int status)
|
||||
QString statusText;
|
||||
int state = StatusReg.ParsingReg.mzkte_status;
|
||||
int numb_err = StatusReg.ParsingReg.mzkte_error;
|
||||
if (state == 0) {
|
||||
if(state == 0)
|
||||
statusText = "Ok";
|
||||
} else if (state == 1) {
|
||||
else if(state == 1)
|
||||
statusText = "Non-Critical";
|
||||
} else if (state == 2) {
|
||||
else if(state == 2)
|
||||
statusText = "Critical";
|
||||
}
|
||||
if(state) {
|
||||
switch (numb_err) {
|
||||
case 1: statusText += QString(" (Err 5 VD)"); break;
|
||||
@@ -1559,11 +1741,10 @@ void M3KTE::displayResultOfScan(QModbusReply *reply, int boardID, int status)
|
||||
QString W_Adr;
|
||||
QString A_Adr;
|
||||
int total_qnt;
|
||||
if(boardID == 3) {
|
||||
if(boardID == 3)
|
||||
total_qnt = 65;
|
||||
} else {
|
||||
else
|
||||
total_qnt = 85;
|
||||
}
|
||||
for(int i = unit.startAddress(), total = total_qnt; i < total; ++i) {
|
||||
if(Boards[boardID].coil[i]==true) {
|
||||
int j = 0;
|
||||
@@ -1651,25 +1832,32 @@ void M3KTE::displayResultOfScan(QModbusReply *reply, int boardID, int status)
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает ответ Modbus и применяет настройки к платам.
|
||||
*
|
||||
* Эта функция проверяет ошибку ответа и, при успешном ответе,
|
||||
* обновляет данные в моделях и элементах интерфейса для соответствующей платы.
|
||||
* В случае ошибок отображает сообщения и регистрирует ошибки.
|
||||
*
|
||||
* @param reply Указатель на объект QModbusReply с ответом от устройства.
|
||||
*/
|
||||
void M3KTE::applySettingsFromScan(QModbusReply *reply)
|
||||
{
|
||||
int Adr = 255;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int i = 0; i < 4; i++)
|
||||
if(Boards[i].adr==reply->serverAddress() &&Boards[i].isActive) {
|
||||
Adr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(reply->error() == QModbusDevice::NoError) {
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
for(int i = 0, total = int(unit.valueCount()); i < (total); ++i) {
|
||||
if(unit.registerType() == QModbusDataUnit::Coils) {
|
||||
Boards[Adr].coil[i + unit.startAddress()] = unit.value(i);
|
||||
if(unit.value(i)==1) {
|
||||
if(unit.value(i) == 1)
|
||||
Boards[Adr].ModbusModelCoil->setData(ui->writeValueTable->model()->index(i + unit.startAddress(), 2), Qt::Checked, Qt::CheckStateRole);
|
||||
} else {
|
||||
else
|
||||
Boards[Adr].ModbusModelCoil->setData(ui->writeValueTable->model()->index(i + unit.startAddress(), 2), Qt::Unchecked, Qt::CheckStateRole);
|
||||
}
|
||||
} else if(unit.registerType() == QModbusDataUnit::HoldingRegisters) {
|
||||
Boards[Adr].HR[i + unit.startAddress()] = unit.value(i);
|
||||
Boards[Adr].ModbusModelHoldingReg->setData(Boards[Adr].ModbusModelHoldingReg->index(i + unit.startAddress(), 3), QString::number(unit.value(i), 10), Qt::EditRole);
|
||||
@@ -1691,6 +1879,12 @@ void M3KTE::applySettingsFromScan(QModbusReply *reply)
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Слот для обработки команды записи нескольких регистров.
|
||||
*
|
||||
* Проверяет наличие активного соединения с Modbus-устройством и
|
||||
* вызывает функцию `multipleRegWrite()` для выполнения записи.
|
||||
*/
|
||||
void M3KTE::slotmultipleRegWrite()
|
||||
{
|
||||
if(!modbusDevice)
|
||||
@@ -1698,6 +1892,13 @@ void M3KTE::slotmultipleRegWrite()
|
||||
multipleRegWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Слот для записи нескольких регистров и отправки данных.
|
||||
*
|
||||
* Проверяет наличие активного соединения с Modbus-устройством и,
|
||||
* если соединение установлено, вызывает функции `multipleRegWrite()` для записи
|
||||
* и `multipleRegSend()` для отправки данных.
|
||||
*/
|
||||
void M3KTE::slotmultipleRegWriteAndSend()
|
||||
{
|
||||
if(!modbusDevice)
|
||||
@@ -1706,22 +1907,28 @@ void M3KTE::slotmultipleRegWriteAndSend()
|
||||
multipleRegSend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отправляет команду записи нескольких регистров или катушек на устройство по Modbus.
|
||||
*
|
||||
* Функция создает объект QModbusDataUnit с типом регистров или катушек, в зависимости от настроек.
|
||||
* Заполняет его значениями и обновляет модели данных внутреннего устройства.
|
||||
* Затем инициирует асинхронную отправку запроса через `modbusDevice->sendWriteRequest()`.
|
||||
* Обрабатывает результат асинхронного ответа и выводит сообщения об ошибках.
|
||||
*/
|
||||
void M3KTE::multipleRegSend()
|
||||
{
|
||||
QModbusDataUnit *unit_tx = nullptr;
|
||||
if(m_regMultipleSettings->getTypeReg()) {
|
||||
if(m_regMultipleSettings->getTypeReg())
|
||||
unit_tx = new QModbusDataUnit(QModbusDataUnit::HoldingRegisters, m_regMultipleSettings->getStartAdr(), m_regMultipleSettings->getCountReg());
|
||||
} else {
|
||||
else
|
||||
unit_tx = new QModbusDataUnit(QModbusDataUnit::Coils, m_regMultipleSettings->getStartAdr(), m_regMultipleSettings->getCountReg());
|
||||
}
|
||||
for(unsigned i = 0; i < m_regMultipleSettings->getCountReg(); i++) {
|
||||
unit_tx->setValue(i, m_regMultipleSettings->getNewValue());
|
||||
if(m_regMultipleSettings->getTypeReg()) {
|
||||
if(m_regMultipleSettings->getTypeReg())
|
||||
Boards[m_regMultipleSettings->getBoardId()].HR[i+m_regMultipleSettings->getStartAdr()] = m_regMultipleSettings->getNewValue();
|
||||
} else {
|
||||
else
|
||||
Boards[m_regMultipleSettings->getBoardId()].coil[i+m_regMultipleSettings->getStartAdr()] = m_regMultipleSettings->getNewValue();
|
||||
}
|
||||
}
|
||||
if(auto *reply = modbusDevice->sendWriteRequest(*unit_tx, Boards[m_regMultipleSettings->getBoardId()].adr)) {
|
||||
unsigned Adr = m_regMultipleSettings->getBoardId();
|
||||
if(!reply->isFinished()) {
|
||||
@@ -1741,26 +1948,41 @@ void M3KTE::multipleRegSend()
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
} else {
|
||||
} else
|
||||
// broadcast replies return immediately
|
||||
reply->deleteLater();
|
||||
}
|
||||
} else {
|
||||
} else
|
||||
statusBar()->showMessage(tr("Write error: ") + modbusDevice->errorString(), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обновляет внутренние модели данных для нескольких регистров или катушек.
|
||||
*
|
||||
* В цикле по количеству регистров или катушек, указанных в настройках, обновляет
|
||||
* значения внутренних моделей `ModbusModelHoldingReg` или `ModbusModelCoil` для соответствующих
|
||||
* позиций, основываясь на типе регистров. Значения устанавливаются в новое заданное значение.
|
||||
*/
|
||||
void M3KTE::multipleRegWrite()
|
||||
{
|
||||
for(unsigned i = 0; i < m_regMultipleSettings->getCountReg(); i++) {
|
||||
if(m_regMultipleSettings->getTypeReg()) {
|
||||
if(m_regMultipleSettings->getTypeReg())
|
||||
Boards[m_regMultipleSettings->getBoardId()].ModbusModelHoldingReg->m_holdingRegisters[i + m_regMultipleSettings->getStartAdr()] = m_regMultipleSettings->getNewValue();
|
||||
} else {
|
||||
else
|
||||
Boards[m_regMultipleSettings->getBoardId()].ModbusModelCoil->m_coils[i + m_regMultipleSettings->getStartAdr()] = (bool)m_regMultipleSettings->getNewValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Записывает значение в один котл на конкретной плате по Modbus.
|
||||
*
|
||||
* Проверяет наличие активного соединения и активности платы.
|
||||
* Создает объект `QModbusDataUnit` для одного катушечного адреса, устанавливает значение.
|
||||
* Выполняет отправку записи и обрабатывает результат асинхронно.
|
||||
*
|
||||
* @param boardId Идентификатор платы.
|
||||
* @param coilAddress Адрес котлы, в которую нужно записать.
|
||||
* @param value Значение для записи (true — включено, false — выключено).
|
||||
*/
|
||||
void M3KTE::writeSingleCoil(int boardId, int coilAddress, bool value)
|
||||
{
|
||||
if(!modbusDevice || !Boards[boardId].isActive)
|
||||
@@ -1768,21 +1990,30 @@ void M3KTE::writeSingleCoil(int boardId, int coilAddress, bool value)
|
||||
QModbusDataUnit unit(QModbusDataUnit::Coils, coilAddress, 1);
|
||||
unit.setValue(0, value ? 1 : 0);
|
||||
if(auto *reply = modbusDevice->sendWriteRequest(unit, Boards[boardId].adr)) {
|
||||
if (!reply->isFinished()) {
|
||||
if(!reply->isFinished())
|
||||
connect(reply, &QModbusReply::finished, this, [this, boardId, reply]() {
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
if(reply->error() != QModbusDevice::NoError)
|
||||
logError(tr("Плата %1 (ID %2)").arg(boardId + 1).arg(Boards[boardId].adr),
|
||||
reply->errorString(), ++Boards[boardId].error_TX,
|
||||
"Single coil write");
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
} else {
|
||||
else
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Записывает значение в один регистр (holding register) на указанной плате по Modbus.
|
||||
*
|
||||
* Проверяет, инициализировано ли соединение и активна ли плата.
|
||||
* Создает объект `QModbusDataUnit` для одного регистра по указанному адресу и присваивает ему значение.
|
||||
* Выполняет асинхронную отправку записи и обрабатывает ответ.
|
||||
*
|
||||
* @param boardID Идентификатор платы.
|
||||
* @param regAddress Адрес регистра, в который нужно записать значение.
|
||||
* @param value Значение для записи в регистр.
|
||||
*/
|
||||
void M3KTE::writeSingleRegister(int boardID, int regAddress, quint16 value)
|
||||
{
|
||||
if(!modbusDevice || !Boards[boardID].isActive)
|
||||
@@ -1790,21 +2021,30 @@ void M3KTE::writeSingleRegister(int boardID, int regAddress, quint16 value)
|
||||
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, regAddress, 1);
|
||||
unit.setValue(0, value);
|
||||
if(auto *reply = modbusDevice->sendWriteRequest(unit, Boards[boardID].adr)) {
|
||||
if (!reply->isFinished()) {
|
||||
if(!reply->isFinished())
|
||||
connect(reply, &QModbusReply::finished, this, [this, boardID, reply]() {
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
if(reply->error() != QModbusDevice::NoError)
|
||||
logError(tr("Плата %1 (ID %2)").arg(boardID + 1).arg(Boards[boardID].adr),
|
||||
reply->errorString(), ++Boards[boardID].error_TX,
|
||||
"Single register write");
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
} else {
|
||||
else
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Читает состояние одного по цепочке (coil) на указанной плате по Modbus.
|
||||
*
|
||||
* Проверяет, инициализирован ли модуль и активна ли плата.
|
||||
* Создает объект `QModbusDataUnit` для одного котла по адресу и отправляет запрос на чтение.
|
||||
* Возвращает указатель на объект `QModbusReply`, который можно использовать для обработки результата асинхронно.
|
||||
*
|
||||
* @param boardID Идентификатор платы.
|
||||
* @param coilAddress Адрес котлы (coil), состояние которой нужно прочитать.
|
||||
* @return Указатель на `QModbusReply`, или `nullptr`, если модуль не инициализирован или плата не активна.
|
||||
*/
|
||||
QModbusReply* M3KTE::readSingleCoil(int boardID, int coilAddress)
|
||||
{
|
||||
if(!modbusDevice || !Boards[boardID].isActive)
|
||||
@@ -1814,6 +2054,15 @@ QModbusReply* M3KTE::readSingleCoil(int boardID, int coilAddress)
|
||||
return reply;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Выбирает позицию в дереве и таблице UI по заданному индексу.
|
||||
*
|
||||
* Расчет осуществляется на основе индекса, деля его на блоки, соответствующие определенным
|
||||
* диапазонам элементов в интерфейсе (например, 85 элементов на плату). Затем обновляется
|
||||
* выделение и прокрутка таблицы до выбранной позиции.
|
||||
*
|
||||
* @param index Индекс элемента, который следует выделить и прокрутить.
|
||||
*/
|
||||
void M3KTE::selectPositionOnTree(unsigned int index)
|
||||
{
|
||||
ui->boardSelectBox->setCurrentIndex(index / 85);
|
||||
@@ -1823,6 +2072,22 @@ void M3KTE::selectPositionOnTree(unsigned int index)
|
||||
ui->writeValueTable->scrollTo(selected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Выполняет автоматический сканирование скорости передачи данных для устройств по Modbus.
|
||||
*
|
||||
* Эта функция перебирает стандартные скорости передачи данных (9600, 14400, 19200 и др.)
|
||||
* и пытается обнаружить устройства, отвечающие на запросы по каждой скорости.
|
||||
* В процессе происходит подключение к порту, отправка специальных запросов, проверка типа устройства
|
||||
* и последующая синхронизация устройств, если такие были найдены.
|
||||
*
|
||||
* В случае успешного обнаружения устройств, результат выводится и отображается, также выполняется
|
||||
* настройка устройств на выбранную скорость.
|
||||
*
|
||||
* Выполняет последовательные соединения, отправку запросов, обработку ответов, а также мониторинг
|
||||
* пользовательского прерывания операции через диалоговое окно прогресса.
|
||||
*
|
||||
* @return true, если сканирование завершено успешно и все устройства синхронизированы, иначе false.
|
||||
*/
|
||||
bool M3KTE::autoBaudRateScan()
|
||||
{
|
||||
unsigned countOfDeviceOnLine = 0;
|
||||
@@ -1856,9 +2121,8 @@ bool M3KTE::autoBaudRateScan()
|
||||
bar->setValue(i);
|
||||
bar->setLabelText(tr("Поиск плат... Текущая скорость: %1").arg(m_baud[i]));
|
||||
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, m_baud[i]);
|
||||
if(!modbusDevice->connectDevice()) {
|
||||
if(!modbusDevice->connectDevice())
|
||||
statusBar()->showMessage(tr("Connect failed: ") + modbusDevice->errorString(), 5000);
|
||||
}
|
||||
for(int tmp_adr = 1; tmp_adr < 248; tmp_adr++) {
|
||||
auto *reply = modbusDevice->sendRawRequest(requestOfDeviceType, tmp_adr);
|
||||
//Запрос типа устройства.
|
||||
@@ -1882,7 +2146,7 @@ bool M3KTE::autoBaudRateScan()
|
||||
bar->close();
|
||||
bar->deleteLater();
|
||||
return false;
|
||||
} else if(!isRun) {
|
||||
} else if(!isRun)
|
||||
//Нужна проверка типа устройства
|
||||
if(reply->error()==QModbusDevice::NoError) {
|
||||
QModbusResponse resp = reply->rawResult();
|
||||
@@ -1893,7 +2157,6 @@ bool M3KTE::autoBaudRateScan()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(countOfDeviceOnLine > 0)
|
||||
resultOfScan += QString("%1 плат M3KTE работают на скорости %2.\n").arg(countOfDeviceOnLine).arg(m_baud[i]);
|
||||
countOfDeviceOnLine = 0;
|
||||
@@ -1906,7 +2169,8 @@ bool M3KTE::autoBaudRateScan()
|
||||
for(int l = i; l < 8; l++) {
|
||||
if(KTE[l].indexOf(KTE[i].at(j)) == -1) {
|
||||
QMessageBox::warning(this, "Error",
|
||||
QString("Несколько устройств по адресу %1, работающих на скоростях %2 и %3.").arg(KTE[i].at(j)).arg(m_baud[i]).arg(m_baud[l]));
|
||||
QString("Несколько устройств по адресу %1, работающих на скоростях %2 и %3.")
|
||||
.arg(KTE[i].at(j)).arg(m_baud[i]).arg(m_baud[l]));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1919,9 +2183,8 @@ bool M3KTE::autoBaudRateScan()
|
||||
bar->setLabelText(tr("Синхронизация плат на скорости %1").arg(m_baud[i]));
|
||||
for(int j = 0; j < KTE[i].size(); j++) {
|
||||
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, m_baud[i]);
|
||||
if(!modbusDevice->connectDevice()) {
|
||||
if(!modbusDevice->connectDevice())
|
||||
statusBar()->showMessage(tr("Connect failed: ") + modbusDevice->errorString(), 5000);
|
||||
}
|
||||
auto *reply = modbusDevice->sendWriteRequest(*_unit, KTE[i].at(j));
|
||||
if(!reply) {
|
||||
onConnectClicked();
|
||||
@@ -1961,9 +2224,8 @@ bool M3KTE::autoBaudRateScan()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if(reply->error()!=QModbusDevice::NoError) {
|
||||
} else if(reply->error() != QModbusDevice::NoError)
|
||||
return false;
|
||||
}
|
||||
modbusDevice->disconnectDevice();
|
||||
}
|
||||
}
|
||||
@@ -1974,10 +2236,17 @@ bool M3KTE::autoBaudRateScan()
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Останавливает процесс сканирования на всех платах.
|
||||
*
|
||||
* Эта функция перебирает все четыре устройства типа Boards и вызывает
|
||||
* метод `stop()` для объекта `boardScanners`, который управляет запуском
|
||||
* и остановкой процесса сканирования. После вызова все текущие сканирования
|
||||
* на платах будут прекращены.
|
||||
*/
|
||||
void M3KTE::stopScanBoard()
|
||||
{
|
||||
for(int i = 0; i < 4; i++) {
|
||||
for(int i = 0; i < 4; i++)
|
||||
Boards[i].boardScanners->stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ private:
|
||||
QModbusDataUnit writeRequest() const;
|
||||
void changeTable(int board, int tabletype);
|
||||
bool event(QEvent* event);
|
||||
bool deadPing(QProgressDialog *bar);
|
||||
bool pingNetworkDevices();
|
||||
void beginScanBoards();
|
||||
void stopScanBoard();
|
||||
|
||||
@@ -16,6 +16,15 @@ MultipleSettings::~MultipleSettings()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработка нажатий кнопок в диалоговом окне настроек.
|
||||
*
|
||||
* В зависимости от нажатой кнопки выполняются разные действия:
|
||||
* - Если нажата кнопка "Записать", сохраняются введенные параметры и вызывается сигнал write().
|
||||
* - Если нажата кнопка "Записать и установить", сохраняются параметры и вызывается сигнал writeAndSend().
|
||||
*
|
||||
* @param button Указатель на нажатую кнопку.
|
||||
*/
|
||||
void MultipleSettings::on_buttonBox_clicked(QAbstractButton *button)
|
||||
{
|
||||
if(button == ui->buttonBox->button(QDialogButtonBox::Ok)) {
|
||||
@@ -32,9 +41,18 @@ void MultipleSettings::on_buttonBox_clicked(QAbstractButton *button)
|
||||
countReg = ui->countBox->value();
|
||||
boardId = ui->boardBox->currentIndex();
|
||||
emit writeAndSend();
|
||||
} else {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработка изменения выбранного типа регистра.
|
||||
*
|
||||
* В зависимости от выбранного типа регистра (index) и текущей выбранной платы (ui->boardBox),
|
||||
* устанавливается диапазон допустимых значений для поля адреса (ui->adrBox).
|
||||
* Также значение адреса сбрасывается на начальное.
|
||||
*
|
||||
* @param index Индекс выбранного типа регистра.
|
||||
*/
|
||||
void MultipleSettings::on_regTypeBox_currentIndexChanged(int index)
|
||||
{
|
||||
short maxRange = 0;
|
||||
@@ -58,13 +76,35 @@ void MultipleSettings::on_regTypeBox_currentIndexChanged(int index)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработка изменения выбранной платы в интерфейсе.
|
||||
*
|
||||
* Этот слот сохраняет выбранный индекс платы в переменную selectedBoard
|
||||
* и обновляет диапазон адресов в зависимости от текущего типа регистра,
|
||||
* вызывая обработчик on_regTypeBox_currentIndexChanged.
|
||||
*
|
||||
* @param index Индекс выбранной платы.
|
||||
*/
|
||||
void MultipleSettings::on_boardBox_currentIndexChanged(int index)
|
||||
{
|
||||
selectedBoard = index;
|
||||
on_regTypeBox_currentIndexChanged(ui->regTypeBox->currentIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработка изменения значения адреса (adrBox).
|
||||
*
|
||||
* При изменении значения адреса (arg1) обновляется максимальный допустимый диапазон
|
||||
* для количества регистров (countBox). Максимальное значение зависит от текущего
|
||||
* выбранного номера платы, типа регистра и текущего адреса.
|
||||
*
|
||||
* Формула для расчета диапазона:
|
||||
* max = (85 - (20 * (boardIndex / 3))) * (1 + (regTypeIndex / 2)) - arg1
|
||||
*
|
||||
* @param arg1 Новое значение адреса.
|
||||
*/
|
||||
void MultipleSettings::on_adrBox_valueChanged(int arg1)
|
||||
{
|
||||
ui->countBox->setRange(1, ((85-(20*(ui->boardBox->currentIndex()/3)))*(1+(ui->regTypeBox->currentIndex()/2))-arg1));
|
||||
ui->countBox->setRange(1, ((85 - (20 * (ui->boardBox->currentIndex() / 3)))
|
||||
* (1 + (ui->regTypeBox->currentIndex() / 2)) - arg1));
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ class MultipleSettings;
|
||||
class MultipleSettings : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MultipleSettings(QWidget *parent = nullptr);
|
||||
~MultipleSettings();
|
||||
@@ -23,16 +22,11 @@ public:
|
||||
signals:
|
||||
void write();
|
||||
void writeAndSend();
|
||||
|
||||
private slots:
|
||||
void on_buttonBox_clicked(QAbstractButton *button);
|
||||
|
||||
void on_regTypeBox_currentIndexChanged(int index);
|
||||
|
||||
void on_boardBox_currentIndexChanged(int index);
|
||||
|
||||
void on_adrBox_valueChanged(int arg1);
|
||||
|
||||
private:
|
||||
Ui::MultipleSettings *ui;
|
||||
quint16 newValue = 0;
|
||||
|
||||
@@ -17,7 +17,6 @@ ParameterBox::ParameterBox(QWidget *parent, pboxMode Mode, quint16 objectID) :
|
||||
case MTemplate:
|
||||
break;
|
||||
}
|
||||
|
||||
ui->objectIdLabel->setText("0x" + QString::number(ID, 16));
|
||||
}
|
||||
|
||||
@@ -26,20 +25,43 @@ ParameterBox::~ParameterBox()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработчик нажатия кнопки отправки (sendButton).
|
||||
*
|
||||
* При нажатии проверяется, заполнено ли выбранное значение в valueBox.
|
||||
* Если значение пустое, выполнение прерывается.
|
||||
* В противном случае генерируется сигнал writeParameter с текущим адресом и выбранным значением (в шестнадцатеричном формате).
|
||||
*/
|
||||
void ParameterBox::on_sendButton_clicked()
|
||||
{
|
||||
if(ui->valueBox->currentText().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
emit writeParameter(ui->adrLine->text().toInt(), ui->valueBox->currentText().toInt(nullptr, 16));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Установка текста параметра.
|
||||
*
|
||||
* Этот метод устанавливает переданный текст (data) в поле для имени параметра (nameLine).
|
||||
*
|
||||
* @param data Текст для отображения в поле имени.
|
||||
*/
|
||||
void ParameterBox::setData(QString data)
|
||||
{
|
||||
ui->nameLine->setText(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Установка данных в параметры в зависимости от режима работы.
|
||||
*
|
||||
* В зависимости от текущего режима (boxMode) заполняются соответствующие элементы интерфейса:
|
||||
* - При режиме Info заполняется только поле имени.
|
||||
* - При режиме MTemplate заполняются поля имени, адреса и список значений.
|
||||
*
|
||||
* @param name Имя параметра.
|
||||
* @param adr Адрес параметра.
|
||||
* @param values Список значений для выбора.
|
||||
*/
|
||||
void ParameterBox::setData(QString name, QString adr, QStringList values)
|
||||
{
|
||||
switch(boxMode) {
|
||||
|
||||
@@ -10,7 +10,6 @@ class ParameterBox;
|
||||
class ParameterBox : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum pboxMode{
|
||||
Info = 0,
|
||||
@@ -20,10 +19,8 @@ public:
|
||||
Coil = 0,
|
||||
HR = 1
|
||||
};
|
||||
|
||||
explicit ParameterBox(QWidget *parent = nullptr, pboxMode Mode = Info, quint16 objectID = 0x80);
|
||||
~ParameterBox();
|
||||
|
||||
void setData(QString data);
|
||||
void setData(QString name, QString adr, QStringList values);
|
||||
quint16 getID(){return ID;}
|
||||
@@ -32,7 +29,6 @@ signals:
|
||||
void writeParameter(int adr, quint16 value);
|
||||
private slots:
|
||||
void on_sendButton_clicked();
|
||||
|
||||
private:
|
||||
quint16 ID;
|
||||
pboxMode boxMode;
|
||||
|
||||
@@ -13,10 +13,6 @@ ParameterDevice::ParameterDevice(QWidget *parent) :
|
||||
ui(new Ui::ParameterDevice)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
updateTimer = new QTimer(this);
|
||||
updateTimer->setSingleShot(true);
|
||||
connect(updateTimer, &QTimer::timeout, this, &ParameterDevice::sortScrollArea);
|
||||
}
|
||||
|
||||
ParameterDevice::~ParameterDevice()
|
||||
@@ -24,29 +20,55 @@ ParameterDevice::~ParameterDevice()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработка выбора режима "Выборочный" (selectiveRadio).
|
||||
*
|
||||
* Включает доступность элементов управления для выбора выборочного режима
|
||||
* и устанавливает режим работы на `selectiveRequest`.
|
||||
*/
|
||||
void ParameterDevice::on_selectiveRadio_clicked()
|
||||
{
|
||||
// Включение элементов для выбора выборочного режима
|
||||
ui->selectiveBox->setEnabled(true);
|
||||
// Установка текущего режима
|
||||
mode = selectiveRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработка выбора режима "Полный" (fullRadio).
|
||||
*
|
||||
* Выключает элементы управления для выборочного режима,
|
||||
* а также устанавливает режим работы на `fullRequest`.
|
||||
*/
|
||||
void ParameterDevice::on_fullRadio_clicked()
|
||||
{
|
||||
// Отключение элементов для выборочного режима
|
||||
ui->selectiveBox->setEnabled(false);
|
||||
// Установка текущего режима
|
||||
mode = fullRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработка нажатия на кнопку checkButton.
|
||||
*
|
||||
* Выполняет чтение данных по определенной логике (полный или выборочный запрос),
|
||||
* отображает прогресс-бар, измеряет общее время выполнения и обновляет UI.
|
||||
*
|
||||
* @note В процессе выполнения очищает текущие параметры, инициирует последовательное чтение данных,
|
||||
* отображает прогресс, а по завершении показывает затраченное время.
|
||||
*/
|
||||
void ParameterDevice::on_checkButton_clicked()
|
||||
{
|
||||
for (ParameterBox* box : parameterBoxes) {
|
||||
if (box) {
|
||||
// Очистка старых параметров
|
||||
for (ParameterBox* box : parameterBoxes)
|
||||
if (box)
|
||||
box->deleteLater();
|
||||
}
|
||||
}
|
||||
parameterBoxes.clear();
|
||||
quint16 strAdr;
|
||||
int cnt;
|
||||
int cnt = 0;
|
||||
// Установка стартового адреса
|
||||
strAdr = ui->adrSpin->value();
|
||||
// В зависимости от режима задаем диапазон чтения
|
||||
switch (mode) {
|
||||
case fullRequest:
|
||||
strAdr = 0x80;
|
||||
@@ -56,94 +78,157 @@ void ParameterDevice::on_checkButton_clicked()
|
||||
cnt = ui->countSpin->value();
|
||||
break;
|
||||
}
|
||||
// Настройка таймера для измерения времени
|
||||
QElapsedTimer timer;
|
||||
// Создаем диалог прогресса
|
||||
QProgressDialog progressDialog("Обработка...", "Отмена", 0, cnt, this);
|
||||
progressDialog.setWindowModality(Qt::WindowModal);
|
||||
progressDialog.setMinimumDuration(0); // показывать сразу
|
||||
progressDialog.setMinimumDuration(0); // показывать сразу же
|
||||
// Создаем цикл событий для ожидания завершения
|
||||
QEventLoop loop;
|
||||
// Соединяем сигнал завершения передачи с выходом из цикла
|
||||
connect(this, &ParameterDevice::transmitEnd, &loop, &QEventLoop::quit);
|
||||
// Запускаем таймер
|
||||
timer.start();
|
||||
// Цикл чтения данных
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
// Обновляем прогресс
|
||||
// Обновление прогресс-бара
|
||||
progressDialog.setValue(i);
|
||||
if (progressDialog.wasCanceled()) {
|
||||
break; // пользователь отменил
|
||||
}
|
||||
if (progressDialog.wasCanceled())
|
||||
break; // пользователь отменил выполнение
|
||||
// Формируем запрос
|
||||
QByteArray data = QByteArray::fromHex("0E04");
|
||||
data.append(strAdr + i);
|
||||
QModbusRequest request(QModbusRequest::EncapsulatedInterfaceTransport, data);
|
||||
// Отправка запроса
|
||||
emit read(request, strAdr + i);
|
||||
// Ожидание завершения передачи
|
||||
loop.exec();
|
||||
if(errorAtTransmit)
|
||||
{
|
||||
// Проверка на ошибку
|
||||
if (errorAtTransmit) {
|
||||
errorAtTransmit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Расчет затраченного времени
|
||||
qint64 elapsedNanoSeconds = timer.nsecsElapsed();
|
||||
// Переводим в минуты, секунды, миллисекунды, микросекунды
|
||||
qint64 totalMicroseconds = elapsedNanoSeconds / 1000;
|
||||
qint64 totalMilliseconds = totalMicroseconds / 1000;
|
||||
qint64 totalSeconds = totalMilliseconds / 1000;
|
||||
qint64 totalMinutes = totalSeconds / 60;
|
||||
qint64 remainingMicroseconds = totalMicroseconds % 1000;
|
||||
qint64 remainingMilliseconds = totalMilliseconds % 1000;
|
||||
qint64 remainingSeconds = totalSeconds % 60;
|
||||
// Формируем строку для отображения времени
|
||||
QString timeString = QString("%1 мин %2 сек %3 мс %4 мкс")
|
||||
.arg(totalMinutes)
|
||||
.arg(remainingSeconds)
|
||||
.arg(remainingMilliseconds)
|
||||
.arg(remainingMicroseconds);
|
||||
// Выводим сообщение с временем выполнения
|
||||
QMessageBox::information(this, "Общее время выполнения", timeString);
|
||||
// Устанавливаем прогресс в конец
|
||||
progressDialog.setValue(cnt);
|
||||
// Сортируем параметры по ID и обновляем интерфейс
|
||||
sortParameterBoxesByID(parameterBoxes);
|
||||
sortScrollArea();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработка ошибки при передаче данных.
|
||||
*
|
||||
* Показывает информационное сообщение с текстом ошибки,
|
||||
* устанавливает флаг errorAtTransmit в true,
|
||||
* а затем сигнализирует о завершении передачи.
|
||||
*
|
||||
* @param error Текст ошибки, который будет отображен пользователю.
|
||||
*/
|
||||
void ParameterDevice::setError(QString error)
|
||||
{
|
||||
|
||||
QMessageBox::information(nullptr, "Получен ответ", error);
|
||||
errorAtTransmit = true;
|
||||
emit transmitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработчик изменения значения спинбокса адреса (adrSpin).
|
||||
*
|
||||
* При изменении значения аргумента (arg1) обновляет диапазон допустимых
|
||||
* значений для другого спинбокса (countSpin), чтобы сумма не превышала 256.
|
||||
*
|
||||
* Устанавливает диапазон: от 1 до (256 - текущего значения adrSpin).
|
||||
*
|
||||
* @param arg1 Новое значение адреса.
|
||||
*/
|
||||
void ParameterDevice::on_adrSpin_valueChanged(int arg1)
|
||||
{
|
||||
ui->countSpin->setRange(1, 256 - arg1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Установка ответа и создание нового параметра.
|
||||
*
|
||||
* Этот метод создает новый объект ParameterBox, устанавливает его данные,
|
||||
* и в зависимости от типа параметра (Coil или HR) устанавливает соответствующие соединения.
|
||||
* После этого добавляет его в список и сигнализирует о завершении передачи.
|
||||
*
|
||||
* @param reply Текст ответа, который отображается в параметре.
|
||||
* @param objectID Идентификатор объекта, связанного с этим параметром.
|
||||
*/
|
||||
void ParameterDevice::setAnswer(QString reply, quint16 objectID)
|
||||
{
|
||||
// Создаем новый объект ParameterBox в режиме Info
|
||||
ParameterBox *newbox = new ParameterBox(nullptr, ParameterBox::Info, objectID);
|
||||
// Устанавливаем данные ответа в созданный параметр
|
||||
newbox->setData(reply);
|
||||
// В зависимости от типа параметра устанавливаем соединения
|
||||
switch(newbox->getType()) {
|
||||
case ParameterBox::Coil:
|
||||
// Для типа Coil связываем сигнал writeParameter с выводом writeSingleCoil
|
||||
connect(newbox, &ParameterBox::writeParameter, this, [this](int adr, quint16 value){
|
||||
emit writeSingleCoil(adr, (bool)value);
|
||||
});
|
||||
break;
|
||||
case ParameterBox::HR:
|
||||
// Для типа HR связываем сигнал writeParameter с выводом writeSingleRegister
|
||||
connect(newbox, &ParameterBox::writeParameter, this, [this](int adr, quint16 value){
|
||||
emit writeSingleRegister(adr, value);
|
||||
});
|
||||
break;
|
||||
}
|
||||
// Добавляем созданный параметр в список
|
||||
parameterBoxes.append(newbox);
|
||||
// Посылаем сигнал о завершении передачи
|
||||
emit transmitEnd();
|
||||
|
||||
// // Остановить, если уже запущен
|
||||
// if (updateTimer->isActive()) {
|
||||
// updateTimer->stop();
|
||||
// }
|
||||
// // Запустить с задержкой 6 секунд
|
||||
// updateTimer->start(6000);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Перестроение (сортировка) содержимого внутри scrollArea.
|
||||
*
|
||||
* Этот метод очищает текущий лейаут внутри UI элемента parameterBoxHubContents,
|
||||
* затем добавляет все параметры из списка parameterBoxes обратно в лейаут.
|
||||
* После этого обновляются и перерасчитываются размеры элементов интерфейса.
|
||||
*/
|
||||
void ParameterDevice::sortScrollArea()
|
||||
{
|
||||
//sortParameterBoxesByID(parameterBoxes);
|
||||
//ui->parameterBoxHubContents->layout()->deleteLater();
|
||||
// Получение текущего лейаута внутри parameterBoxHubContents
|
||||
QLayout* pblayout = ui->parameterBoxHubContents->layout();
|
||||
// Если лейаут отсутствует, создаем новый и устанавливаем его
|
||||
if (!pblayout) {
|
||||
// Создайте новый лейаут, если его нет
|
||||
pblayout = new QVBoxLayout(ui->parameterBoxHubContents);
|
||||
ui->parameterBoxHubContents->setLayout(pblayout);
|
||||
}
|
||||
// Очистка текущих элементов из лейаута
|
||||
QLayoutItem *item;
|
||||
while ((item = pblayout->takeAt(0)) != nullptr) {
|
||||
//delete item;
|
||||
delete item;
|
||||
}
|
||||
|
||||
//ui->parameterBoxHubContents->setLayout(pblayout);
|
||||
|
||||
// Добавление всех параметров из списка parameterBoxes обратно в лейаут
|
||||
for (int i = 0; i < parameterBoxes.count(); i++) {
|
||||
pblayout->addWidget(parameterBoxes.at(i));
|
||||
}
|
||||
|
||||
// Обновление размеров и отображения интерфейса
|
||||
ui->parameterBoxHubContents->update();
|
||||
ui->parameterBoxHubContents->adjustSize();
|
||||
ui->scrollArea->updateGeometry();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <QTimer>
|
||||
#include <QMessageBox>
|
||||
#include <QProgressDialog>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
void sortParameterBoxesByID(QVector<ParameterBox*>& parameterBoxes);
|
||||
|
||||
@@ -19,7 +20,6 @@ class ParameterDevice;
|
||||
class ParameterDevice : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum requestMode{
|
||||
fullRequest = 0,
|
||||
@@ -37,24 +37,16 @@ signals:
|
||||
void writeSingleCoil(int coilAddress, bool value);
|
||||
void writeSingleRegister(int registerAddress, quint16 value);
|
||||
void transmitEnd();
|
||||
|
||||
private slots:
|
||||
void on_selectiveRadio_clicked();
|
||||
|
||||
void on_fullRadio_clicked();
|
||||
|
||||
void on_checkButton_clicked();
|
||||
|
||||
void on_adrSpin_valueChanged(int arg1);
|
||||
|
||||
private:
|
||||
void sortScrollArea();
|
||||
|
||||
private:
|
||||
int adr;
|
||||
QTimer *updateTimer;
|
||||
QVector<ParameterBox*>parameterBoxes;
|
||||
|
||||
requestMode mode = fullRequest;
|
||||
Ui::ParameterDevice *ui;
|
||||
};
|
||||
|
||||
@@ -6,116 +6,185 @@ ParameterWorkspace::ParameterWorkspace(QWidget *parent) :
|
||||
ui(new Ui::ParameterWorkspace)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
}
|
||||
|
||||
ParameterWorkspace::~ParameterWorkspace()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
// QModbusRequest requestOfDeviceType(QModbusRequest::EncapsulatedInterfaceTransport, QByteArray::fromHex("0E0404"));
|
||||
// auto *reply = modbusDevice->sendRawRequest(requestOfDeviceType, tmp_adr);
|
||||
|
||||
/**
|
||||
* @brief Устанавливает количество устройств и обновляет интерфейс.
|
||||
*
|
||||
* Очистка текущего списка устройств и удаления соответствующих вкладок,
|
||||
* затем создание новых устройств и добавление их в интерфейс.
|
||||
*
|
||||
* @param count Новое количество устройств.
|
||||
*/
|
||||
void ParameterWorkspace::setDeviceCount(int count)
|
||||
{
|
||||
for(const auto& device : deviceList)
|
||||
{
|
||||
device.tab->deleteLater();
|
||||
device.device->deleteLater();
|
||||
// Удаление и очистка текущих устройств
|
||||
for (const auto& device : deviceList) {
|
||||
device.tab->deleteLater(); ///< Удаление вкладки устройства
|
||||
device.device->deleteLater(); ///< Удаление объекта устройства
|
||||
}
|
||||
deviceList.clear();
|
||||
for(int i = 0; i < ui->tabWidget->count(); i++)
|
||||
deviceList.clear(); ///< Очистка списка устройств
|
||||
// Удаление всех вкладок из таб-виджета
|
||||
for (int i = 0; i < ui->tabWidget->count(); ++i) {
|
||||
ui->tabWidget->removeTab(i);
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
device* _device = new device();
|
||||
deviceList.append(*_device);
|
||||
deviceList[i].device = new ParameterDevice();
|
||||
}
|
||||
// Создание новых устройств
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto _device = new device(); ///< Создаваемое устройство
|
||||
deviceList.append(*_device); ///< Добавление в список
|
||||
deviceList[i].device = new ParameterDevice(); ///< Создание объекта ParameterDevice
|
||||
// Добавление вкладки для устройства
|
||||
int newtab = ui->tabWidget->addTab(deviceList[i].device, tr("Device №%1").arg(i + 1));
|
||||
deviceList[i].tab = ui->tabWidget->widget(newtab);
|
||||
connect(deviceList[i].device, &ParameterDevice::read, this, [this, i](QModbusRequest request, quint16 objectID){
|
||||
// Подключение сигналов от устройства к слотам
|
||||
connect(deviceList[i].device, &ParameterDevice::read, this,
|
||||
[this, i](QModbusRequest request, quint16 objectID) {
|
||||
readDeviceIdentification(deviceList[i].device, request, deviceList[i].adr, objectID);
|
||||
});
|
||||
connect(deviceList[i].device, &ParameterDevice::writeSingleCoil, this, [this, i](int coilAddress, bool value){
|
||||
connect(deviceList[i].device, &ParameterDevice::writeSingleCoil, this,
|
||||
[this, i](int coilAddress, bool value){
|
||||
writeSingleCoil(deviceList[i].adr, coilAddress, value);
|
||||
});
|
||||
connect(deviceList[i].device, &ParameterDevice::writeSingleRegister, this, [this, i](int registerAddress, quint16 value){
|
||||
connect(deviceList[i].device, &ParameterDevice::writeSingleRegister, this,
|
||||
[this, i](int registerAddress, quint16 value){
|
||||
writeSingleRegister(deviceList[i].adr, registerAddress, value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отправляет команду на изменение состояния одиночного котла по Modbus.
|
||||
*
|
||||
* Проверяет состояние соединения, создает запрос и отправляет его.
|
||||
* Обрабатывает завершение запроса, проверяя наличие ошибок.
|
||||
*
|
||||
* @param adr Адрес устройства по Modbus.
|
||||
* @param coilAddress Адрес катушки (котла).
|
||||
* @param value Значение для установки (true — включено, false — выключено).
|
||||
*/
|
||||
void ParameterWorkspace::writeSingleCoil(int adr, int coilAddress, bool value)
|
||||
{
|
||||
if (!modbusDevice && modbusDevice->state() == QModbusDevice::ConnectedState)
|
||||
// Проверка правильности состояния соединения
|
||||
if (!modbusDevice || modbusDevice->state() != QModbusDevice::ConnectedState)
|
||||
return;
|
||||
// Создание пакета данных для записи
|
||||
QModbusDataUnit unit(QModbusDataUnit::Coils, coilAddress, 1);
|
||||
unit.setValue(0, value ? 1 : 0);
|
||||
// Отправка запроса
|
||||
if (auto *reply = modbusDevice->sendWriteRequest(unit, adr)) {
|
||||
// Обработка завершения запроса
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [this, reply]() {
|
||||
connect(reply, &QModbusReply::finished, this, [reply]() {
|
||||
// Проверка ошибок выполнения
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
//ERROR
|
||||
// Обработка ошибок (можно добавить лог или сообщение)
|
||||
}
|
||||
reply->deleteLater();
|
||||
reply->deleteLater(); // Очистка объекта
|
||||
});
|
||||
} else {
|
||||
// Если ответ уже готов, очищаем его сразу
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Отправляет команду на запись одного регистрового значения по Modbus.
|
||||
*
|
||||
* Проверяет состояние соединения, создает запрос и отправляет его.
|
||||
* Обрабатывает завершение запроса и возможные ошибки.
|
||||
*
|
||||
* @param adr Адрес устройства по Modbus.
|
||||
* @param regAddress Адрес регистрового аргумента.
|
||||
* @param value Значение для записи.
|
||||
*/
|
||||
void ParameterWorkspace::writeSingleRegister(int adr, int regAddress, quint16 value)
|
||||
{
|
||||
if (!modbusDevice && modbusDevice->state() == QModbusDevice::ConnectedState)
|
||||
// Проверка правильности состояния соединения
|
||||
if (!modbusDevice || modbusDevice->state() != QModbusDevice::ConnectedState)
|
||||
return;
|
||||
// Создание пакета данных для записи
|
||||
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, regAddress, 1);
|
||||
unit.setValue(0, value);
|
||||
// Отправка запроса
|
||||
if (auto *reply = modbusDevice->sendWriteRequest(unit, adr)) {
|
||||
// Обработка завершения запроса
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [this, reply]() {
|
||||
connect(reply, &QModbusReply::finished, this, [reply]() {
|
||||
// Проверка наличия ошибок
|
||||
if (reply->error() != QModbusDevice::NoError) {
|
||||
//ERROR
|
||||
// Можно добавить лог или обработку ошибок
|
||||
}
|
||||
reply->deleteLater();
|
||||
reply->deleteLater(); // Очистка объекта
|
||||
});
|
||||
} else {
|
||||
// Если ответ уже готов, очищаем его сразу
|
||||
reply->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Читает идентификационную информацию устройства по Modbus.
|
||||
*
|
||||
* Отправляет необработанный запрос к устройству и обрабатывает ответ.
|
||||
* В случае успешного ответа извлекает данные и вызывает метод `setAnswer`.
|
||||
* В случае ошибки вызывает `setError` и выводит сообщение в лог.
|
||||
*
|
||||
* @param device Указатель на устройство, которому предназначен ответ.
|
||||
* @param request Объект запроса Modbus.
|
||||
* @param adr Адрес устройства по Modbus.
|
||||
* @param objectID Идентификатор объекта для обработки.
|
||||
*/
|
||||
void ParameterWorkspace::readDeviceIdentification(ParameterDevice *device, QModbusRequest request, int adr, quint16 objectID)
|
||||
{
|
||||
if (!modbusDevice && modbusDevice->state() == QModbusDevice::ConnectedState)
|
||||
// Проверка состояния соединения
|
||||
if (!modbusDevice || modbusDevice->state() != QModbusDevice::ConnectedState)
|
||||
return;
|
||||
if(auto *reply = modbusDevice->sendRawRequest(request, adr))
|
||||
{
|
||||
// Отправка сырого запроса к устройству
|
||||
if (auto *reply = modbusDevice->sendRawRequest(request, adr)) {
|
||||
// Обработка ответа, если он не завершен
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [this, device, reply, objectID](){
|
||||
if(reply->error() == QModbusDevice::NoError)
|
||||
{
|
||||
connect(reply, &QModbusReply::finished, this, [device, reply, objectID]() {
|
||||
// Проверка ошибок
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
QModbusResponse resp = reply->rawResult();
|
||||
if(resp.data().size() >= MODBUS_REQUEST_PROTOCOL_INFO_LENGTH)
|
||||
{
|
||||
QString result = QString(resp.data().remove(0, MODBUS_REQUEST_PROTOCOL_INFO_LENGTH));
|
||||
// Проверка размера данных
|
||||
if (resp.data().size() >= MODBUS_REQUEST_PROTOCOL_INFO_LENGTH) {
|
||||
// Удаление протокольной части данных
|
||||
QByteArray data = resp.data();
|
||||
data.remove(0, MODBUS_REQUEST_PROTOCOL_INFO_LENGTH);
|
||||
QString result = QString::fromUtf8(data); // или другой метод преобразования, в зависимости от формата
|
||||
device->setAnswer(result, objectID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Обработка ошибок
|
||||
device->setError(reply->errorString());
|
||||
qDebug()<<"Получен ответ:" + reply->errorString();
|
||||
qDebug() << "Получен ответ с ошибкой:" << reply->errorString();
|
||||
}
|
||||
reply->deleteLater();
|
||||
reply->deleteLater(); // Очистка объекта
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Ошибка при отправке запроса
|
||||
device->setError("Unknow error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обновляет параметры устройства по идентификатору.
|
||||
*
|
||||
* Обновляет статус активности, адрес и устанавливает новый адрес в объекте устройства.
|
||||
*
|
||||
* @param ID Индекс или уникальный идентификатор устройства в списке.
|
||||
* @param status Новый статус активности (true/false).
|
||||
* @param adr Новый адрес устройства.
|
||||
*/
|
||||
void ParameterWorkspace::updateDevice(int ID, bool status, int adr)
|
||||
{
|
||||
deviceList[ID].isActive = status;
|
||||
|
||||
@@ -24,7 +24,6 @@ class ParameterWorkspace;
|
||||
class ParameterWorkspace : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ParameterWorkspace(QWidget *parent = nullptr);
|
||||
~ParameterWorkspace();
|
||||
@@ -32,12 +31,10 @@ public:
|
||||
void setModbusClient(QModbusClient *device){modbusDevice = device;}
|
||||
void setDeviceCount(int count);
|
||||
void updateDevice(int deviceID, bool status, int adr);
|
||||
|
||||
private slots:
|
||||
void readDeviceIdentification(ParameterDevice *board, QModbusRequest request, int adr, quint16 objectID);
|
||||
void writeSingleCoil(int adr, int coilAddress, bool value);
|
||||
void writeSingleRegister(int adr, int registerAddress, quint16 value);
|
||||
|
||||
private:
|
||||
struct device{
|
||||
bool isActive = false;
|
||||
|
||||
@@ -13,26 +13,52 @@ ScanBoard::~ScanBoard()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Получает текущий состояние чекбокса.
|
||||
* @return Текущий Qt::CheckState.
|
||||
*/
|
||||
Qt::CheckState ScanBoard::getCheckState()
|
||||
{
|
||||
return checkState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработчик изменения состояния чекбокса "Apply to All".
|
||||
* @param arg1 Новое состояние чекбокса (целое число, преобразуемое в Qt::CheckState).
|
||||
*
|
||||
* Этот слот вызывается при изменении состояния чекбокса и сохраняет новое значение.
|
||||
*/
|
||||
void ScanBoard::on_applyToAllBox_stateChanged(int arg1)
|
||||
{
|
||||
checkState = (Qt::CheckState)arg1;
|
||||
checkState = static_cast<Qt::CheckState>(arg1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Выводит результат сканирования в лог.
|
||||
* @param resultOfScan Строка с результатом сканирования.
|
||||
*
|
||||
* Добавляет результат сканирования в лог-виджет ui->logger.
|
||||
*/
|
||||
void ScanBoard::showMeTheTruth(QString resultOfScan)
|
||||
{
|
||||
ui->logger->append(resultOfScan);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Получает текущий baud-скорость.
|
||||
* @return Значение скорости в виде quint16.
|
||||
*/
|
||||
quint16 ScanBoard::getBaud()
|
||||
{
|
||||
return baud;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обработчик изменения текста в поле выбора скорости baud.
|
||||
* @param arg1 Новое значение текса в comboBox.
|
||||
*
|
||||
* Парсит текст в целое число и сохраняет в переменную baud.
|
||||
*/
|
||||
void ScanBoard::on_baudRateBox_currentTextChanged(const QString &arg1)
|
||||
{
|
||||
baud = arg1.toInt(nullptr, 10);
|
||||
|
||||
@@ -14,19 +14,15 @@ class ScanBoard : public QDialog
|
||||
public:
|
||||
explicit ScanBoard(QWidget *parent = nullptr);
|
||||
~ScanBoard();
|
||||
|
||||
Qt::CheckState getCheckState();
|
||||
void showMeTheTruth(QString resultOfScan);
|
||||
quint16 getBaud();
|
||||
private slots:
|
||||
void on_applyToAllBox_stateChanged(int arg1);
|
||||
|
||||
void on_baudRateBox_currentTextChanged(const QString &arg1);
|
||||
|
||||
private:
|
||||
Qt::CheckState checkState;
|
||||
quint16 baud;
|
||||
|
||||
Ui::ScanBoard *ui;
|
||||
};
|
||||
|
||||
|
||||
@@ -36,42 +36,74 @@ SettingsDialog::~SettingsDialog()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Возвращает текущие настройки.
|
||||
* @return Объект настроек типа Settings.
|
||||
*/
|
||||
SettingsDialog::Settings SettingsDialog::settings() const
|
||||
{
|
||||
return m_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обновляет индекс baud-скорости в UI и сохраняет значение в настройки.
|
||||
* @param baud Индекс выбранной скорости.
|
||||
* @return Новое значение baud, сохранённое в настройках.
|
||||
*/
|
||||
int SettingsDialog::UpdateBaud(int baud)
|
||||
{
|
||||
// Устанавливаем текущий индекс в comboBox
|
||||
ui->baudCombo->setCurrentIndex(baud);
|
||||
// Обновляем настройки и возвращаем новую скорость как число
|
||||
return (m_settings.baud = ui->baudCombo->currentText().toInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обновляет индекс паритета в UI и сохраняет значение в настройки.
|
||||
* @param parity Индекс выбранного паритета.
|
||||
* @return Новое значение паритета, сохранённое в настройках.
|
||||
*/
|
||||
int SettingsDialog::UpdateParity(int parity)
|
||||
{
|
||||
// Устанавливаем текущий индекс
|
||||
ui->parityCombo->setCurrentIndex(parity);
|
||||
if(parity>0) {
|
||||
// Если parity > 0, увеличиваем его значение перед сохранением
|
||||
if (parity > 0)
|
||||
return (m_settings.parity = ++parity);
|
||||
} else {
|
||||
else
|
||||
return (m_settings.parity = parity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Получает текущий индекс выбранной baud-скорости.
|
||||
* @return Индекс выбранного элемента в comboBox.
|
||||
*/
|
||||
int SettingsDialog::curBaud()
|
||||
{
|
||||
return ui->baudCombo->currentIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Получает текущий индекс выбранного паритета.
|
||||
* @return Индекс выбранного элемента в comboBox.
|
||||
*/
|
||||
int SettingsDialog::curParity()
|
||||
{
|
||||
return ui->parityCombo->currentIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Обновляет список доступных COM-портов.
|
||||
*
|
||||
* Этот метод вызывается при нажатии кнопки, обновляя список портов в UI.
|
||||
* Он очищает текущий список и заполняет его всеми доступными портами.
|
||||
*/
|
||||
void SettingsDialog::on_updateComBox_clicked()
|
||||
{
|
||||
ui->comBox->clear();
|
||||
const auto listPorts = QSerialPortInfo::availablePorts();
|
||||
for (const auto& port : listPorts) {
|
||||
ui->comBox->addItem(QString(port.portName() + ": " + port.manufacturer()), QVariant(port.portName()));
|
||||
// Добавляем элемент с именем порта и производителем
|
||||
ui->comBox->addItem(QString("%1: %2").arg(port.portName(), port.manufacturer()), QVariant(port.portName()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,20 +27,15 @@ public:
|
||||
int responseTime = 1000;
|
||||
int numberOfRetries = 0;
|
||||
};
|
||||
|
||||
explicit SettingsDialog(QWidget *parent = nullptr);
|
||||
~SettingsDialog();
|
||||
|
||||
Settings settings() const;
|
||||
|
||||
int UpdateBaud(int baud);
|
||||
int UpdateParity(int parity);
|
||||
|
||||
int curBaud();
|
||||
int curParity();
|
||||
private slots:
|
||||
void on_updateComBox_clicked();
|
||||
|
||||
private:
|
||||
Settings m_settings;
|
||||
Ui::SettingsDialog *ui;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "writeregistermodel.h"
|
||||
|
||||
#include "writeregistermodel.h"
|
||||
|
||||
enum { NumColumn = 0, NameColumn = 1, CoilsColumn = 2, HoldingColumn = 3, ColumnCount = 5, CurrentUColumn = 4};
|
||||
|
||||
WriteRegisterModel::WriteRegisterModel(QObject *parent, int _tmpRC, bool _isHR)
|
||||
@@ -21,25 +19,56 @@ int WriteRegisterModel::columnCount(const QModelIndex &/*parent*/) const
|
||||
return ColumnCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Возвращает данные модели для отображения в представлении.
|
||||
*
|
||||
* В зависимости от роли и столбца возвращает соответствующее значение:
|
||||
* - номер строки;
|
||||
* - имя;
|
||||
* - состояние чекбокса;
|
||||
* - значение в Вольтах для регистров и текущего напряжения.
|
||||
*
|
||||
* @param index Индекс элемента модели.
|
||||
* @param role Роль запрашиваемых данных.
|
||||
* @return Значение данных в виде QVariant.
|
||||
*/
|
||||
QVariant WriteRegisterModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
// Проверка корректности индекса
|
||||
if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
|
||||
return QVariant();
|
||||
// Проверка соответствия размеров
|
||||
Q_ASSERT(m_coils.count() == RowCount);
|
||||
Q_ASSERT(m_holdingRegisters.count() == RowCount);
|
||||
// Обработка столбца с номером
|
||||
if (index.column() == NumColumn && role == Qt::DisplayRole)
|
||||
return QString::number(index.row());
|
||||
// Обработка столбца с именем
|
||||
if (index.column() == NameColumn && role == Qt::DisplayRole)
|
||||
return QString("ТЭ%1").arg(index.row()%(RowCount/(1+isHR))+1);
|
||||
if (index.column() == CoilsColumn && role == Qt::CheckStateRole) // coils
|
||||
return QString("ТЭ%1").arg(index.row() % (RowCount / (1 + isHR)));
|
||||
// Обработка чекбокса
|
||||
if (index.column() == CoilsColumn && role == Qt::CheckStateRole)
|
||||
return m_coils.at(index.row()) ? Qt::Checked : Qt::Unchecked;
|
||||
if (index.column() == HoldingColumn && role == Qt::DisplayRole) // holding registers
|
||||
return QString("%1 В").arg(QString::number((double)((double)m_holdingRegisters.at(index.row())/(double)1000), 'f', 3));
|
||||
// Обработка значения в регистре (в Вольтах)
|
||||
if (index.column() == HoldingColumn && role == Qt::DisplayRole)
|
||||
return QString("%1 В").arg(QString::number((double)m_holdingRegisters.at(index.row()) / 1000.0, 'f', 3));
|
||||
// Обработка текущего напряжения (в Вольтах)
|
||||
if (index.column() == CurrentUColumn && role == Qt::DisplayRole)
|
||||
return QString("%1 В").arg(QString::number((double)((double)m_currentU.at(index.row())/(double)1000), 'f', 3));
|
||||
return QString("%1 В").arg(QString::number((double)m_currentU.at(index.row()) / 1000.0, 'f', 3));
|
||||
// Возврат пустого QVariant по умолчанию
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Возвращает заголовки столбцов для отображения в представлении.
|
||||
*
|
||||
* Обеспечивает отображение названий для горизонтальных заголовков.
|
||||
*
|
||||
* @param section Индекс столбца.
|
||||
* @param orientation Ориентация (горизонтальная или вертикальная).
|
||||
* @param role Роль данных, обычно Qt::DisplayRole.
|
||||
* @return Заголовок в виде QVariant или пустой QVariant при несоответствии.
|
||||
*/
|
||||
QVariant WriteRegisterModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole)
|
||||
@@ -63,69 +92,136 @@ QVariant WriteRegisterModel::headerData(int section, Qt::Orientation orientation
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Устанавливает данные модели по индексу.
|
||||
*
|
||||
* Обработка для чекбоксов (CoilsColumn) и регистров (HoldingColumn).
|
||||
*
|
||||
* @param index Индекс элемента.
|
||||
* @param value Новое значение.
|
||||
* @param role Роль, определяющая тип данных (например, Qt::CheckStateRole, Qt::EditRole).
|
||||
* @return true, если данные успешно установлены; иначе false.
|
||||
*/
|
||||
bool WriteRegisterModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
// Проверяем валидность индекса и границы
|
||||
if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
|
||||
return false;
|
||||
// Проверка размеров массивов
|
||||
Q_ASSERT(m_coils.count() == RowCount);
|
||||
Q_ASSERT(m_holdingRegisters.count() == RowCount);
|
||||
if (index.column() == CoilsColumn && role == Qt::CheckStateRole) { // coils
|
||||
auto s = static_cast<Qt::CheckState>(value.toUInt());
|
||||
s == Qt::Checked ? m_coils.setBit(index.row()) : m_coils.clearBit(index.row());
|
||||
// Обработка для чекбоксов
|
||||
if (index.column() == CoilsColumn && role == Qt::CheckStateRole) {
|
||||
auto state = static_cast<Qt::CheckState>(value.toUInt());
|
||||
if (state == Qt::Checked)
|
||||
m_coils.setBit(index.row());
|
||||
else
|
||||
m_coils.clearBit(index.row());
|
||||
emit dataChanged(index, index);
|
||||
return true;
|
||||
}
|
||||
if (index.column() == HoldingColumn && role == Qt::EditRole) { // holding registers
|
||||
// Обработка для регистров
|
||||
if (index.column() == HoldingColumn && role == Qt::EditRole) {
|
||||
bool result = false;
|
||||
quint16 newValue = value.toString().toUShort(&result, 10);
|
||||
if (result)
|
||||
// Преобразование QVariant в строку, затем в число
|
||||
QString valueStr = value.toString();
|
||||
quint16 newValue = valueStr.toUShort(&result, 10);
|
||||
if (result) {
|
||||
m_holdingRegisters[index.row()] = newValue;
|
||||
emit dataChanged(index, index);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// Если не обработано, вернуть false
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Устанавливает флаги для элементов модели, определяя их поведение.
|
||||
*
|
||||
* @param index Индекс элемента модели.
|
||||
* @return Флаги, определяющие свойства элемента.
|
||||
*/
|
||||
Qt::ItemFlags WriteRegisterModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
// Проверка валидности индекса
|
||||
if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
|
||||
return QAbstractTableModel::flags(index);
|
||||
// Получение базовых флагов
|
||||
Qt::ItemFlags flags = QAbstractTableModel::flags(index);
|
||||
// Отключение элементов вне диапазона адресов
|
||||
if ((index.row() < m_address) || (index.row() >= (m_address + m_number)))
|
||||
flags &= ~Qt::ItemIsEnabled;
|
||||
if (index.column() == CoilsColumn) // coils
|
||||
// Установка флага чекбокса для столбца Coils
|
||||
if (index.column() == CoilsColumn)
|
||||
return flags | Qt::ItemIsUserCheckable;
|
||||
if (index.column() == HoldingColumn) // holding registers
|
||||
// Установка флага редактируемости для столбца HoldingRegisters
|
||||
if (index.column() == HoldingColumn)
|
||||
return flags | Qt::ItemIsEditable;
|
||||
// Возврат текущих флагов по умолчанию
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Устанавливает начальный адрес для модели.
|
||||
*
|
||||
* @param address Начальный адрес.
|
||||
*/
|
||||
void WriteRegisterModel::setStartAddress(int address)
|
||||
{
|
||||
m_address = address;
|
||||
//emit updateViewport();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Устанавливает число значений (величин) на основе строки.
|
||||
*
|
||||
* @param number Строковое представление числа.
|
||||
*/
|
||||
void WriteRegisterModel::setNumberOfValues(const QString &number)
|
||||
{
|
||||
m_number = number.toInt();
|
||||
//emit updateViewport();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Получает состояние coil по индексу.
|
||||
*
|
||||
* @param index Индекс элемента.
|
||||
* @return true, если coil активен; иначе false.
|
||||
*/
|
||||
bool WriteRegisterModel::get_coil(const QModelIndex &index)
|
||||
{
|
||||
return m_coils.at(index.row());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Получает значение holding register по индексу.
|
||||
*
|
||||
* @param index Индекс элемента.
|
||||
* @return Значение регистров.
|
||||
*/
|
||||
uint WriteRegisterModel::get_holreg(const QModelIndex &index)
|
||||
{
|
||||
return m_holdingRegisters.at(index.row());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Устанавливает текущие значения U для указанных индексов.
|
||||
*
|
||||
* Изначально обновляет значение в массиве m_currentU по указанному индексу.
|
||||
* Если isHR установлено в true, также обновляет значение по индексу, смещённому на m_number.
|
||||
*
|
||||
* @param _tmpU Новое значение U.
|
||||
* @param index Индекс элемента.
|
||||
* @return Всегда возвращает true.
|
||||
*/
|
||||
bool WriteRegisterModel::set_currentU(unsigned _tmpU, unsigned index)
|
||||
{
|
||||
// Установка значения для текущего индекса
|
||||
m_currentU[index] = _tmpU;
|
||||
|
||||
// Если isHR активен, обновляем также и по смещенному индексу
|
||||
if (isHR)
|
||||
m_currentU[index + m_number] = _tmpU;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -14,25 +14,18 @@ public:
|
||||
WriteRegisterModel(QObject *parent = nullptr, int _tmpRC = 85, bool _isHR = false);
|
||||
bool get_coil(const QModelIndex &index);
|
||||
uint get_holreg(const QModelIndex &index);
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
|
||||
bool set_currentU(unsigned _tmpU, unsigned index);
|
||||
|
||||
public slots:
|
||||
void setStartAddress(int address);
|
||||
void setNumberOfValues(const QString &number);
|
||||
|
||||
signals:
|
||||
void updateViewport();
|
||||
|
||||
public:
|
||||
int m_number = 0;
|
||||
int m_address = 0;
|
||||
|
||||
Reference in New Issue
Block a user