#include "free_monitor_widget.h" #include "services/register_monitor_service.h" #include "toolbar_icon_factory.h" #include "ui_free_monitor_widget.h" #include #include #include #include #include #include #include #include #include namespace { QString fromUtf8(const std::string &value) { return QString::fromUtf8(value.data(), static_cast(value.size())); } QString addressText(const RegisterAddress &address) { return QString::fromStdString(address.toString()); } } // namespace FreeMonitorWidget::FreeMonitorWidget( RegisterMonitorService &service, QWidget *parent) : QWidget(parent), ui_(std::make_unique()), service_(service) { ui_->setupUi(this); ui_->removeButton->setIcon(makeUiIcon(UiIcon::Delete)); ui_->clearButton->setIcon(makeUiIcon(UiIcon::ClearList)); ui_->monitorTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); ui_->monitorTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::ResizeToContents); ui_->monitorTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); ui_->monitorTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch); ui_->monitorTable->horizontalHeader()->setSectionResizeMode(4, QHeaderView::ResizeToContents); ui_->monitorTable->horizontalHeader()->setSectionResizeMode(5, QHeaderView::Stretch); ui_->monitorTable->verticalHeader()->setVisible(false); connect(ui_->addButton, &QPushButton::clicked, this, &FreeMonitorWidget::addAddresses); connect(ui_->addressEdit, &QLineEdit::returnPressed, this, &FreeMonitorWidget::addAddresses); connect(ui_->removeButton, &QToolButton::clicked, this, &FreeMonitorWidget::removeSelectedAddresses); connect(ui_->clearButton, &QToolButton::clicked, this, &FreeMonitorWidget::clearAddresses); reloadAddresses(); } FreeMonitorWidget::~FreeMonitorWidget() = default; void FreeMonitorWidget::setWriteEnabled(bool enabled) { write_enabled_ = enabled; updateWriteControls(); } void FreeMonitorWidget::refreshValues( ApplicationMode mode, PlcConnectionState plc_state) { QString source = tr("数据源:未启用"); bool values_available = false; bool communication_fault = false; if (mode == ApplicationMode::OfflineRunning) { source = tr("数据源:虚拟 M/D"); values_available = true; } else if (mode == ApplicationMode::OnlineRunning) { source = tr("数据源:PLC 缓存"); values_available = true; communication_fault = plc_state == PlcConnectionState::Faulted; } ui_->sourceLabel->setText(source); const std::vector values = values_available ? service_.values(communication_fault) : std::vector{}; for (int row = 0; row < ui_->monitorTable->rowCount(); ++row) { QTableWidgetItem *value_item = ui_->monitorTable->item(row, 2); QTableWidgetItem *state_item = ui_->monitorTable->item(row, 5); if (!values_available || static_cast(row) >= values.size()) { value_item->setText(QStringLiteral("--")); state_item->setText(tr("未运行")); continue; } const MonitorValue &value = values.at(static_cast(row)); if (value.state == MonitorValueState::Unavailable) { value_item->setText(QStringLiteral("--")); state_item->setText(mode == ApplicationMode::OnlineRunning ? tr("等待读取") : tr("不可用")); continue; } value_item->setText(value.address.area() == RegisterArea::M ? (value.bitValue ? QStringLiteral("ON") : QStringLiteral("OFF")) : QString::number(value.wordValue)); state_item->setText(value.state == MonitorValueState::CommunicationFault ? tr("通信故障,最后有效值") : tr("有效")); } updateWriteControls(); } void FreeMonitorWidget::reloadAddresses() { const std::vector &addresses = service_.addresses(); ui_->monitorTable->setRowCount(static_cast(addresses.size())); for (std::size_t index = 0; index < addresses.size(); ++index) { const RegisterAddress &address = addresses[index]; const int row = static_cast(index); ui_->monitorTable->setItem(row, 0, new QTableWidgetItem(addressText(address))); ui_->monitorTable->setItem( row, 1, new QTableWidgetItem(address.area() == RegisterArea::M ? tr("位") : tr("16 位整数"))); ui_->monitorTable->setItem(row, 2, new QTableWidgetItem(QStringLiteral("--"))); if (address.area() == RegisterArea::M) { auto *target = new QComboBox(ui_->monitorTable); target->addItem(tr("OFF"), false); target->addItem(tr("ON"), true); ui_->monitorTable->setCellWidget(row, 3, target); } else { auto *target = new QLineEdit(ui_->monitorTable); target->setValidator(new QIntValidator(-32768, 32767, target)); target->setText(QStringLiteral("0")); target->setAlignment(Qt::AlignRight | Qt::AlignVCenter); ui_->monitorTable->setCellWidget(row, 3, target); } auto *write_button = new QPushButton(tr("写入"), ui_->monitorTable); write_button->setProperty("monitorRow", row); connect(write_button, &QPushButton::clicked, this, [this, write_button] { writeRow(write_button->property("monitorRow").toInt()); }); ui_->monitorTable->setCellWidget(row, 4, write_button); ui_->monitorTable->setItem(row, 5, new QTableWidgetItem(tr("未运行"))); } updateWriteControls(); } void FreeMonitorWidget::writeRow(int row) { if (!write_enabled_ || row < 0 || static_cast(row) >= service_.addresses().size()) { return; } const RegisterAddress address = service_.addresses().at(static_cast(row)); RegisterMonitorWriteResult result; if (address.area() == RegisterArea::M) { auto *target = qobject_cast(ui_->monitorTable->cellWidget(row, 3)); if (target == nullptr) { return; } result = service_.writeBit(address, target->currentData().toBool()); } else { auto *target = qobject_cast(ui_->monitorTable->cellWidget(row, 3)); if (target == nullptr) { return; } bool converted = false; const int value = target->text().toInt(&converted); if (!converted || value < -32768 || value > 32767) { ui_->monitorTable->item(row, 5)->setText(tr("目标值无效")); emit operationMessage(tr("写入 D 区地址失败:请输入 -32768~32767 的整数")); return; } result = service_.writeWord(address, static_cast(value)); } QTableWidgetItem *state_item = ui_->monitorTable->item(row, 5); if (result.succeeded) { state_item->setText(tr("写入请求已发送")); emit operationMessage(tr("%1 写入请求已发送,等待当前值刷新") .arg(addressText(address))); return; } state_item->setText(tr("写入失败")); handleResult(tr("写入 %1").arg(addressText(address)), false, result.message); } void FreeMonitorWidget::updateWriteControls() { for (int row = 0; row < ui_->monitorTable->rowCount(); ++row) { QWidget *target = ui_->monitorTable->cellWidget(row, 3); if (target != nullptr) { target->setEnabled(write_enabled_); } QWidget *write_button = ui_->monitorTable->cellWidget(row, 4); if (write_button != nullptr) { write_button->setEnabled(write_enabled_); } } } void FreeMonitorWidget::addAddresses() { const QByteArray address = ui_->addressEdit->text().toUtf8(); const RegisterMonitorResult result = service_.addRange( std::string(address.constData(), static_cast(address.size())), ui_->countSpinBox->value()); if (result.succeeded) { reloadAddresses(); ui_->addressEdit->selectAll(); emit monitorAddressesChanged(); emit operationMessage(tr("已添加 %1 个监控地址").arg(result.affectedCount)); return; } handleResult(tr("添加监控地址"), false, result.message); } void FreeMonitorWidget::removeSelectedAddresses() { std::set rows; for (const QModelIndex &index : ui_->monitorTable->selectionModel()->selectedRows()) { rows.insert(index.row()); } std::vector addresses; const std::vector &all_addresses = service_.addresses(); for (int row : rows) { if (row >= 0 && static_cast(row) < all_addresses.size()) { addresses.push_back(all_addresses.at(static_cast(row))); } } const RegisterMonitorResult result = service_.remove(addresses); if (result.succeeded) { reloadAddresses(); emit monitorAddressesChanged(); emit operationMessage(tr("已删除 %1 个监控地址").arg(result.affectedCount)); return; } handleResult(tr("删除监控地址"), false, result.message); } void FreeMonitorWidget::clearAddresses() { if (service_.addresses().empty()) { return; } if (QMessageBox::question( this, tr("清空自由监控"), tr("确定清空当前监控表吗?")) != QMessageBox::Yes) { return; } const RegisterMonitorResult result = service_.clear(); if (result.succeeded) { reloadAddresses(); emit monitorAddressesChanged(); emit operationMessage(tr("自由监控已清空")); return; } handleResult(tr("清空自由监控"), false, result.message); } void FreeMonitorWidget::handleResult( const QString &action, bool succeeded, const std::string &message) { if (!succeeded) { emit operationMessage(action + QStringLiteral(":") + fromUtf8(message)); } }