4 Commits

Author SHA1 Message Date
Вячеслав Штейбезандт
ba0506caf7 Ещё немного комментариев 2025-12-05 15:22:19 +03:00
Вячеслав Штейбезандт
f10f3c8927 Оптимизация кода, начало работы с комментариями 2025-12-01 17:23:24 +03:00
Вячеслав Штейбезандт
3f1cd53e19 Добавлен модуль для работы с (0x2B / 0x0E) Read Device Identification 2025-10-21 17:23:23 +03:00
Вячеслав Штейбезандт
e402334153 Алексей: сделал определение плат по char, а не hex
т.е. не 0x1-0x4, а "1"-"4" (0x31-0x34)
2025-10-16 17:32:52 +03:00
29 changed files with 2131 additions and 999 deletions

View File

@@ -32,6 +32,9 @@ SOURCES += \
main.cpp \
m3kte.cpp \
multiplesettings.cpp \
parameterbox.cpp \
parameterdevice.cpp \
parameterworkspace.cpp \
scanboard.cpp \
settingsdialog.cpp \
writeregistermodel.cpp
@@ -43,6 +46,9 @@ HEADERS += \
lineringer.h \
m3kte.h \
multiplesettings.h \
parameterbox.h \
parameterdevice.h \
parameterworkspace.h \
scanboard.h \
settingsdialog.h \
writeregistermodel.h
@@ -54,6 +60,9 @@ FORMS += \
lineringer.ui \
m3kte.ui \
multiplesettings.ui \
parameterbox.ui \
parameterdevice.ui \
parameterworkspace.ui \
scanboard.ui \
settingsdialog.ui

View File

