|
- #include "free_monitor_widget.h"
-
- #include "services/register_monitor_service.h"
- #include "toolbar_icon_factory.h"
- #include "ui_free_monitor_widget.h"
-
- #include <QComboBox>
- #include <QDoubleValidator>
- #include <QHeaderView>
- #include <QIntValidator>
- #include <QLineEdit>
- #include <QLocale>
- #include <QMessageBox>
- #include <QPushButton>
- #include <QTableWidgetItem>
-
- #include <cstdint>
- #include <cmath>
- #include <limits>
- #include <set>
- #include <type_traits>
-
- namespace {
-
- QString fromUtf8(const std::string &value)
- {
- return QString::fromUtf8(value.data(), static_cast<int>(value.size()));
- }
-
- QString addressText(const RegisterAddress &address)
- {
- return QString::fromStdString(address.toString());
- }
-
- QString numericValueText(const RegisterNumericValue &value)
- {
- return std::visit(
- [](const auto &typed_value)
- {
- using Value = std::decay_t<decltype(typed_value)>;
- if constexpr (std::is_same_v<Value, float>)
- {
- return QString::number(
- static_cast<double>(typed_value), 'g',
- std::numeric_limits<float>::max_digits10);
- }
- else if constexpr (std::is_same_v<Value, double>)
- {
- return QString::number(
- typed_value, 'g', std::numeric_limits<double>::max_digits10);
- }
- else
- {
- return QString::number(static_cast<qlonglong>(typed_value));
- }
- },
- value);
- }
-
- } // namespace
-
- FreeMonitorWidget::FreeMonitorWidget(
- RegisterMonitorService &service,
- QWidget *parent)
- : QWidget(parent),
- ui_(std::make_unique<Ui::FreeMonitorWidget>()),
- 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_->dataTypeComboBox->setItemData(0, static_cast<int>(RegisterDataType::Int16));
- ui_->dataTypeComboBox->setItemData(1, static_cast<int>(RegisterDataType::Int32));
- ui_->dataTypeComboBox->setItemData(2, static_cast<int>(RegisterDataType::Float32));
- ui_->dataTypeComboBox->setItemData(3, static_cast<int>(RegisterDataType::Float64));
- 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::Editing)
- {
- source = tr("数据源:离线初始值 · 虚拟 M/D");
- values_available = true;
- }
- else 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<MonitorValue> values = values_available
- ? service_.values(communication_fault) : std::vector<MonitorValue>{};
- 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<std::size_t>(row) >= values.size())
- {
- value_item->setText(QStringLiteral("--"));
- state_item->setText(tr("未运行"));
- continue;
- }
- const MonitorValue &value = values.at(static_cast<std::size_t>(row));
- if (value.state == MonitorValueState::Unavailable)
- {
- value_item->setText(QStringLiteral("--"));
- state_item->setText(mode == ApplicationMode::OnlineRunning
- ? tr("等待读取") : tr("不可用"));
- continue;
- }
- if (value.address.area() == RegisterArea::M)
- {
- value_item->setText(value.bitValue ? QStringLiteral("ON")
- : QStringLiteral("OFF"));
- }
- else
- {
- value_item->setText(numericValueText(value.numericValue));
- }
- state_item->setText(value.state == MonitorValueState::CommunicationFault
- ? tr("通信故障,最后有效值") : tr("有效"));
- }
- updateWriteControls();
- }
-
- void FreeMonitorWidget::reloadAddresses()
- {
- const std::vector<RegisterAddress> &addresses = service_.addresses();
- ui_->monitorTable->setRowCount(static_cast<int>(addresses.size()));
- for (std::size_t index = 0; index < addresses.size(); ++index)
- {
- const RegisterAddress &address = addresses[index];
- const int row = static_cast<int>(index);
- ui_->monitorTable->setItem(row, 0, new QTableWidgetItem(addressText(address)));
- const MonitorPoint &point = service_.points().at(index);
- QString type_text = tr("位");
- if (address.area() == RegisterArea::D)
- {
- const RegisterDataTypeDescriptor &descriptor =
- registerDataTypeDescriptor(point.dataType);
- type_text = QString::fromLatin1(descriptor.displayName);
- if (descriptor.wordCount > 1)
- {
- type_text += tr(" (%1~%2)").arg(
- addressText(address),
- addressText(RegisterAddress{
- RegisterArea::D,
- point.address.index() + descriptor.wordCount - 1}));
- }
- }
- ui_->monitorTable->setItem(row, 1, new QTableWidgetItem(type_text));
- 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);
- if (point.dataType == RegisterDataType::Int16)
- {
- target->setValidator(new QIntValidator(-32768, 32767, target));
- }
- else if (point.dataType == RegisterDataType::Int32)
- {
- target->setValidator(new QIntValidator(
- std::numeric_limits<int>::min(),
- std::numeric_limits<int>::max(), target));
- }
- else
- {
- const double maximum = point.dataType == RegisterDataType::Float32
- ? static_cast<double>(std::numeric_limits<float>::max())
- : std::numeric_limits<double>::max();
- const int digits = point.dataType == RegisterDataType::Float32
- ? std::numeric_limits<float>::max_digits10
- : std::numeric_limits<double>::max_digits10;
- auto *validator = new QDoubleValidator(-maximum, maximum, digits, target);
- validator->setNotation(QDoubleValidator::ScientificNotation);
- validator->setLocale(QLocale::c());
- target->setValidator(validator);
- target->setPlaceholderText(tr("小数或科学计数法"));
- }
- 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<std::size_t>(row) >= service_.addresses().size())
- {
- return;
- }
-
- const MonitorPoint point = service_.points().at(static_cast<std::size_t>(row));
- const RegisterAddress address = point.address;
- RegisterMonitorWriteResult result;
- if (address.area() == RegisterArea::M)
- {
- auto *target = qobject_cast<QComboBox *>(ui_->monitorTable->cellWidget(row, 3));
- if (target == nullptr)
- {
- return;
- }
- result = service_.writeBit(address, target->currentData().toBool());
- }
- else
- {
- auto *target = qobject_cast<QLineEdit *>(ui_->monitorTable->cellWidget(row, 3));
- if (target == nullptr)
- {
- return;
- }
- bool converted = false;
- const double value = QLocale::c().toDouble(target->text(), &converted);
- if (!converted
- || !encodeRegisterNumericValue(point.dataType, value).has_value())
- {
- ui_->monitorTable->item(row, 5)->setText(tr("目标值无效"));
- emit operationMessage(
- tr("写入 D 区地址失败:请输入有效的 %1 数值")
- .arg(QString::fromLatin1(
- registerDataTypeDescriptor(point.dataType).displayName)));
- return;
- }
- result = service_.writeNumeric(address, point.dataType, 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<std::size_t>(address.size())),
- ui_->countSpinBox->value(),
- static_cast<RegisterDataType>(ui_->dataTypeComboBox->currentData().toInt()));
- if (result.succeeded)
- {
- reloadAddresses();
- ui_->addressEdit->selectAll();
- emit monitorAddressesChanged();
- const QString message = fromUtf8(result.message);
- emit operationMessage(
- message.isEmpty()
- ? tr("已添加 %1 个监控点").arg(result.affectedCount)
- : tr("已添加 %1 个监控点;%2")
- .arg(result.affectedCount)
- .arg(message));
- return;
- }
- handleResult(tr("添加监控地址"), false, result.message);
- }
-
- void FreeMonitorWidget::removeSelectedAddresses()
- {
- std::set<int> rows;
- for (const QModelIndex &index : ui_->monitorTable->selectionModel()->selectedRows())
- {
- rows.insert(index.row());
- }
- std::vector<MonitorPoint> points;
- const std::vector<MonitorPoint> &all_points = service_.points();
- for (int row : rows)
- {
- if (row >= 0 && static_cast<std::size_t>(row) < all_points.size())
- {
- points.push_back(all_points.at(static_cast<std::size_t>(row)));
- }
- }
- const RegisterMonitorResult result = service_.remove(points);
- 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));
- }
- }
|