Ещё немного комментариев
This commit is contained in:
parent
f10f3c8927
commit
ba0506caf7
@ -43,6 +43,14 @@ void DebugTerminalDialog::setMainTerm(M3KTE* term)
|
|||||||
mainTerm = term;
|
mainTerm = term;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Устанавливает указатель на объект QModbusClient для отладочного терминала.
|
||||||
|
*
|
||||||
|
* Этот метод присваивает указатель на Modbus-устройство и, при наличии графического диалога
|
||||||
|
* для отображения данных АЦП, передает ему тот же объект для синхронизации.
|
||||||
|
*
|
||||||
|
* @param device Указатель на объект QModbusClient, представляющий Modbus-устройство.
|
||||||
|
*/
|
||||||
void DebugTerminalDialog::setModbusDevice(QModbusClient *device)
|
void DebugTerminalDialog::setModbusDevice(QModbusClient *device)
|
||||||
{
|
{
|
||||||
m_modbusDevice = device;
|
m_modbusDevice = device;
|
||||||
@ -50,6 +58,15 @@ void DebugTerminalDialog::setModbusDevice(QModbusClient *device)
|
|||||||
m_adcGraphDialog->setModbusDevice(device);
|
m_adcGraphDialog->setModbusDevice(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Устанавливает режим отладки, активируя или деактивируя коили.
|
||||||
|
*
|
||||||
|
* Этот метод вызывает функцию writeCoil для нескольких адресов (0, 1, 2, 3),
|
||||||
|
* устанавливая значение enable для каждого из них. Предполагается, что эти коили
|
||||||
|
* управляют режимом отладки устройства или системы.
|
||||||
|
*
|
||||||
|
* @param enable Целое значение (например, 0 или 1), указывающее, включать или выключать режим отладки.
|
||||||
|
*/
|
||||||
void DebugTerminalDialog::setDebugTerminalCoil(int enable)
|
void DebugTerminalDialog::setDebugTerminalCoil(int enable)
|
||||||
{
|
{
|
||||||
writeCoil(0, COIL_DEBUG_MODE, enable);
|
writeCoil(0, COIL_DEBUG_MODE, enable);
|
||||||
@ -58,6 +75,15 @@ void DebugTerminalDialog::setDebugTerminalCoil(int enable)
|
|||||||
writeCoil(3, COIL_DEBUG_MODE, enable);
|
writeCoil(3, COIL_DEBUG_MODE, enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обрабатывает событие отображения окна диалога.
|
||||||
|
*
|
||||||
|
* Этот метод вызывается при показе окна и сначала вызывает базовую реализацию showEvent.
|
||||||
|
* Затем происходит сброс всех настроек или состояний через resetAll(), а после этого
|
||||||
|
* устанавливается значение 1 для коили, отвечающих за режим отладки, с помощью setDebugTerminalCoil(1).
|
||||||
|
*
|
||||||
|
* @param event Указатель на объект QShowEvent, содержащий информацию о событии отображения.
|
||||||
|
*/
|
||||||
void DebugTerminalDialog::showEvent(QShowEvent *event)
|
void DebugTerminalDialog::showEvent(QShowEvent *event)
|
||||||
{
|
{
|
||||||
QDialog::showEvent(event);
|
QDialog::showEvent(event);
|
||||||
@ -66,6 +92,15 @@ void DebugTerminalDialog::showEvent(QShowEvent *event)
|
|||||||
setDebugTerminalCoil(1);
|
setDebugTerminalCoil(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обрабатывает событие закрытия диалогового окна.
|
||||||
|
*
|
||||||
|
* Перед закрытием окна вызывается метод setDebugTerminalCoil(0),
|
||||||
|
* который отключает режим отладки, устанавливая значение коил в 0.
|
||||||
|
* Затем вызывается базовая обработка closeEvent.
|
||||||
|
*
|
||||||
|
* @param event Указатель на объект QCloseEvent, содержащий информацию о событии закрытия.
|
||||||
|
*/
|
||||||
void DebugTerminalDialog::closeEvent(QCloseEvent *event)
|
void DebugTerminalDialog::closeEvent(QCloseEvent *event)
|
||||||
{
|
{
|
||||||
// При закрытии окна записываем в коил 555 значение "0"
|
// При закрытии окна записываем в коил 555 значение "0"
|
||||||
@ -84,6 +119,15 @@ void DebugTerminalDialog::updateConnectionStatus(int boardID, bool connected)
|
|||||||
// Реализация по необходимости
|
// Реализация по необходимости
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработка клика по кнопкам в диалоговом окне.
|
||||||
|
*
|
||||||
|
* В зависимости от роли нажатой кнопки, выполняются соответствующие действия:
|
||||||
|
* - Если роль Reset, вызывается resetAll(), сбрасывающий настройки.
|
||||||
|
* - Если роль Accept, вызывается accept(), подтверждающий изменения.
|
||||||
|
*
|
||||||
|
* @param button Указатель на нажатую кнопку.
|
||||||
|
*/
|
||||||
void DebugTerminalDialog::on_buttonBox_clicked(QAbstractButton *button)
|
void DebugTerminalDialog::on_buttonBox_clicked(QAbstractButton *button)
|
||||||
{
|
{
|
||||||
switch (ui->buttonBox->buttonRole(button)) {
|
switch (ui->buttonBox->buttonRole(button)) {
|
||||||
@ -489,6 +533,16 @@ void DebugTerminalDialog::on_ledVH3TestChkBox_4_stateChanged(int state)
|
|||||||
writeCoil(3, COIL_LED_VH3_TEST, state == Qt::Checked ? 1 : 0);
|
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)
|
void DebugTerminalDialog::openAdc(int boardID, int teNumber)
|
||||||
{
|
{
|
||||||
// Удаляем старый диалог и создаем новый
|
// Удаляем старый диалог и создаем новый
|
||||||
@ -507,12 +561,31 @@ void DebugTerminalDialog::openAdc(int boardID, int teNumber)
|
|||||||
m_adcGraphDialog->activateWindow();
|
m_adcGraphDialog->activateWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Устанавливает интервал обновления графика в миллисекундах.
|
||||||
|
*
|
||||||
|
* Этот метод задает период времени (в миллисекундах), с которым график в диалоге
|
||||||
|
* AdcGraphDialog обновляется. Если диалог открыт, вызывает его метод setTimeout.
|
||||||
|
*
|
||||||
|
* @param milliseconds Интервал обновления графика в миллисекундах.
|
||||||
|
*/
|
||||||
void DebugTerminalDialog::setGraphUpdateInterval(int milliseconds)
|
void DebugTerminalDialog::setGraphUpdateInterval(int milliseconds)
|
||||||
{
|
{
|
||||||
if(m_adcGraphDialog)
|
if(m_adcGraphDialog)
|
||||||
m_adcGraphDialog->setTimeout(milliseconds);
|
m_adcGraphDialog->setTimeout(milliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Записывает значение коила для указанной платы.
|
||||||
|
*
|
||||||
|
* Этот метод выбирает группу элементов в интерфейсе в зависимости от идентификатора платы (boardID),
|
||||||
|
* проверяет, что выбранная плата активна (enabled), и затем сообщает о смене значения коила через сигнал.
|
||||||
|
* Перед этим выводит диагностическое сообщение в лог.
|
||||||
|
*
|
||||||
|
* @param boardID Идентификатор платы (0-3).
|
||||||
|
* @param coil Номер коила, который необходимо изменить.
|
||||||
|
* @param value Новое значение для коила.
|
||||||
|
*/
|
||||||
void DebugTerminalDialog::writeCoil(int boardID, int coil, int value)
|
void DebugTerminalDialog::writeCoil(int boardID, int coil, int value)
|
||||||
{
|
{
|
||||||
QGroupBox* boardGroup = nullptr;
|
QGroupBox* boardGroup = nullptr;
|
||||||
@ -534,6 +607,16 @@ void DebugTerminalDialog::writeTENumber(int boardId, int teNumber)
|
|||||||
m_adcGraphDialog->setTENumber(boardId, 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)
|
void DebugTerminalDialog::setBoardActive(int boardID, bool active)
|
||||||
{
|
{
|
||||||
// Получаем групбокс для указанной платы
|
// Получаем групбокс для указанной платы
|
||||||
|
|||||||
@ -45,14 +45,41 @@ LineRinger::~LineRinger()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обновляет заголовки столбцов таблицы устройств.
|
||||||
|
*
|
||||||
|
* Этот метод устанавливает список заголовков для таблицы отображения устройств
|
||||||
|
* в интерфейсе и затем автоматически изменяет ширину колонок под содержимое.
|
||||||
|
*/
|
||||||
void LineRinger::syncColumnHeaders()
|
void LineRinger::syncColumnHeaders()
|
||||||
{
|
{
|
||||||
|
// Создаём список заголовков колонок
|
||||||
QStringList headers;
|
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->setHorizontalHeaderLabels(headers);
|
||||||
|
// Автоматически подгоняем ширину колонок под содержимое
|
||||||
ui->deviceOnlineView->resizeColumnsToContents();
|
ui->deviceOnlineView->resizeColumnsToContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработчик нажатия кнопки "Подключить/Отключить".
|
||||||
|
*
|
||||||
|
* Этот слот управляет подключением или отключением модбус-устройства.
|
||||||
|
* При отсутствии подключения он выполняет настройку параметров соединения,
|
||||||
|
* инициирует подключение и обновляет интерфейс в зависимости от результата.
|
||||||
|
* Если устройство уже подключено, происходит отключение и обновление интерфейса.
|
||||||
|
*/
|
||||||
void LineRinger::on_connectButton_clicked()
|
void LineRinger::on_connectButton_clicked()
|
||||||
{
|
{
|
||||||
if(!modbusDevice)
|
if(!modbusDevice)
|
||||||
@ -210,6 +237,16 @@ LineRinger::callStatus LineRinger::lineCall()
|
|||||||
return callStatus::NOERROR;
|
return callStatus::NOERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработчик нажатия кнопки "Опросить" для устройства LineRinger.
|
||||||
|
*
|
||||||
|
* Этот слот очищает текущий список устройств, обновляет интерфейс, а затем,
|
||||||
|
* в случае автоматического определения скорости передачи (isAutoBaud),
|
||||||
|
* последовательно пытается подключиться к устройствам с различными скоростями.
|
||||||
|
* В процессе поиска отображается прогресс-бар, который можно отменить.
|
||||||
|
* Если обнаружен вызываемый ответ или возникает ошибка, происходит завершение поиска.
|
||||||
|
* В случае ручного режима поиска, выполняется однократная попытка сканирования.
|
||||||
|
*/
|
||||||
void LineRinger::on_ringButton_clicked()
|
void LineRinger::on_ringButton_clicked()
|
||||||
{
|
{
|
||||||
ui->deviceOnlineView->clear();
|
ui->deviceOnlineView->clear();
|
||||||
|
|||||||
@ -979,10 +979,8 @@ bool M3KTE::event(QEvent *event)
|
|||||||
// Создаем запрос на запись нового ID в регистр
|
// Создаем запрос на запись нового ID в регистр
|
||||||
auto *_unit = new QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 172, 1);
|
auto *_unit = new QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 172, 1);
|
||||||
_unit->setValue(0, _event->BoardNewID());
|
_unit->setValue(0, _event->BoardNewID());
|
||||||
|
|
||||||
// Обновляем локально временный адрес платы
|
// Обновляем локально временный адрес платы
|
||||||
Boards[_event->BoardNum()]._tmp_adr = _event->BoardNewID();
|
Boards[_event->BoardNum()]._tmp_adr = _event->BoardNewID();
|
||||||
|
|
||||||
// Отправка запроса на изменение адреса через Modbus
|
// Отправка запроса на изменение адреса через Modbus
|
||||||
if(auto *reply = modbusDevice->sendWriteRequest(*_unit, Boards[_event->BoardNum()].adr)) {
|
if(auto *reply = modbusDevice->sendWriteRequest(*_unit, Boards[_event->BoardNum()].adr)) {
|
||||||
if(!reply->isFinished()) {
|
if(!reply->isFinished()) {
|
||||||
@ -1286,7 +1284,6 @@ void M3KTE::onParityUpdate()
|
|||||||
{
|
{
|
||||||
// Останавливаем сканирование плат
|
// Останавливаем сканирование плат
|
||||||
stopScanBoard();
|
stopScanBoard();
|
||||||
|
|
||||||
// Настройка регистра для установки режима четности
|
// Настройка регистра для установки режима четности
|
||||||
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, 174, 1);
|
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, 174, 1);
|
||||||
switch(m_deviceSettingsDialog->currentParity()) {
|
switch(m_deviceSettingsDialog->currentParity()) {
|
||||||
@ -1294,16 +1291,13 @@ void M3KTE::onParityUpdate()
|
|||||||
case 1: unit.setValue(0, 0x0400); break; // Четный
|
case 1: unit.setValue(0, 0x0400); break; // Четный
|
||||||
case 2: unit.setValue(0, 0x0600); break; // Нечетный
|
case 2: unit.setValue(0, 0x0600); break; // Нечетный
|
||||||
}
|
}
|
||||||
|
|
||||||
// Переменные для подсчёта прогресса
|
// Переменные для подсчёта прогресса
|
||||||
auto totalActiveBoards = QSharedPointer<int>::create(0);
|
auto totalActiveBoards = QSharedPointer<int>::create(0);
|
||||||
auto confirmedBoards = QSharedPointer<int>::create(0);
|
auto confirmedBoards = QSharedPointer<int>::create(0);
|
||||||
auto pendingBoards = QSharedPointer<QSet<int>>::create();
|
auto pendingBoards = QSharedPointer<QSet<int>>::create();
|
||||||
|
|
||||||
// Сохраняем текущий и новый параметры четности для отката при ошибках
|
// Сохраняем текущий и новый параметры четности для отката при ошибках
|
||||||
auto oldParity = m_settingsDialog->curParity();
|
auto oldParity = m_settingsDialog->curParity();
|
||||||
auto newParity = m_deviceSettingsDialog->currentParity();
|
auto newParity = m_deviceSettingsDialog->currentParity();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Лямбда-функция, вызываемая по завершении всех запросов.
|
* Лямбда-функция, вызываемая по завершении всех запросов.
|
||||||
* Обрабатывает успешное изменение или ошибку.
|
* Обрабатывает успешное изменение или ошибку.
|
||||||
@ -1318,7 +1312,6 @@ void M3KTE::onParityUpdate()
|
|||||||
modbusDevice->disconnectDevice();
|
modbusDevice->disconnectDevice();
|
||||||
modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, newParity);
|
modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, newParity);
|
||||||
modbusDevice->connectDevice();
|
modbusDevice->connectDevice();
|
||||||
|
|
||||||
// Обработка ошибок и успеха при смене паритета
|
// Обработка ошибок и успеха при смене паритета
|
||||||
auto errorHandler = [this, oldParity]() {
|
auto errorHandler = [this, oldParity]() {
|
||||||
disconnect(this, &M3KTE::errorAtCheckBoards, this, nullptr);
|
disconnect(this, &M3KTE::errorAtCheckBoards, this, nullptr);
|
||||||
@ -1328,34 +1321,28 @@ void M3KTE::onParityUpdate()
|
|||||||
modbusDevice->connectDevice();
|
modbusDevice->connectDevice();
|
||||||
beginScanBoards();
|
beginScanBoards();
|
||||||
};
|
};
|
||||||
|
|
||||||
auto successHandler = [this, newParity]() {
|
auto successHandler = [this, newParity]() {
|
||||||
disconnect(this, &M3KTE::successAtCheckBoards, this, nullptr);
|
disconnect(this, &M3KTE::successAtCheckBoards, this, nullptr);
|
||||||
m_settingsDialog->UpdateParity(newParity);
|
m_settingsDialog->UpdateParity(newParity);
|
||||||
modbusDevice->setTimeout(m_settingsDialog->settings().responseTime);
|
modbusDevice->setTimeout(m_settingsDialog->settings().responseTime);
|
||||||
beginScanBoards();
|
beginScanBoards();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Подписка на события успеха и ошибки
|
// Подписка на события успеха и ошибки
|
||||||
connect(this, &M3KTE::errorAtCheckBoards, this, errorHandler);
|
connect(this, &M3KTE::errorAtCheckBoards, this, errorHandler);
|
||||||
connect(this, &M3KTE::successAtCheckBoards, this, successHandler);
|
connect(this, &M3KTE::successAtCheckBoards, this, successHandler);
|
||||||
|
|
||||||
// Запускаем повторную проверку плат
|
// Запускаем повторную проверку плат
|
||||||
checkBoards();
|
checkBoards();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Отправляем запросы на каждую активную плату
|
// Отправляем запросы на каждую активную плату
|
||||||
for(int i = 0; i < 4; i++) {
|
for(int i = 0; i < 4; i++) {
|
||||||
if(!Boards[i].isActive)
|
if(!Boards[i].isActive)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int slaveAddress = Boards[i].adr;
|
int slaveAddress = Boards[i].adr;
|
||||||
auto *reply = modbusDevice->sendWriteRequest(unit, slaveAddress);
|
auto *reply = modbusDevice->sendWriteRequest(unit, slaveAddress);
|
||||||
if(reply) {
|
if(reply) {
|
||||||
(*totalActiveBoards)++;
|
(*totalActiveBoards)++;
|
||||||
pendingBoards->insert(slaveAddress);
|
pendingBoards->insert(slaveAddress);
|
||||||
|
|
||||||
// Обработка завершения ответа
|
// Обработка завершения ответа
|
||||||
connect(reply, &QModbusReply::finished, this,
|
connect(reply, &QModbusReply::finished, this,
|
||||||
[this, i, reply, slaveAddress, totalActiveBoards, confirmedBoards, pendingBoards, processResult]() {
|
[this, i, reply, slaveAddress, totalActiveBoards, confirmedBoards, pendingBoards, processResult]() {
|
||||||
@ -1369,11 +1356,9 @@ void M3KTE::onParityUpdate()
|
|||||||
logError(tr("Плата %1 (ID %2)").arg(i + 1).arg(slaveAddress),
|
logError(tr("Плата %1 (ID %2)").arg(i + 1).arg(slaveAddress),
|
||||||
reply->errorString(), ++Boards[i].error_baud_change,
|
reply->errorString(), ++Boards[i].error_baud_change,
|
||||||
"Ошибка при изменении чётности.");
|
"Ошибка при изменении чётности.");
|
||||||
|
|
||||||
// Удаляем складной ответ
|
// Удаляем складной ответ
|
||||||
pendingBoards->remove(slaveAddress);
|
pendingBoards->remove(slaveAddress);
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|
||||||
// Когда все ответы получены, обрабатываем результат
|
// Когда все ответы получены, обрабатываем результат
|
||||||
if(pendingBoards->isEmpty())
|
if(pendingBoards->isEmpty())
|
||||||
processResult();
|
processResult();
|
||||||
|
|||||||
@ -16,6 +16,15 @@ MultipleSettings::~MultipleSettings()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработка нажатий кнопок в диалоговом окне настроек.
|
||||||
|
*
|
||||||
|
* В зависимости от нажатой кнопки выполняются разные действия:
|
||||||
|
* - Если нажата кнопка "Записать", сохраняются введенные параметры и вызывается сигнал write().
|
||||||
|
* - Если нажата кнопка "Записать и установить", сохраняются параметры и вызывается сигнал writeAndSend().
|
||||||
|
*
|
||||||
|
* @param button Указатель на нажатую кнопку.
|
||||||
|
*/
|
||||||
void MultipleSettings::on_buttonBox_clicked(QAbstractButton *button)
|
void MultipleSettings::on_buttonBox_clicked(QAbstractButton *button)
|
||||||
{
|
{
|
||||||
if(button == ui->buttonBox->button(QDialogButtonBox::Ok)) {
|
if(button == ui->buttonBox->button(QDialogButtonBox::Ok)) {
|
||||||
@ -35,6 +44,15 @@ void MultipleSettings::on_buttonBox_clicked(QAbstractButton *button)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработка изменения выбранного типа регистра.
|
||||||
|
*
|
||||||
|
* В зависимости от выбранного типа регистра (index) и текущей выбранной платы (ui->boardBox),
|
||||||
|
* устанавливается диапазон допустимых значений для поля адреса (ui->adrBox).
|
||||||
|
* Также значение адреса сбрасывается на начальное.
|
||||||
|
*
|
||||||
|
* @param index Индекс выбранного типа регистра.
|
||||||
|
*/
|
||||||
void MultipleSettings::on_regTypeBox_currentIndexChanged(int index)
|
void MultipleSettings::on_regTypeBox_currentIndexChanged(int index)
|
||||||
{
|
{
|
||||||
short maxRange = 0;
|
short maxRange = 0;
|
||||||
@ -58,12 +76,33 @@ void MultipleSettings::on_regTypeBox_currentIndexChanged(int index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработка изменения выбранной платы в интерфейсе.
|
||||||
|
*
|
||||||
|
* Этот слот сохраняет выбранный индекс платы в переменную selectedBoard
|
||||||
|
* и обновляет диапазон адресов в зависимости от текущего типа регистра,
|
||||||
|
* вызывая обработчик on_regTypeBox_currentIndexChanged.
|
||||||
|
*
|
||||||
|
* @param index Индекс выбранной платы.
|
||||||
|
*/
|
||||||
void MultipleSettings::on_boardBox_currentIndexChanged(int index)
|
void MultipleSettings::on_boardBox_currentIndexChanged(int index)
|
||||||
{
|
{
|
||||||
selectedBoard = index;
|
selectedBoard = index;
|
||||||
on_regTypeBox_currentIndexChanged(ui->regTypeBox->currentIndex());
|
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)
|
void MultipleSettings::on_adrBox_valueChanged(int arg1)
|
||||||
{
|
{
|
||||||
ui->countBox->setRange(1, ((85 - (20 * (ui->boardBox->currentIndex() / 3)))
|
ui->countBox->setRange(1, ((85 - (20 * (ui->boardBox->currentIndex() / 3)))
|
||||||
|
|||||||
@ -25,6 +25,13 @@ ParameterBox::~ParameterBox()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработчик нажатия кнопки отправки (sendButton).
|
||||||
|
*
|
||||||
|
* При нажатии проверяется, заполнено ли выбранное значение в valueBox.
|
||||||
|
* Если значение пустое, выполнение прерывается.
|
||||||
|
* В противном случае генерируется сигнал writeParameter с текущим адресом и выбранным значением (в шестнадцатеричном формате).
|
||||||
|
*/
|
||||||
void ParameterBox::on_sendButton_clicked()
|
void ParameterBox::on_sendButton_clicked()
|
||||||
{
|
{
|
||||||
if(ui->valueBox->currentText().isEmpty())
|
if(ui->valueBox->currentText().isEmpty())
|
||||||
@ -32,11 +39,29 @@ void ParameterBox::on_sendButton_clicked()
|
|||||||
emit writeParameter(ui->adrLine->text().toInt(), ui->valueBox->currentText().toInt(nullptr, 16));
|
emit writeParameter(ui->adrLine->text().toInt(), ui->valueBox->currentText().toInt(nullptr, 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Установка текста параметра.
|
||||||
|
*
|
||||||
|
* Этот метод устанавливает переданный текст (data) в поле для имени параметра (nameLine).
|
||||||
|
*
|
||||||
|
* @param data Текст для отображения в поле имени.
|
||||||
|
*/
|
||||||
void ParameterBox::setData(QString data)
|
void ParameterBox::setData(QString data)
|
||||||
{
|
{
|
||||||
ui->nameLine->setText(data);
|
ui->nameLine->setText(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Установка данных в параметры в зависимости от режима работы.
|
||||||
|
*
|
||||||
|
* В зависимости от текущего режима (boxMode) заполняются соответствующие элементы интерфейса:
|
||||||
|
* - При режиме Info заполняется только поле имени.
|
||||||
|
* - При режиме MTemplate заполняются поля имени, адреса и список значений.
|
||||||
|
*
|
||||||
|
* @param name Имя параметра.
|
||||||
|
* @param adr Адрес параметра.
|
||||||
|
* @param values Список значений для выбора.
|
||||||
|
*/
|
||||||
void ParameterBox::setData(QString name, QString adr, QStringList values)
|
void ParameterBox::setData(QString name, QString adr, QStringList values)
|
||||||
{
|
{
|
||||||
switch(boxMode) {
|
switch(boxMode) {
|
||||||
|
|||||||
@ -20,28 +20,56 @@ ParameterDevice::~ParameterDevice()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработка выбора режима "Выборочный" (selectiveRadio).
|
||||||
|
*
|
||||||
|
* Включает доступность элементов управления для выбора выборочного режима
|
||||||
|
* и устанавливает режим работы на `selectiveRequest`.
|
||||||
|
*/
|
||||||
void ParameterDevice::on_selectiveRadio_clicked()
|
void ParameterDevice::on_selectiveRadio_clicked()
|
||||||
{
|
{
|
||||||
|
// Включение элементов для выбора выборочного режима
|
||||||
ui->selectiveBox->setEnabled(true);
|
ui->selectiveBox->setEnabled(true);
|
||||||
|
// Установка текущего режима
|
||||||
mode = selectiveRequest;
|
mode = selectiveRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработка выбора режима "Полный" (fullRadio).
|
||||||
|
*
|
||||||
|
* Выключает элементы управления для выборочного режима,
|
||||||
|
* а также устанавливает режим работы на `fullRequest`.
|
||||||
|
*/
|
||||||
void ParameterDevice::on_fullRadio_clicked()
|
void ParameterDevice::on_fullRadio_clicked()
|
||||||
{
|
{
|
||||||
|
// Отключение элементов для выборочного режима
|
||||||
ui->selectiveBox->setEnabled(false);
|
ui->selectiveBox->setEnabled(false);
|
||||||
|
// Установка текущего режима
|
||||||
mode = fullRequest;
|
mode = fullRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработка нажатия на кнопку checkButton.
|
||||||
|
*
|
||||||
|
* Выполняет чтение данных по определенной логике (полный или выборочный запрос),
|
||||||
|
* отображает прогресс-бар, измеряет общее время выполнения и обновляет UI.
|
||||||
|
*
|
||||||
|
* @note В процессе выполнения очищает текущие параметры, инициирует последовательное чтение данных,
|
||||||
|
* отображает прогресс, а по завершении показывает затраченное время.
|
||||||
|
*/
|
||||||
void ParameterDevice::on_checkButton_clicked()
|
void ParameterDevice::on_checkButton_clicked()
|
||||||
{
|
{
|
||||||
for(ParameterBox* box : parameterBoxes)
|
// Очистка старых параметров
|
||||||
if(box)
|
for (ParameterBox* box : parameterBoxes)
|
||||||
|
if (box)
|
||||||
box->deleteLater();
|
box->deleteLater();
|
||||||
parameterBoxes.clear();
|
parameterBoxes.clear();
|
||||||
quint16 strAdr;
|
quint16 strAdr;
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
// Установка стартового адреса
|
||||||
strAdr = ui->adrSpin->value();
|
strAdr = ui->adrSpin->value();
|
||||||
switch(mode) {
|
// В зависимости от режима задаем диапазон чтения
|
||||||
|
switch (mode) {
|
||||||
case fullRequest:
|
case fullRequest:
|
||||||
strAdr = 0x80;
|
strAdr = 0x80;
|
||||||
cnt = 128;
|
cnt = 128;
|
||||||
@ -50,51 +78,72 @@ void ParameterDevice::on_checkButton_clicked()
|
|||||||
cnt = ui->countSpin->value();
|
cnt = ui->countSpin->value();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Настройка таймера для измерения времени
|
||||||
QElapsedTimer timer;
|
QElapsedTimer timer;
|
||||||
|
// Создаем диалог прогресса
|
||||||
QProgressDialog progressDialog("Обработка...", "Отмена", 0, cnt, this);
|
QProgressDialog progressDialog("Обработка...", "Отмена", 0, cnt, this);
|
||||||
progressDialog.setWindowModality(Qt::WindowModal);
|
progressDialog.setWindowModality(Qt::WindowModal);
|
||||||
progressDialog.setMinimumDuration(0); // показывать сразу
|
progressDialog.setMinimumDuration(0); // показывать сразу же
|
||||||
|
// Создаем цикл событий для ожидания завершения
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
|
// Соединяем сигнал завершения передачи с выходом из цикла
|
||||||
connect(this, &ParameterDevice::transmitEnd, &loop, &QEventLoop::quit);
|
connect(this, &ParameterDevice::transmitEnd, &loop, &QEventLoop::quit);
|
||||||
|
// Запускаем таймер
|
||||||
timer.start();
|
timer.start();
|
||||||
for(int i = 0; i < cnt; i ++) {
|
// Цикл чтения данных
|
||||||
// Обновляем прогресс
|
for (int i = 0; i < cnt; i++) {
|
||||||
|
// Обновление прогресс-бара
|
||||||
progressDialog.setValue(i);
|
progressDialog.setValue(i);
|
||||||
if(progressDialog.wasCanceled())
|
if (progressDialog.wasCanceled())
|
||||||
break; // пользователь отменил
|
break; // пользователь отменил выполнение
|
||||||
|
// Формируем запрос
|
||||||
QByteArray data = QByteArray::fromHex("0E04");
|
QByteArray data = QByteArray::fromHex("0E04");
|
||||||
data.append(strAdr+i);
|
data.append(strAdr + i);
|
||||||
QModbusRequest request(QModbusRequest::EncapsulatedInterfaceTransport, data);
|
QModbusRequest request(QModbusRequest::EncapsulatedInterfaceTransport, data);
|
||||||
emit read(request, strAdr+i);
|
// Отправка запроса
|
||||||
|
emit read(request, strAdr + i);
|
||||||
|
// Ожидание завершения передачи
|
||||||
loop.exec();
|
loop.exec();
|
||||||
if(errorAtTransmit) {
|
// Проверка на ошибку
|
||||||
|
if (errorAtTransmit) {
|
||||||
errorAtTransmit = false;
|
errorAtTransmit = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Расчет затраченного времени
|
||||||
qint64 elapsedNanoSeconds = timer.nsecsElapsed();
|
qint64 elapsedNanoSeconds = timer.nsecsElapsed();
|
||||||
// Вычисляем компоненты времени
|
// Переводим в минуты, секунды, миллисекунды, микросекунды
|
||||||
qint64 totalMicroseconds = elapsedNanoSeconds / 1000; // Микросекунды
|
qint64 totalMicroseconds = elapsedNanoSeconds / 1000;
|
||||||
qint64 totalMilliseconds = totalMicroseconds / 1000; // Миллисекунды
|
qint64 totalMilliseconds = totalMicroseconds / 1000;
|
||||||
qint64 totalSeconds = totalMilliseconds / 1000; // Секунды
|
qint64 totalSeconds = totalMilliseconds / 1000;
|
||||||
qint64 totalMinutes = totalSeconds / 60; // Минуты
|
qint64 totalMinutes = totalSeconds / 60;
|
||||||
// Остатки после деления
|
|
||||||
qint64 remainingMicroseconds = totalMicroseconds % 1000;
|
qint64 remainingMicroseconds = totalMicroseconds % 1000;
|
||||||
qint64 remainingMilliseconds = totalMilliseconds % 1000;
|
qint64 remainingMilliseconds = totalMilliseconds % 1000;
|
||||||
qint64 remainingSeconds = totalSeconds % 60;
|
qint64 remainingSeconds = totalSeconds % 60;
|
||||||
// Форматируем строку для отображения
|
// Формируем строку для отображения времени
|
||||||
QString timeString = QString("%1 мин %2 сек %3 мс %4 мкс")
|
QString timeString = QString("%1 мин %2 сек %3 мс %4 мкс")
|
||||||
.arg(totalMinutes)
|
.arg(totalMinutes)
|
||||||
.arg(remainingSeconds)
|
.arg(remainingSeconds)
|
||||||
.arg(remainingMilliseconds)
|
.arg(remainingMilliseconds)
|
||||||
.arg(remainingMicroseconds);
|
.arg(remainingMicroseconds);
|
||||||
// Выводим
|
// Выводим сообщение с временем выполнения
|
||||||
QMessageBox::information(this, "Общее время выполнения", timeString);
|
QMessageBox::information(this, "Общее время выполнения", timeString);
|
||||||
|
// Устанавливаем прогресс в конец
|
||||||
progressDialog.setValue(cnt);
|
progressDialog.setValue(cnt);
|
||||||
|
// Сортируем параметры по ID и обновляем интерфейс
|
||||||
sortParameterBoxesByID(parameterBoxes);
|
sortParameterBoxesByID(parameterBoxes);
|
||||||
sortScrollArea();
|
sortScrollArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработка ошибки при передаче данных.
|
||||||
|
*
|
||||||
|
* Показывает информационное сообщение с текстом ошибки,
|
||||||
|
* устанавливает флаг errorAtTransmit в true,
|
||||||
|
* а затем сигнализирует о завершении передачи.
|
||||||
|
*
|
||||||
|
* @param error Текст ошибки, который будет отображен пользователю.
|
||||||
|
*/
|
||||||
void ParameterDevice::setError(QString error)
|
void ParameterDevice::setError(QString error)
|
||||||
{
|
{
|
||||||
QMessageBox::information(nullptr, "Получен ответ", error);
|
QMessageBox::information(nullptr, "Получен ответ", error);
|
||||||
@ -102,43 +151,84 @@ void ParameterDevice::setError(QString error)
|
|||||||
emit transmitEnd();
|
emit transmitEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработчик изменения значения спинбокса адреса (adrSpin).
|
||||||
|
*
|
||||||
|
* При изменении значения аргумента (arg1) обновляет диапазон допустимых
|
||||||
|
* значений для другого спинбокса (countSpin), чтобы сумма не превышала 256.
|
||||||
|
*
|
||||||
|
* Устанавливает диапазон: от 1 до (256 - текущего значения adrSpin).
|
||||||
|
*
|
||||||
|
* @param arg1 Новое значение адреса.
|
||||||
|
*/
|
||||||
void ParameterDevice::on_adrSpin_valueChanged(int arg1)
|
void ParameterDevice::on_adrSpin_valueChanged(int arg1)
|
||||||
{
|
{
|
||||||
ui->countSpin->setRange(1, 256 - arg1);
|
ui->countSpin->setRange(1, 256 - arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Установка ответа и создание нового параметра.
|
||||||
|
*
|
||||||
|
* Этот метод создает новый объект ParameterBox, устанавливает его данные,
|
||||||
|
* и в зависимости от типа параметра (Coil или HR) устанавливает соответствующие соединения.
|
||||||
|
* После этого добавляет его в список и сигнализирует о завершении передачи.
|
||||||
|
*
|
||||||
|
* @param reply Текст ответа, который отображается в параметре.
|
||||||
|
* @param objectID Идентификатор объекта, связанного с этим параметром.
|
||||||
|
*/
|
||||||
void ParameterDevice::setAnswer(QString reply, quint16 objectID)
|
void ParameterDevice::setAnswer(QString reply, quint16 objectID)
|
||||||
{
|
{
|
||||||
|
// Создаем новый объект ParameterBox в режиме Info
|
||||||
ParameterBox *newbox = new ParameterBox(nullptr, ParameterBox::Info, objectID);
|
ParameterBox *newbox = new ParameterBox(nullptr, ParameterBox::Info, objectID);
|
||||||
|
// Устанавливаем данные ответа в созданный параметр
|
||||||
newbox->setData(reply);
|
newbox->setData(reply);
|
||||||
|
// В зависимости от типа параметра устанавливаем соединения
|
||||||
switch(newbox->getType()) {
|
switch(newbox->getType()) {
|
||||||
case ParameterBox::Coil:
|
case ParameterBox::Coil:
|
||||||
|
// Для типа Coil связываем сигнал writeParameter с выводом writeSingleCoil
|
||||||
connect(newbox, &ParameterBox::writeParameter, this, [this](int adr, quint16 value){
|
connect(newbox, &ParameterBox::writeParameter, this, [this](int adr, quint16 value){
|
||||||
emit writeSingleCoil(adr, (bool)value);
|
emit writeSingleCoil(adr, (bool)value);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case ParameterBox::HR:
|
case ParameterBox::HR:
|
||||||
|
// Для типа HR связываем сигнал writeParameter с выводом writeSingleRegister
|
||||||
connect(newbox, &ParameterBox::writeParameter, this, [this](int adr, quint16 value){
|
connect(newbox, &ParameterBox::writeParameter, this, [this](int adr, quint16 value){
|
||||||
emit writeSingleRegister(adr, value);
|
emit writeSingleRegister(adr, value);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Добавляем созданный параметр в список
|
||||||
parameterBoxes.append(newbox);
|
parameterBoxes.append(newbox);
|
||||||
|
// Посылаем сигнал о завершении передачи
|
||||||
emit transmitEnd();
|
emit transmitEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Перестроение (сортировка) содержимого внутри scrollArea.
|
||||||
|
*
|
||||||
|
* Этот метод очищает текущий лейаут внутри UI элемента parameterBoxHubContents,
|
||||||
|
* затем добавляет все параметры из списка parameterBoxes обратно в лейаут.
|
||||||
|
* После этого обновляются и перерасчитываются размеры элементов интерфейса.
|
||||||
|
*/
|
||||||
void ParameterDevice::sortScrollArea()
|
void ParameterDevice::sortScrollArea()
|
||||||
{
|
{
|
||||||
|
// Получение текущего лейаута внутри parameterBoxHubContents
|
||||||
QLayout* pblayout = ui->parameterBoxHubContents->layout();
|
QLayout* pblayout = ui->parameterBoxHubContents->layout();
|
||||||
if(!pblayout) {
|
// Если лейаут отсутствует, создаем новый и устанавливаем его
|
||||||
// Создайте новый лейаут, если его нет
|
if (!pblayout) {
|
||||||
pblayout = new QVBoxLayout(ui->parameterBoxHubContents);
|
pblayout = new QVBoxLayout(ui->parameterBoxHubContents);
|
||||||
ui->parameterBoxHubContents->setLayout(pblayout);
|
ui->parameterBoxHubContents->setLayout(pblayout);
|
||||||
}
|
}
|
||||||
|
// Очистка текущих элементов из лейаута
|
||||||
QLayoutItem *item;
|
QLayoutItem *item;
|
||||||
while((item = pblayout->takeAt(0)) != nullptr){}
|
while ((item = pblayout->takeAt(0)) != nullptr) {
|
||||||
for(int i = 0; i < parameterBoxes.count(); i++)
|
delete item;
|
||||||
|
}
|
||||||
|
// Добавление всех параметров из списка parameterBoxes обратно в лейаут
|
||||||
|
for (int i = 0; i < parameterBoxes.count(); i++) {
|
||||||
pblayout->addWidget(parameterBoxes.at(i));
|
pblayout->addWidget(parameterBoxes.at(i));
|
||||||
|
}
|
||||||
|
// Обновление размеров и отображения интерфейса
|
||||||
ui->parameterBoxHubContents->update();
|
ui->parameterBoxHubContents->update();
|
||||||
ui->parameterBoxHubContents->adjustSize();
|
ui->parameterBoxHubContents->adjustSize();
|
||||||
ui->scrollArea->updateGeometry();
|
ui->scrollArea->updateGeometry();
|
||||||
|
|||||||
@ -13,96 +13,178 @@ ParameterWorkspace::~ParameterWorkspace()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Устанавливает количество устройств и обновляет интерфейс.
|
||||||
|
*
|
||||||
|
* Очистка текущего списка устройств и удаления соответствующих вкладок,
|
||||||
|
* затем создание новых устройств и добавление их в интерфейс.
|
||||||
|
*
|
||||||
|
* @param count Новое количество устройств.
|
||||||
|
*/
|
||||||
void ParameterWorkspace::setDeviceCount(int count)
|
void ParameterWorkspace::setDeviceCount(int count)
|
||||||
{
|
{
|
||||||
for(const auto& device : deviceList) {
|
// Удаление и очистка текущих устройств
|
||||||
device.tab->deleteLater();
|
for (const auto& device : deviceList) {
|
||||||
device.device->deleteLater();
|
device.tab->deleteLater(); ///< Удаление вкладки устройства
|
||||||
|
device.device->deleteLater(); ///< Удаление объекта устройства
|
||||||
}
|
}
|
||||||
deviceList.clear();
|
deviceList.clear(); ///< Очистка списка устройств
|
||||||
for(int i = 0; i < ui->tabWidget->count(); i++)
|
// Удаление всех вкладок из таб-виджета
|
||||||
|
for (int i = 0; i < ui->tabWidget->count(); ++i) {
|
||||||
ui->tabWidget->removeTab(i);
|
ui->tabWidget->removeTab(i);
|
||||||
for(int i = 0; i < count; i++) {
|
}
|
||||||
device* _device = new device();
|
// Создание новых устройств
|
||||||
deviceList.append(*_device);
|
for (int i = 0; i < count; ++i) {
|
||||||
deviceList[i].device = new ParameterDevice();
|
auto _device = new device(); ///< Создаваемое устройство
|
||||||
int newtab = ui->tabWidget->addTab(deviceList[i].device, tr("Device №%1").arg(i+1));
|
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);
|
deviceList[i].tab = ui->tabWidget->widget(newtab);
|
||||||
|
// Подключение сигналов от устройства к слотам
|
||||||
connect(deviceList[i].device, &ParameterDevice::read, this,
|
connect(deviceList[i].device, &ParameterDevice::read, this,
|
||||||
[this, i](QModbusRequest request, quint16 objectID){
|
[this, i](QModbusRequest request, quint16 objectID) {
|
||||||
readDeviceIdentification(deviceList[i].device, request, deviceList[i].adr, objectID);
|
readDeviceIdentification(deviceList[i].device, request, deviceList[i].adr, objectID);
|
||||||
});
|
});
|
||||||
connect(deviceList[i].device, &ParameterDevice::writeSingleCoil, this,
|
connect(deviceList[i].device, &ParameterDevice::writeSingleCoil, this,
|
||||||
[this, i](int coilAddress, bool value){
|
[this, i](int coilAddress, bool value){
|
||||||
writeSingleCoil(deviceList[i].adr, coilAddress, value);
|
writeSingleCoil(deviceList[i].adr, coilAddress, value);
|
||||||
});
|
});
|
||||||
connect(deviceList[i].device, &ParameterDevice::writeSingleRegister, this,
|
connect(deviceList[i].device, &ParameterDevice::writeSingleRegister, this,
|
||||||
[this, i](int registerAddress, quint16 value){
|
[this, i](int registerAddress, quint16 value){
|
||||||
writeSingleRegister(deviceList[i].adr, registerAddress, 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)
|
void ParameterWorkspace::writeSingleCoil(int adr, int coilAddress, bool value)
|
||||||
{
|
{
|
||||||
if(!modbusDevice && modbusDevice->state() == QModbusDevice::ConnectedState)
|
// Проверка правильности состояния соединения
|
||||||
|
if (!modbusDevice || modbusDevice->state() != QModbusDevice::ConnectedState)
|
||||||
return;
|
return;
|
||||||
|
// Создание пакета данных для записи
|
||||||
QModbusDataUnit unit(QModbusDataUnit::Coils, coilAddress, 1);
|
QModbusDataUnit unit(QModbusDataUnit::Coils, coilAddress, 1);
|
||||||
unit.setValue(0, value ? 1 : 0);
|
unit.setValue(0, value ? 1 : 0);
|
||||||
if(auto *reply = modbusDevice->sendWriteRequest(unit, adr)) {
|
// Отправка запроса
|
||||||
if(!reply->isFinished())
|
if (auto *reply = modbusDevice->sendWriteRequest(unit, adr)) {
|
||||||
|
// Обработка завершения запроса
|
||||||
|
if (!reply->isFinished()) {
|
||||||
connect(reply, &QModbusReply::finished, this, [reply]() {
|
connect(reply, &QModbusReply::finished, this, [reply]() {
|
||||||
if(reply->error() != QModbusDevice::NoError)
|
// Проверка ошибок выполнения
|
||||||
{/*ERROR*/}
|
if (reply->error() != QModbusDevice::NoError) {
|
||||||
reply->deleteLater();
|
// Обработка ошибок (можно добавить лог или сообщение)
|
||||||
|
}
|
||||||
|
reply->deleteLater(); // Очистка объекта
|
||||||
});
|
});
|
||||||
else
|
} else {
|
||||||
|
// Если ответ уже готов, очищаем его сразу
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Отправляет команду на запись одного регистрового значения по Modbus.
|
||||||
|
*
|
||||||
|
* Проверяет состояние соединения, создает запрос и отправляет его.
|
||||||
|
* Обрабатывает завершение запроса и возможные ошибки.
|
||||||
|
*
|
||||||
|
* @param adr Адрес устройства по Modbus.
|
||||||
|
* @param regAddress Адрес регистрового аргумента.
|
||||||
|
* @param value Значение для записи.
|
||||||
|
*/
|
||||||
void ParameterWorkspace::writeSingleRegister(int adr, int regAddress, quint16 value)
|
void ParameterWorkspace::writeSingleRegister(int adr, int regAddress, quint16 value)
|
||||||
{
|
{
|
||||||
if(!modbusDevice && modbusDevice->state() == QModbusDevice::ConnectedState)
|
// Проверка правильности состояния соединения
|
||||||
|
if (!modbusDevice || modbusDevice->state() != QModbusDevice::ConnectedState)
|
||||||
return;
|
return;
|
||||||
|
// Создание пакета данных для записи
|
||||||
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, regAddress, 1);
|
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, regAddress, 1);
|
||||||
unit.setValue(0, value);
|
unit.setValue(0, value);
|
||||||
if(auto *reply = modbusDevice->sendWriteRequest(unit, adr)) {
|
// Отправка запроса
|
||||||
if(!reply->isFinished())
|
if (auto *reply = modbusDevice->sendWriteRequest(unit, adr)) {
|
||||||
|
// Обработка завершения запроса
|
||||||
|
if (!reply->isFinished()) {
|
||||||
connect(reply, &QModbusReply::finished, this, [reply]() {
|
connect(reply, &QModbusReply::finished, this, [reply]() {
|
||||||
if(reply->error() != QModbusDevice::NoError)
|
// Проверка наличия ошибок
|
||||||
{/*ERROR*/}
|
if (reply->error() != QModbusDevice::NoError) {
|
||||||
reply->deleteLater();
|
// Можно добавить лог или обработку ошибок
|
||||||
|
}
|
||||||
|
reply->deleteLater(); // Очистка объекта
|
||||||
});
|
});
|
||||||
else
|
} else {
|
||||||
|
// Если ответ уже готов, очищаем его сразу
|
||||||
reply->deleteLater();
|
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)
|
void ParameterWorkspace::readDeviceIdentification(ParameterDevice *device, QModbusRequest request, int adr, quint16 objectID)
|
||||||
{
|
{
|
||||||
if(!modbusDevice && modbusDevice->state() == QModbusDevice::ConnectedState)
|
// Проверка состояния соединения
|
||||||
|
if (!modbusDevice || modbusDevice->state() != QModbusDevice::ConnectedState)
|
||||||
return;
|
return;
|
||||||
if(auto *reply = modbusDevice->sendRawRequest(request, adr)) {
|
// Отправка сырого запроса к устройству
|
||||||
if(!reply->isFinished()) {
|
if (auto *reply = modbusDevice->sendRawRequest(request, adr)) {
|
||||||
connect(reply, &QModbusReply::finished, this, [device, reply, objectID](){
|
// Обработка ответа, если он не завершен
|
||||||
if(reply->error() == QModbusDevice::NoError) {
|
if (!reply->isFinished()) {
|
||||||
|
connect(reply, &QModbusReply::finished, this, [device, reply, objectID]() {
|
||||||
|
// Проверка ошибок
|
||||||
|
if (reply->error() == QModbusDevice::NoError) {
|
||||||
QModbusResponse resp = reply->rawResult();
|
QModbusResponse resp = reply->rawResult();
|
||||||
if(resp.data().size() >= MODBUS_REQUEST_PROTOCOL_INFO_LENGTH) {
|
// Проверка размера данных
|
||||||
QString result = QString(resp.data().remove(0, MODBUS_REQUEST_PROTOCOL_INFO_LENGTH));
|
if (resp.data().size() >= MODBUS_REQUEST_PROTOCOL_INFO_LENGTH) {
|
||||||
|
// Удаление протокольной части данных
|
||||||
|
QByteArray data = resp.data();
|
||||||
|
data.remove(0, MODBUS_REQUEST_PROTOCOL_INFO_LENGTH);
|
||||||
|
QString result = QString::fromUtf8(data); // или другой метод преобразования, в зависимости от формата
|
||||||
device->setAnswer(result, objectID);
|
device->setAnswer(result, objectID);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Обработка ошибок
|
||||||
device->setError(reply->errorString());
|
device->setError(reply->errorString());
|
||||||
qDebug()<<"Получен ответ:" + reply->errorString();
|
qDebug() << "Получен ответ с ошибкой:" << reply->errorString();
|
||||||
}
|
}
|
||||||
reply->deleteLater();
|
reply->deleteLater(); // Очистка объекта
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
|
// Ошибка при отправке запроса
|
||||||
device->setError("Unknow error");
|
device->setError("Unknow error");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обновляет параметры устройства по идентификатору.
|
||||||
|
*
|
||||||
|
* Обновляет статус активности, адрес и устанавливает новый адрес в объекте устройства.
|
||||||
|
*
|
||||||
|
* @param ID Индекс или уникальный идентификатор устройства в списке.
|
||||||
|
* @param status Новый статус активности (true/false).
|
||||||
|
* @param adr Новый адрес устройства.
|
||||||
|
*/
|
||||||
void ParameterWorkspace::updateDevice(int ID, bool status, int adr)
|
void ParameterWorkspace::updateDevice(int ID, bool status, int adr)
|
||||||
{
|
{
|
||||||
deviceList[ID].isActive = status;
|
deviceList[ID].isActive = status;
|
||||||
|
|||||||
@ -13,26 +13,52 @@ ScanBoard::~ScanBoard()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получает текущий состояние чекбокса.
|
||||||
|
* @return Текущий Qt::CheckState.
|
||||||
|
*/
|
||||||
Qt::CheckState ScanBoard::getCheckState()
|
Qt::CheckState ScanBoard::getCheckState()
|
||||||
{
|
{
|
||||||
return checkState;
|
return checkState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработчик изменения состояния чекбокса "Apply to All".
|
||||||
|
* @param arg1 Новое состояние чекбокса (целое число, преобразуемое в Qt::CheckState).
|
||||||
|
*
|
||||||
|
* Этот слот вызывается при изменении состояния чекбокса и сохраняет новое значение.
|
||||||
|
*/
|
||||||
void ScanBoard::on_applyToAllBox_stateChanged(int arg1)
|
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)
|
void ScanBoard::showMeTheTruth(QString resultOfScan)
|
||||||
{
|
{
|
||||||
ui->logger->append(resultOfScan);
|
ui->logger->append(resultOfScan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получает текущий baud-скорость.
|
||||||
|
* @return Значение скорости в виде quint16.
|
||||||
|
*/
|
||||||
quint16 ScanBoard::getBaud()
|
quint16 ScanBoard::getBaud()
|
||||||
{
|
{
|
||||||
return baud;
|
return baud;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обработчик изменения текста в поле выбора скорости baud.
|
||||||
|
* @param arg1 Новое значение текса в comboBox.
|
||||||
|
*
|
||||||
|
* Парсит текст в целое число и сохраняет в переменную baud.
|
||||||
|
*/
|
||||||
void ScanBoard::on_baudRateBox_currentTextChanged(const QString &arg1)
|
void ScanBoard::on_baudRateBox_currentTextChanged(const QString &arg1)
|
||||||
{
|
{
|
||||||
baud = arg1.toInt(nullptr, 10);
|
baud = arg1.toInt(nullptr, 10);
|
||||||
|
|||||||
@ -36,40 +36,74 @@ SettingsDialog::~SettingsDialog()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Возвращает текущие настройки.
|
||||||
|
* @return Объект настроек типа Settings.
|
||||||
|
*/
|
||||||
SettingsDialog::Settings SettingsDialog::settings() const
|
SettingsDialog::Settings SettingsDialog::settings() const
|
||||||
{
|
{
|
||||||
return m_settings;
|
return m_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обновляет индекс baud-скорости в UI и сохраняет значение в настройки.
|
||||||
|
* @param baud Индекс выбранной скорости.
|
||||||
|
* @return Новое значение baud, сохранённое в настройках.
|
||||||
|
*/
|
||||||
int SettingsDialog::UpdateBaud(int baud)
|
int SettingsDialog::UpdateBaud(int baud)
|
||||||
{
|
{
|
||||||
|
// Устанавливаем текущий индекс в comboBox
|
||||||
ui->baudCombo->setCurrentIndex(baud);
|
ui->baudCombo->setCurrentIndex(baud);
|
||||||
|
// Обновляем настройки и возвращаем новую скорость как число
|
||||||
return (m_settings.baud = ui->baudCombo->currentText().toInt());
|
return (m_settings.baud = ui->baudCombo->currentText().toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обновляет индекс паритета в UI и сохраняет значение в настройки.
|
||||||
|
* @param parity Индекс выбранного паритета.
|
||||||
|
* @return Новое значение паритета, сохранённое в настройках.
|
||||||
|
*/
|
||||||
int SettingsDialog::UpdateParity(int parity)
|
int SettingsDialog::UpdateParity(int parity)
|
||||||
{
|
{
|
||||||
|
// Устанавливаем текущий индекс
|
||||||
ui->parityCombo->setCurrentIndex(parity);
|
ui->parityCombo->setCurrentIndex(parity);
|
||||||
if(parity > 0)
|
// Если parity > 0, увеличиваем его значение перед сохранением
|
||||||
|
if (parity > 0)
|
||||||
return (m_settings.parity = ++parity);
|
return (m_settings.parity = ++parity);
|
||||||
else
|
else
|
||||||
return (m_settings.parity = parity);
|
return (m_settings.parity = parity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получает текущий индекс выбранной baud-скорости.
|
||||||
|
* @return Индекс выбранного элемента в comboBox.
|
||||||
|
*/
|
||||||
int SettingsDialog::curBaud()
|
int SettingsDialog::curBaud()
|
||||||
{
|
{
|
||||||
return ui->baudCombo->currentIndex();
|
return ui->baudCombo->currentIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получает текущий индекс выбранного паритета.
|
||||||
|
* @return Индекс выбранного элемента в comboBox.
|
||||||
|
*/
|
||||||
int SettingsDialog::curParity()
|
int SettingsDialog::curParity()
|
||||||
{
|
{
|
||||||
return ui->parityCombo->currentIndex();
|
return ui->parityCombo->currentIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обновляет список доступных COM-портов.
|
||||||
|
*
|
||||||
|
* Этот метод вызывается при нажатии кнопки, обновляя список портов в UI.
|
||||||
|
* Он очищает текущий список и заполняет его всеми доступными портами.
|
||||||
|
*/
|
||||||
void SettingsDialog::on_updateComBox_clicked()
|
void SettingsDialog::on_updateComBox_clicked()
|
||||||
{
|
{
|
||||||
ui->comBox->clear();
|
ui->comBox->clear();
|
||||||
const auto listPorts = QSerialPortInfo::availablePorts();
|
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->comBox->addItem(QString("%1: %2").arg(port.portName(), port.manufacturer()), QVariant(port.portName()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,31 +19,62 @@ int WriteRegisterModel::columnCount(const QModelIndex &/*parent*/) const
|
|||||||
return ColumnCount;
|
return ColumnCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Возвращает данные модели для отображения в представлении.
|
||||||
|
*
|
||||||
|
* В зависимости от роли и столбца возвращает соответствующее значение:
|
||||||
|
* - номер строки;
|
||||||
|
* - имя;
|
||||||
|
* - состояние чекбокса;
|
||||||
|
* - значение в Вольтах для регистров и текущего напряжения.
|
||||||
|
*
|
||||||
|
* @param index Индекс элемента модели.
|
||||||
|
* @param role Роль запрашиваемых данных.
|
||||||
|
* @return Значение данных в виде QVariant.
|
||||||
|
*/
|
||||||
QVariant WriteRegisterModel::data(const QModelIndex &index, int role) const
|
QVariant WriteRegisterModel::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if(!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
|
// Проверка корректности индекса
|
||||||
|
if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
// Проверка соответствия размеров
|
||||||
Q_ASSERT(m_coils.count() == RowCount);
|
Q_ASSERT(m_coils.count() == RowCount);
|
||||||
Q_ASSERT(m_holdingRegisters.count() == RowCount);
|
Q_ASSERT(m_holdingRegisters.count() == RowCount);
|
||||||
if(index.column() == NumColumn && role == Qt::DisplayRole)
|
// Обработка столбца с номером
|
||||||
|
if (index.column() == NumColumn && role == Qt::DisplayRole)
|
||||||
return QString::number(index.row());
|
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() == NameColumn && role == Qt::DisplayRole)
|
||||||
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;
|
return m_coils.at(index.row()) ? Qt::Checked : Qt::Unchecked;
|
||||||
if(index.column() == HoldingColumn && role == Qt::DisplayRole) // holding registers
|
// Обработка значения в регистре (в Вольтах)
|
||||||
return QString("%1 В").arg(QString::number((double)((double)m_holdingRegisters.at(index.row()) / (double)1000), 'f', 3));
|
if (index.column() == HoldingColumn && role == Qt::DisplayRole)
|
||||||
if(index.column() == CurrentUColumn && role == Qt::DisplayRole)
|
return QString("%1 В").arg(QString::number((double)m_holdingRegisters.at(index.row()) / 1000.0, 'f', 3));
|
||||||
return QString("%1 В").arg(QString::number((double)((double)m_currentU.at(index.row()) / (double)1000), '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();
|
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
|
QVariant WriteRegisterModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
if(role != Qt::DisplayRole)
|
if (role != Qt::DisplayRole)
|
||||||
return QVariant();
|
return QVariant();
|
||||||
if(orientation == Qt::Horizontal) {
|
if (orientation == Qt::Horizontal) {
|
||||||
switch(section) {
|
switch (section) {
|
||||||
case NumColumn:
|
case NumColumn:
|
||||||
return QStringLiteral("#Reg");
|
return QStringLiteral("#Reg");
|
||||||
case NameColumn:
|
case NameColumn:
|
||||||
@ -61,67 +92,136 @@ QVariant WriteRegisterModel::headerData(int section, Qt::Orientation orientation
|
|||||||
return QVariant();
|
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)
|
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;
|
return false;
|
||||||
|
// Проверка размеров массивов
|
||||||
Q_ASSERT(m_coils.count() == RowCount);
|
Q_ASSERT(m_coils.count() == RowCount);
|
||||||
Q_ASSERT(m_holdingRegisters.count() == RowCount);
|
Q_ASSERT(m_holdingRegisters.count() == RowCount);
|
||||||
if(index.column() == CoilsColumn && role == Qt::CheckStateRole) { // coils
|
// Обработка для чекбоксов
|
||||||
auto s = static_cast<Qt::CheckState>(value.toUInt());
|
if (index.column() == CoilsColumn && role == Qt::CheckStateRole) {
|
||||||
s == Qt::Checked ? m_coils.setBit(index.row()) : m_coils.clearBit(index.row());
|
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);
|
emit dataChanged(index, index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(index.column() == HoldingColumn && role == Qt::EditRole) { // holding registers
|
// Обработка для регистров
|
||||||
|
if (index.column() == HoldingColumn && role == Qt::EditRole) {
|
||||||
bool result = false;
|
bool result = false;
|
||||||
quint16 newValue = value.toString().toUShort(&result, 10);
|
// Преобразование QVariant в строку, затем в число
|
||||||
if(result)
|
QString valueStr = value.toString();
|
||||||
|
quint16 newValue = valueStr.toUShort(&result, 10);
|
||||||
|
if (result) {
|
||||||
m_holdingRegisters[index.row()] = newValue;
|
m_holdingRegisters[index.row()] = newValue;
|
||||||
emit dataChanged(index, index);
|
emit dataChanged(index, index);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
// Если не обработано, вернуть false
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Устанавливает флаги для элементов модели, определяя их поведение.
|
||||||
|
*
|
||||||
|
* @param index Индекс элемента модели.
|
||||||
|
* @return Флаги, определяющие свойства элемента.
|
||||||
|
*/
|
||||||
Qt::ItemFlags WriteRegisterModel::flags(const QModelIndex &index) const
|
Qt::ItemFlags WriteRegisterModel::flags(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
if(!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
|
// Проверка валидности индекса
|
||||||
|
if (!index.isValid() || index.row() >= RowCount || index.column() >= ColumnCount)
|
||||||
return QAbstractTableModel::flags(index);
|
return QAbstractTableModel::flags(index);
|
||||||
|
// Получение базовых флагов
|
||||||
Qt::ItemFlags flags = QAbstractTableModel::flags(index);
|
Qt::ItemFlags flags = QAbstractTableModel::flags(index);
|
||||||
if((index.row() < m_address) || (index.row() >= (m_address + m_number)))
|
// Отключение элементов вне диапазона адресов
|
||||||
|
if ((index.row() < m_address) || (index.row() >= (m_address + m_number)))
|
||||||
flags &= ~Qt::ItemIsEnabled;
|
flags &= ~Qt::ItemIsEnabled;
|
||||||
if(index.column() == CoilsColumn) // coils
|
// Установка флага чекбокса для столбца Coils
|
||||||
|
if (index.column() == CoilsColumn)
|
||||||
return flags | Qt::ItemIsUserCheckable;
|
return flags | Qt::ItemIsUserCheckable;
|
||||||
if(index.column() == HoldingColumn) // holding registers
|
// Установка флага редактируемости для столбца HoldingRegisters
|
||||||
|
if (index.column() == HoldingColumn)
|
||||||
return flags | Qt::ItemIsEditable;
|
return flags | Qt::ItemIsEditable;
|
||||||
|
// Возврат текущих флагов по умолчанию
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Устанавливает начальный адрес для модели.
|
||||||
|
*
|
||||||
|
* @param address Начальный адрес.
|
||||||
|
*/
|
||||||
void WriteRegisterModel::setStartAddress(int address)
|
void WriteRegisterModel::setStartAddress(int address)
|
||||||
{
|
{
|
||||||
m_address = address;
|
m_address = address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Устанавливает число значений (величин) на основе строки.
|
||||||
|
*
|
||||||
|
* @param number Строковое представление числа.
|
||||||
|
*/
|
||||||
void WriteRegisterModel::setNumberOfValues(const QString &number)
|
void WriteRegisterModel::setNumberOfValues(const QString &number)
|
||||||
{
|
{
|
||||||
m_number = number.toInt();
|
m_number = number.toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получает состояние coil по индексу.
|
||||||
|
*
|
||||||
|
* @param index Индекс элемента.
|
||||||
|
* @return true, если coil активен; иначе false.
|
||||||
|
*/
|
||||||
bool WriteRegisterModel::get_coil(const QModelIndex &index)
|
bool WriteRegisterModel::get_coil(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
return m_coils.at(index.row());
|
return m_coils.at(index.row());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Получает значение holding register по индексу.
|
||||||
|
*
|
||||||
|
* @param index Индекс элемента.
|
||||||
|
* @return Значение регистров.
|
||||||
|
*/
|
||||||
uint WriteRegisterModel::get_holreg(const QModelIndex &index)
|
uint WriteRegisterModel::get_holreg(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
return m_holdingRegisters.at(index.row());
|
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)
|
bool WriteRegisterModel::set_currentU(unsigned _tmpU, unsigned index)
|
||||||
{
|
{
|
||||||
|
// Установка значения для текущего индекса
|
||||||
m_currentU[index] = _tmpU;
|
m_currentU[index] = _tmpU;
|
||||||
if(isHR)
|
|
||||||
|
// Если isHR активен, обновляем также и по смещенному индексу
|
||||||
|
if (isHR)
|
||||||
m_currentU[index + m_number] = _tmpU;
|
m_currentU[index + m_number] = _tmpU;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user