@@ -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,28 +99,25 @@ 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));
.arg(m_boardId + 1)
.arg(m_teNumber)
.arg(m_startAddress)
.arg(m_startAddress + m_registerCount - 1));
// Записываем новый номер ТЭ в устройство
if (m_modbusDevice) {
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(auto *reply = m_modbusDevice->sendWriteRequest(unit, m_boardAddress)) {
if(!reply->isFinished())
connect(reply, &QModbusReply::finished, this, [this, reply, teNumber]() {
if (reply->error() == QModbusDevice::NoError) {
if(reply->error() == QModbusDevice::NoError) {
qDebug() << "TE number changed to:" << teNumber;
// Можно обновить данные сразу после смены ТЭ
readAdcDataAndIndices();
@@ -143,9 +126,8 @@ void AdcGraphDialog::setTENumber(int boardID, int teNumber)
}
reply->deleteLater();
});
} else {
else
reply->deleteLater();
}
}
}
ui->teNumberSpinBox->setValue(teNumber);
@@ -153,130 +135,111 @@ void AdcGraphDialog::setTENumber(int boardID, int 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(auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
if(!reply->isFinished())
connect(reply, &QModbusReply::finished, this, [this, reply]() {
if (reply->error() == QModbusDevice::NoError) {
if(reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit result = reply->result();
if (result.valueCount() >= 2) {
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(auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
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->error() == QModbusDevice::NoError) {
if(!reply)
return;
if(reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit result = reply->result();
if (result.valueCount() >= 2) {
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();
}
void AdcGraphDialog::updateStatistics()
{
if (m_series->count() > 0) {
if(m_series->count() > 0) {
double min = 1000, max = -1000, sum = 0;
for (const QPointF &point : m_series->points()) {
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]")
.arg(m_series->count())
.arg(m_startAddress)
.arg(m_startAddress + m_registerCount - 1)
.arg(m_stableStartIndex) // Абсолютный начальный индекс
.arg(m_stableEndIndex) // Абсолютный конечный индекс
);
}
QString("%1 отсч. (адр %2-%3) [стаб: %4-%5]")
.arg(m_series->count())
.arg(m_startAddress)
.arg(m_startAddress + m_registerCount - 1)
.arg(m_stableStartIndex) // Абсолютный начальный индекс
.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) {
if(m_stableStartIndex >= m_startAddress && m_stableStartIndex < m_startAddress + m_registerCount) {
int relativeStartIndex = m_stableStartIndex - m_startAddress;
m_stableStartLine->append(relativeStartIndex, yMin);
m_stableStartLine->append(relativeStartIndex, yMax);
}
// Добавляем вертикальную линию для конца стабильного участка
if (m_stableEndIndex >= m_startAddress && m_stableEndIndex < m_startAddress + m_registerCount) {
if(m_stableEndIndex >= m_startAddress && m_stableEndIndex < m_startAddress + m_registerCount) {
int relativeEndIndex = m_stableEndIndex - m_startAddress;
m_stableEndLine->append(relativeEndIndex, yMin);
m_stableEndLine->append(relativeEndIndex, yMax);
@@ -288,111 +251,83 @@ 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(auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
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) {
if(reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit result = reply->result();
// Обрабатываем индексы стабильности (первые 2 регистра)
if (result.valueCount() >= 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()) {
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(auto *reply = m_modbusDevice->sendReadRequest(unit, m_boardAddress)) {
if(!reply->isFinished())
connect(reply, &QModbusReply::finished, this, &AdcGraphDialog::onReadReady);
} else {
else
reply->deleteLater();
}
} else {
// qDebug() << "Failed to send ADC buffer read request";
}
}
@@ -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));
.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->error() == QModbusDevice::NoError) {
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) {
if(m_series->count() > 0) {
double min = 1000, max = -1000, sum = 0;
for (const QPointF &point : m_series->points()) {
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,65 +429,38 @@ 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 = -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()

View File

@@ -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();

View File

@@ -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,19 +533,27 @@ 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)
{
// Удаляем старый диалог и создаем новый
if (m_adcGraphDialog) {
if(m_adcGraphDialog) {
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) {
m_adcGraphDialog->setTimeout(milliseconds);
}
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)
{
// Получаем групбокс для указанной платы
@@ -624,22 +628,20 @@ void DebugTerminalDialog::setBoardActive(int boardID, bool active)
case 3: boardGroup = ui->DbgPlate_4; break; // Плата 4
default: return;
}
if (boardGroup) {
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;
}
}

View File

@@ -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();

View File

@@ -10,17 +10,14 @@ 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;
}
_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()
@@ -74,7 +71,7 @@ unsigned short DeviceSettingsDialog::currentParity()
void DeviceSettingsDialog::updateSettingsAfterConnection(unsigned tmp_speed, unsigned tmp_parity, unsigned *tmp_adr, bool *ActiveDevices)
{
ui->speedBox->setCurrentText(QString::number(_currentSpeed=tmp_speed, 10));
if(tmp_parity>0)
if(tmp_parity > 0)
tmp_parity--;
ui->parityBox->setCurrentIndex(_currentParity = tmp_parity);
for(int i = 0; i < 4; i++) {
@@ -82,9 +79,8 @@ 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++) {
_currentAdrs[i] = i+1;
}
for(int i = 0; i < 4; i++)
_currentAdrs[i] = i + 1;
ui->adrSpinBox->setValue(_currentAdrs[ui->idComboBox->currentIndex()]);
break;
case QDialogButtonBox::AcceptRole:
@@ -122,7 +117,7 @@ void DeviceSettingsDialog::on_buttonBox_clicked(QAbstractButton *button)
void DeviceSettingsDialog::initPollForBoard(unsigned boardID, unsigned boardAdr)
{
ui->idPollComboBox->addItem(QString("Плата №%1 (ID %2)").arg(boardID+1).arg(boardAdr), QVariant(boardID));
ui->idPollComboBox->addItem(QString("Плата №%1 (ID %2)").arg(boardID + 1).arg(boardAdr), QVariant(boardID));
}
void DeviceSettingsDialog::updatePollStatus(unsigned boardID, bool status)

View File

@@ -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;
};

View File

@@ -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,27 +34,52 @@ LineRinger::LineRinger(QWidget *parent) :
ui->deviceOnlineView->horizontalHeader()->setVisible(true);
syncColumnHeaders();
ui->deviceOnlineView->setColumnHidden(1, true);
ui->ringButton->setEnabled(false);
modbusDevice = new QModbusRtuSerialMaster(this);
}
LineRinger::~LineRinger()
{
if (modbusDevice->state() == QModbusDevice::ConnectedState)
if(modbusDevice->state() == QModbusDevice::ConnectedState)
on_connectButton_clicked();
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);
@@ -99,13 +125,13 @@ LineRinger::callStatus LineRinger::lineCall()
*tmp_isRun = true;
});
connect(this, &LineRinger::stopLineCall, this, [tmp_isRun]() {
*tmp_isRun = true;
*tmp_isRun = true;
});
bar->setLabelText(tr("Поиск устройств... Текущий адрес: %1").arg(tmp_adr));
bar->setCancelButton(nullptr);
bar->setRange(1, 247);
bar->setMinimumDuration(100);
for(tmp_adr = 1; tmp_adr<248; tmp_adr++) {
for(tmp_adr = 1; tmp_adr < 248; tmp_adr++) {
bar->setValue(tmp_adr);
bar->setLabelText(tr("Поиск устройств... Текущий адрес: %1/247").arg(tmp_adr));
auto *reply = modbusDevice->sendRawRequest(readDeviceBasicIdentification, tmp_adr);
@@ -130,7 +156,7 @@ LineRinger::callStatus LineRinger::lineCall()
return callStatus::INTERRUPT;
} else if(!isRun) {
//Нужна проверка типа устройства
if(reply->error()!=QModbusDevice::TimeoutError) {
if(reply->error() != QModbusDevice::TimeoutError) {
deviceOnLine currentDevice;
currentDevice.adr = tmp_adr;
currentDevice.baudRate = currentBaudRate;
@@ -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++) {
currentDevice.fields[objectID] += QString(result.at(2+i));
}
result.remove(0, lengthOfObject+2);
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,13 +208,11 @@ 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++) {
currentDevice.fields[objectID] += QString(regularResult.at(2+i));
}
regularResult.remove(0, lengthOfObject+2);
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++) {
ui->deviceOnlineView->setItem(newRow, i+2, new QTableWidgetItem(currentDevice.fields[i]));
}
if(reply->error()!=QModbusDevice::NoError) {
for(int i = 0; i < 7; i++)
ui->deviceOnlineView->setItem(newRow, i + 2, new QTableWidgetItem(currentDevice.fields[i]));
if(reply->error()!=QModbusDevice::NoError)
ui->deviceOnlineView->setItem(newRow, 9, new QTableWidgetItem(QString("%1: %2").arg(reply->error()).arg(reply->errorString())));
} else if(regularReplyError) {
ui->deviceOnlineView->setItem(newRow, 9, new QTableWidgetItem(regularReplyErrorString));
}
else if(regularReplyError)
ui->deviceOnlineView->setItem(newRow, 9, new QTableWidgetItem(regularReplyErrorString));
ui->deviceOnlineView->resizeColumnsToContents();
syncColumnHeaders();
}
@@ -216,13 +237,23 @@ LineRinger::callStatus LineRinger::lineCall()
return callStatus::NOERROR;
}
/**
* @brief Обработчик нажатия кнопки "Опросить" для устройства LineRinger.
*
* Этот слот очищает текущий список устройств, обновляет интерфейс, а затем,
* в случае автоматического определения скорости передачи (isAutoBaud),
* последовательно пытается подключиться к устройствам с различными скоростями.
* В процессе поиска отображается прогресс-бар, который можно отменить.
* Если обнаружен вызываемый ответ или возникает ошибка, происходит завершение поиска.
* В случае ручного режима поиска, выполняется однократная попытка сканирования.
*/
void LineRinger::on_ringButton_clicked()
{
ui->deviceOnlineView->clear();
devicesList.clear();
syncColumnHeaders();
while(ui->deviceOnlineView->rowCount()!=0)
ui->deviceOnlineView->removeRow(ui->deviceOnlineView->rowCount()-1);
while(ui->deviceOnlineView->rowCount() != 0)
ui->deviceOnlineView->removeRow(ui->deviceOnlineView->rowCount() - 1);
if(isAutoBaud) {
auto bar = new QProgressDialog(this);
bar->setLabelText(tr("Поиск устройств... Текущая скорость: %1").arg(currentBaudRate));
@@ -231,16 +262,16 @@ void LineRinger::on_ringButton_clicked()
bar->setMinimumDuration(0);
bar->setCancelButton(nullptr);
connect(bar, &QProgressDialog::canceled, this, [this]() {
emit stopLineCall();
emit stopLineCall();
});
bar->setValue(0);
ui->deviceOnlineView->setColumnHidden(1, false);
modbusDevice->disconnectDevice();
for (int i = 0; i < ui->baudRateBox->count(); i++) {
bar->setValue(i+1);
for(int i = 0; i < ui->baudRateBox->count(); i++) {
bar->setValue(i + 1);
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
ui->baudRateBox->itemText(i).toInt(nullptr, 10));
if (!modbusDevice->connectDevice()) {
if(!modbusDevice->connectDevice()) {
QMessageBox::warning(this, "Ошибка", "Произошла ошибка при попытке подключения.");
on_connectButton_clicked();
break;
@@ -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());

View File

@@ -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;
};

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,7 @@
#include "multiplesettings.h"
#include "scanboard.h"
#include "lineringer.h"
#include "parameterworkspace.h"
#include <QModbusTcpClient>
#include <QModbusRtuSerialMaster>
@@ -58,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();
@@ -65,8 +67,6 @@ private:
void applySettingsFromScan(QModbusReply *reply);
void multipleRegWrite();
void multipleRegSend();
void writeSingleCoil(int boardId, int coilAddress, bool value);
void writeSingleRegister(int boardId, int regAddress, quint16 value);
bool autoBaudRateScan();
void selectPositionOnTree(unsigned index);
signals:
@@ -95,6 +95,8 @@ public:
~M3KTE();
QModbusClient* getModbusDevice() const { return modbusDevice; }
QModbusReply* readSingleCoil(int boardID, int coilAddress);
void writeSingleCoil(int boardId, int coilAddress, bool value);
void writeSingleRegister(int boardId, int regAddress, quint16 value);
private:
Ui::M3KTE *ui;
QTableWidget *loggerTable = nullptr;
@@ -107,6 +109,7 @@ private:
DebugTerminalDialog *m_debugTerminalDialog = nullptr;
SettingsDialog *m_settingsDialog = nullptr;
MultipleSettings *m_regMultipleSettings = nullptr;
ParameterWorkspace *m_parameterWorkspace = nullptr;
ScanBoard *m_scanBoard = nullptr;
LineRinger *m_lineRinger = nullptr;
QGroupBox *Boards_Fields[4];

View File

@@ -14643,6 +14643,7 @@
<string>Модули</string>
</property>
<addaction name="LineCall"/>
<addaction name="ParameterScan"/>
</widget>
<widget class="QMenu" name="DebugMenu">
<property name="title">
@@ -14691,11 +14692,37 @@
<property name="text">
<string>Открыть</string>
</property>
<property name="iconVisibleInMenu">
<bool>true</bool>
</property>
<property name="shortcutVisibleInContextMenu">
<bool>true</bool>
</property>
</action>
<action name="OpenADCBuff">
<property name="text">
<string>Буфер АЦП</string>
</property>
<property name="iconVisibleInMenu">
<bool>true</bool>
</property>
<property name="shortcutVisibleInContextMenu">
<bool>true</bool>
</property>
</action>
<action name="ParameterScan">
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Сканировать параметры</string>
</property>
</action>
</widget>
<resources/>

View File

@@ -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)) {
@@ -25,46 +34,77 @@ void MultipleSettings::on_buttonBox_clicked(QAbstractButton *button)
countReg = ui->countBox->value();
boardId = ui->boardBox->currentIndex();
emit write();
} else if (button == ui->buttonBox->button(QDialogButtonBox::SaveAll)) {
} else if(button == ui->buttonBox->button(QDialogButtonBox::SaveAll)) {
newValue = ui->regValueLine->text().toInt(nullptr, 16);
typeReg = ui->regTypeBox->currentIndex();
startAdr = ui->adrBox->value();
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;
switch (ui->boardBox->currentIndex()) {
switch(ui->boardBox->currentIndex()) {
case 3:
maxRange = 64;
break;
default:
maxRange = 84;
}
switch (index) {
switch(index) {
case 0:
case 1:
ui->adrBox->setRange(0, maxRange);
ui->adrBox->setValue(0);
break;
case 2:
ui->adrBox->setRange(85, 85+maxRange);
ui->adrBox->setRange(85, 85 + maxRange);
ui->adrBox->setValue(85);
break;
}
}
/**
* @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));
}

View File

@@ -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;

View File

@@ -0,0 +1,78 @@
#include "parameterbox.h"
#include "ui_parameterbox.h"
ParameterBox::ParameterBox(QWidget *parent, pboxMode Mode, quint16 objectID) :
QWidget(parent),
ui(new Ui::ParameterBox)
{
ui->setupUi(this);
boxMode = Mode;
ID = objectID;
switch(boxMode) {
case Info:
ui->adrLine->setHidden(true);
ui->valueBox->setHidden(true);
ui->sendButton->setHidden(true);
break;
case MTemplate:
break;
}
ui->objectIdLabel->setText("0x" + QString::number(ID, 16));
}
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) {
case Info:
ui->nameLine->setText(name);
break;
case MTemplate:
ui->nameLine->setText(name);
ui->adrLine->setText(adr);
ui->valueBox->clear();
ui->valueBox->addItems(values);
break;
}
}

39
M3KTE_TERM/parameterbox.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef PARAMETERBOX_H
#define PARAMETERBOX_H
#include <QWidget>
namespace Ui {
class ParameterBox;
}
class ParameterBox : public QWidget
{
Q_OBJECT
public:
enum pboxMode{
Info = 0,
MTemplate = 1
};
enum objectType{
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;}
objectType getType(){return type;}
signals:
void writeParameter(int adr, quint16 value);
private slots:
void on_sendButton_clicked();
private:
quint16 ID;
pboxMode boxMode;
objectType type = HR;
Ui::ParameterBox *ui;
};
#endif // PARAMETERBOX_H

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ParameterBox</class>
<widget class="QWidget" name="ParameterBox">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>43</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="QLineEdit" name="adrLine">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="sendButton">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="text">
<string>SEND</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="nameLine">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QComboBox" name="valueBox">
<property name="minimumSize">
<size>
<width>0</width>
<height>25</height>
</size>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="objectIdLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,236 @@
#include "parameterdevice.h"
#include "ui_parameterdevice.h"
void sortParameterBoxesByID(QVector<ParameterBox*>& parameterBoxes) {
std::sort(parameterBoxes.begin(), parameterBoxes.end(),
[](ParameterBox* a, ParameterBox* b) {
return a->getID() < b->getID();
});
}
ParameterDevice::ParameterDevice(QWidget *parent) :
QWidget(parent),
ui(new Ui::ParameterDevice)
{
ui->setupUi(this);
}
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)
box->deleteLater();
parameterBoxes.clear();
quint16 strAdr;
int cnt = 0;
// Установка стартового адреса
strAdr = ui->adrSpin->value();
// В зависимости от режима задаем диапазон чтения
switch (mode) {
case fullRequest:
strAdr = 0x80;
cnt = 128;
break;
case selectiveRequest:
cnt = ui->countSpin->value();
break;
}
// Настройка таймера для измерения времени
QElapsedTimer timer;
// Создаем диалог прогресса
QProgressDialog progressDialog("Обработка...", "Отмена", 0, cnt, this);
progressDialog.setWindowModality(Qt::WindowModal);
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; // пользователь отменил выполнение
// Формируем запрос
QByteArray data = QByteArray::fromHex("0E04");
data.append(strAdr + i);
QModbusRequest request(QModbusRequest::EncapsulatedInterfaceTransport, data);
// Отправка запроса
emit read(request, strAdr + i);
// Ожидание завершения передачи
loop.exec();
// Проверка на ошибку
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();
}
/**
* @brief Перестроение (сортировка) содержимого внутри scrollArea.
*
* Этот метод очищает текущий лейаут внутри UI элемента parameterBoxHubContents,
* затем добавляет все параметры из списка parameterBoxes обратно в лейаут.
* После этого обновляются и перерасчитываются размеры элементов интерфейса.
*/
void ParameterDevice::sortScrollArea()
{
// Получение текущего лейаута внутри 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;
}
// Добавление всех параметров из списка parameterBoxes обратно в лейаут
for (int i = 0; i < parameterBoxes.count(); i++) {
pblayout->addWidget(parameterBoxes.at(i));
}
// Обновление размеров и отображения интерфейса
ui->parameterBoxHubContents->update();
ui->parameterBoxHubContents->adjustSize();
ui->scrollArea->updateGeometry();
ui->scrollArea->viewport()->update();
}

View File

@@ -0,0 +1,54 @@
#ifndef ParameterDevice_H
#define ParameterDevice_H
#include <QWidget>
#include <QModbusRequest>
#include "parameterbox.h"
#include <algorithm>
#include <QLayout>
#include <QTimer>
#include <QMessageBox>
#include <QProgressDialog>
#include <QElapsedTimer>
void sortParameterBoxesByID(QVector<ParameterBox*>& parameterBoxes);
namespace Ui {
class ParameterDevice;
}
class ParameterDevice : public QWidget
{
Q_OBJECT
public:
enum requestMode{
fullRequest = 0,
selectiveRequest = 1
};
void setAnswer(QString reply, quint16 objectID);
void setError(QString error);
void setAdr(int _adr){adr = _adr;}
int getAdr(){return adr;}
bool errorAtTransmit = false;
explicit ParameterDevice(QWidget *parent = nullptr);
~ParameterDevice();
signals:
void read(QModbusRequest request, quint16 objectID);
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;
QVector<ParameterBox*>parameterBoxes;
requestMode mode = fullRequest;
Ui::ParameterDevice *ui;
};
#endif // ParameterDevice_H

View File

@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ParameterDevice</class>
<widget class="QWidget" name="ParameterDevice">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>687</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Extended Objects</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="1">
<widget class="QGroupBox" name="selectiveBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="title">
<string/>
</property>
<property name="checkable">
<bool>false</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="1">
<widget class="QSpinBox" name="adrSpin">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="minimum">
<number>128</number>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Start Adr:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Count:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="countSpin">
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="selectiveRadio">
<property name="text">
<string>Selective Request</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QPushButton" name="checkButton">
<property name="text">
<string>Check</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="fullRadio">
<property name="text">
<string>Full Request</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="parameterBoxHubContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>380</width>
<height>450</height>
</rect>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,193 @@
#include "parameterworkspace.h"
#include "ui_parameterworkspace.h"
ParameterWorkspace::ParameterWorkspace(QWidget *parent) :
QWidget(parent),
ui(new Ui::ParameterWorkspace)
{
ui->setupUi(this);
}
ParameterWorkspace::~ParameterWorkspace()
{
delete ui;
}
/**
* @brief Устанавливает количество устройств и обновляет интерфейс.
*
* Очистка текущего списка устройств и удаления соответствующих вкладок,
* затем создание новых устройств и добавление их в интерфейс.
*
* @param count Новое количество устройств.
*/
void ParameterWorkspace::setDeviceCount(int count)
{
// Удаление и очистка текущих устройств
for (const auto& device : deviceList) {
device.tab->deleteLater(); ///< Удаление вкладки устройства
device.device->deleteLater(); ///< Удаление объекта устройства
}
deviceList.clear(); ///< Очистка списка устройств
// Удаление всех вкладок из таб-виджета
for (int i = 0; i < ui->tabWidget->count(); ++i) {
ui->tabWidget->removeTab(i);
}
// Создание новых устройств
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) {
readDeviceIdentification(deviceList[i].device, request, deviceList[i].adr, objectID);
});
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){
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)
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, [reply]() {
// Проверка ошибок выполнения
if (reply->error() != QModbusDevice::NoError) {
// Обработка ошибок (можно добавить лог или сообщение)
}
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)
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, [reply]() {
// Проверка наличия ошибок
if (reply->error() != QModbusDevice::NoError) {
// Можно добавить лог или обработку ошибок
}
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)
return;
// Отправка сырого запроса к устройству
if (auto *reply = modbusDevice->sendRawRequest(request, adr)) {
// Обработка ответа, если он не завершен
if (!reply->isFinished()) {
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) {
// Удаление протокольной части данных
QByteArray data = resp.data();
data.remove(0, MODBUS_REQUEST_PROTOCOL_INFO_LENGTH);
QString result = QString::fromUtf8(data); // или другой метод преобразования, в зависимости от формата
device->setAnswer(result, objectID);
}
} else {
// Обработка ошибок
device->setError(reply->errorString());
qDebug() << "Получен ответ с ошибкой:" << reply->errorString();
}
reply->deleteLater(); // Очистка объекта
});
}
} 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;
deviceList[ID].adr = adr;
deviceList[ID].device->setAdr(adr);
}

View File

@@ -0,0 +1,50 @@
#ifndef PARAMETERWORKSPACE_H
#define PARAMETERWORKSPACE_H
#include <QWidget>
#include <QDebug>
#include <QModbusRequest>
#include <QModbusDataUnit>
#include <QModbusTcpClient>
#include <QModbusRtuSerialMaster>
#include <QtSerialBus/qtserialbusglobal.h>
#if QT_CONFIG(modbus_serialport)
#include <QSerialPort>
#endif
#include "parameterdevice.h"
#define MODBUS_REQUEST_PROTOCOL_INFO_LENGTH 8
namespace Ui {
class ParameterWorkspace;
}
class ParameterWorkspace : public QWidget
{
Q_OBJECT
public:
explicit ParameterWorkspace(QWidget *parent = nullptr);
~ParameterWorkspace();
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;
int adr = 1;
ParameterDevice *device = nullptr;
QWidget *tab = nullptr;
};
QVector<device> deviceList;
QModbusClient *modbusDevice = nullptr;
Ui::ParameterWorkspace *ui;
};
#endif // PARAMETERWORKSPACE_H

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ParameterWorkspace</class>
<widget class="QWidget" name="ParameterWorkspace">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>610</width>
<height>646</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="tabPosition">
<enum>QTabWidget::North</enum>
</property>
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -19,7 +19,7 @@ SettingsDialog::SettingsDialog(QWidget *parent) :
#if QT_CONFIG(modbus_serialport)
m_settings.portName = ui->comBox->currentData().toString();
m_settings.parity = ui->parityCombo->currentIndex();
if (m_settings.parity > 0)
if(m_settings.parity > 0)
m_settings.parity++;
m_settings.baud = ui->baudCombo->currentText().toInt();
m_settings.dataBits = ui->dataBitsCombo->currentText().toInt();
@@ -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) {
return (m_settings.parity = ++parity);
} else {
// Если parity > 0, увеличиваем его значение перед сохранением
if (parity > 0)
return (m_settings.parity = ++parity);
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()));
for (const auto& port : listPorts) {
// Добавляем элемент с именем порта и производителем
ui->comBox->addItem(QString("%1: %2").arg(port.portName(), port.manufacturer()), QVariant(port.portName()));
}
}

View File

@@ -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;

View File

@@ -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() == CurrentUColumn && role == Qt::DisplayRole)
return QString("%1 В").arg(QString::number((double)((double)m_currentU.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)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)
// Проверяем валидность индекса и границы
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);
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;
if(isHR)
// Если isHR активен, обновляем также и по смещенному индексу
if (isHR)
m_currentU[index + m_number] = _tmpU;
return true;
}

View File

@@ -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;