| @@ -18,6 +18,7 @@ SOURCES += \ | |||||
| src/main.cpp \ | src/main.cpp \ | ||||
| src/ui/main_window.cpp \ | src/ui/main_window.cpp \ | ||||
| src/ui/alarm_configuration_dialog.cpp \ | src/ui/alarm_configuration_dialog.cpp \ | ||||
| src/ui/register_comment_dialog.cpp \ | |||||
| src/ui/plc_connection_dialog.cpp \ | src/ui/plc_connection_dialog.cpp \ | ||||
| src/ui/free_monitor_widget.cpp \ | src/ui/free_monitor_widget.cpp \ | ||||
| src/ui/runtime_monitor_widget.cpp \ | src/ui/runtime_monitor_widget.cpp \ | ||||
| @@ -41,6 +42,7 @@ SOURCES += \ | |||||
| src/services/offline_simulation_service.cpp \ | src/services/offline_simulation_service.cpp \ | ||||
| src/services/runtime_mode_service.cpp \ | src/services/runtime_mode_service.cpp \ | ||||
| src/services/register_monitor_service.cpp \ | src/services/register_monitor_service.cpp \ | ||||
| src/services/register_comment_service.cpp \ | |||||
| src/infrastructure/plc_register_repository.cpp \ | src/infrastructure/plc_register_repository.cpp \ | ||||
| src/infrastructure/plc_communication_error_classifier.cpp \ | src/infrastructure/plc_communication_error_classifier.cpp \ | ||||
| src/infrastructure/plc_communication_service.cpp \ | src/infrastructure/plc_communication_service.cpp \ | ||||
| @@ -51,6 +53,7 @@ SOURCES += \ | |||||
| HEADERS += \ | HEADERS += \ | ||||
| src/ui/main_window.h \ | src/ui/main_window.h \ | ||||
| src/ui/alarm_configuration_dialog.h \ | src/ui/alarm_configuration_dialog.h \ | ||||
| src/ui/register_comment_dialog.h \ | |||||
| src/ui/plc_connection_dialog.h \ | src/ui/plc_connection_dialog.h \ | ||||
| src/ui/free_monitor_widget.h \ | src/ui/free_monitor_widget.h \ | ||||
| src/ui/runtime_monitor_widget.h \ | src/ui/runtime_monitor_widget.h \ | ||||
| @@ -75,6 +78,7 @@ HEADERS += \ | |||||
| src/services/offline_simulation_service.h \ | src/services/offline_simulation_service.h \ | ||||
| src/services/runtime_mode_service.h \ | src/services/runtime_mode_service.h \ | ||||
| src/services/register_monitor_service.h \ | src/services/register_monitor_service.h \ | ||||
| src/services/register_comment_service.h \ | |||||
| src/services/plc_communication_gateway.h \ | src/services/plc_communication_gateway.h \ | ||||
| src/infrastructure/plc_register_repository.h \ | src/infrastructure/plc_register_repository.h \ | ||||
| src/infrastructure/plc_communication_error_classifier.h \ | src/infrastructure/plc_communication_error_classifier.h \ | ||||
| @@ -86,6 +90,7 @@ HEADERS += \ | |||||
| FORMS += \ | FORMS += \ | ||||
| src/ui/main_window.ui \ | src/ui/main_window.ui \ | ||||
| src/ui/alarm_configuration_dialog.ui \ | src/ui/alarm_configuration_dialog.ui \ | ||||
| src/ui/register_comment_dialog.ui \ | |||||
| src/ui/plc_connection_dialog.ui \ | src/ui/plc_connection_dialog.ui \ | ||||
| src/ui/free_monitor_widget.ui \ | src/ui/free_monitor_widget.ui \ | ||||
| src/ui/runtime_monitor_widget.ui | src/ui/runtime_monitor_widget.ui | ||||
| @@ -22,6 +22,7 @@ | |||||
| #include "services/project_service.h" | #include "services/project_service.h" | ||||
| #include "services/runtime_mode_service.h" | #include "services/runtime_mode_service.h" | ||||
| #include "services/register_monitor_service.h" | #include "services/register_monitor_service.h" | ||||
| #include "services/register_comment_service.h" | |||||
| #include "ui/main_window.h" | #include "ui/main_window.h" | ||||
| int main(int argc, char *argv[]) | int main(int argc, char *argv[]) | ||||
| @@ -44,6 +45,7 @@ int main(int argc, char *argv[]) | |||||
| HmiRuntimeService hmi_runtime_service(active_register_repository); | HmiRuntimeService hmi_runtime_service(active_register_repository); | ||||
| AlarmEditorService alarm_editor_service(project_service); | AlarmEditorService alarm_editor_service(project_service); | ||||
| AlarmService alarm_service(project_service, active_register_repository); | AlarmService alarm_service(project_service, active_register_repository); | ||||
| RegisterCommentService register_comment_service(project_service); | |||||
| RegisterMonitorService register_monitor_service(active_register_repository); | RegisterMonitorService register_monitor_service(active_register_repository); | ||||
| OfflineSimulationService offline_simulation_service(virtual_register_repository); | OfflineSimulationService offline_simulation_service(virtual_register_repository); | ||||
| RuntimeModeService runtime_mode_service( | RuntimeModeService runtime_mode_service( | ||||
| @@ -62,6 +64,7 @@ int main(int argc, char *argv[]) | |||||
| alarm_editor_service, | alarm_editor_service, | ||||
| alarm_service, | alarm_service, | ||||
| hmi_navigation_service, | hmi_navigation_service, | ||||
| register_comment_service, | |||||
| register_monitor_service); | register_monitor_service); | ||||
| main_window.show(); | main_window.show(); | ||||
| @@ -17,9 +17,9 @@ constexpr qreal kSceneMargin = 28.0; | |||||
| constexpr qreal kRailInset = 40.0; | constexpr qreal kRailInset = 40.0; | ||||
| constexpr qreal kMinimumSceneWidth = 980.0; | constexpr qreal kMinimumSceneWidth = 980.0; | ||||
| constexpr qreal kCellWidth = 128.0; | constexpr qreal kCellWidth = 128.0; | ||||
| constexpr qreal kCellHeight = 86.0; | |||||
| constexpr qreal kCellHeight = 120.0; | |||||
| constexpr qreal kNodeTerminalX = 50.0; | constexpr qreal kNodeTerminalX = 50.0; | ||||
| constexpr qreal kRungHeaderHeight = 34.0; | |||||
| constexpr qreal kRungHeaderHeight = 52.0; | |||||
| constexpr qreal kRungGap = 18.0; | constexpr qreal kRungGap = 18.0; | ||||
| constexpr qreal kLadderLineWidth = 1.8; | constexpr qreal kLadderLineWidth = 1.8; | ||||
| constexpr int kMinimumLogicColumns = 7; | constexpr int kMinimumLogicColumns = 7; | ||||
| @@ -48,6 +48,29 @@ QString registerAddressText(const RegisterAddress &address) | |||||
| return QString::fromStdString(address.toString()); | return QString::fromStdString(address.toString()); | ||||
| } | } | ||||
| QString timerAddressText(const TimerAddress &address) | |||||
| { | |||||
| return QString::fromStdString(address.toString()); | |||||
| } | |||||
| void drawRegisterComment( | |||||
| QPainter *painter, const QString &comment, qreal top = 22.0) | |||||
| { | |||||
| if (comment.isEmpty()) | |||||
| { | |||||
| return; | |||||
| } | |||||
| QFont font = painter->font(); | |||||
| font.setPointSizeF(8.0); | |||||
| painter->setFont(font); | |||||
| const QString visible_comment = painter->fontMetrics().elidedText( | |||||
| comment, Qt::ElideRight, static_cast<int>(kCellWidth - 8.0)); | |||||
| painter->drawText( | |||||
| QRectF(-kCellWidth / 2.0, top, kCellWidth, 18), | |||||
| Qt::AlignCenter, | |||||
| visible_comment); | |||||
| } | |||||
| QString comparisonText(ComparisonOperator comparison) | QString comparisonText(ComparisonOperator comparison) | ||||
| { | { | ||||
| switch (comparison) | switch (comparison) | ||||
| @@ -76,18 +99,37 @@ QString nodeToolTip(const LogicNodeConfig &config) | |||||
| : LogicEditorWidget::tr("常闭")) | : LogicEditorWidget::tr("常闭")) | ||||
| .arg(registerAddressText(value.address)); | .arg(registerAddressText(value.address)); | ||||
| } | } | ||||
| else if constexpr (std::is_same_v<Config, EdgeContactNodeConfig>) | |||||
| { | |||||
| return LogicEditorWidget::tr("%1沿触点:%2") | |||||
| .arg(value.mode == EdgeMode::Rising | |||||
| ? LogicEditorWidget::tr("上升") | |||||
| : LogicEditorWidget::tr("下降")) | |||||
| .arg(registerAddressText(value.address)); | |||||
| } | |||||
| else if constexpr (std::is_same_v<Config, TimerContactNodeConfig>) | |||||
| { | |||||
| return LogicEditorWidget::tr("T 触点:%1") | |||||
| .arg(timerAddressText(value.address)); | |||||
| } | |||||
| else if constexpr (std::is_same_v<Config, CoilNodeConfig>) | else if constexpr (std::is_same_v<Config, CoilNodeConfig>) | ||||
| { | { | ||||
| return LogicEditorWidget::tr("输出线圈:%1") | return LogicEditorWidget::tr("输出线圈:%1") | ||||
| .arg(registerAddressText(value.address)); | .arg(registerAddressText(value.address)); | ||||
| } | } | ||||
| else | |||||
| else if constexpr (std::is_same_v<Config, CompareNodeConfig>) | |||||
| { | { | ||||
| return LogicEditorWidget::tr("比较条件:%1 %2 %3") | return LogicEditorWidget::tr("比较条件:%1 %2 %3") | ||||
| .arg(registerAddressText(value.address)) | .arg(registerAddressText(value.address)) | ||||
| .arg(comparisonText(value.comparison)) | .arg(comparisonText(value.comparison)) | ||||
| .arg(value.value); | .arg(value.value); | ||||
| } | } | ||||
| else | |||||
| { | |||||
| return LogicEditorWidget::tr("TON:%1,预设 %2 ms") | |||||
| .arg(timerAddressText(value.address)) | |||||
| .arg(value.presetMs); | |||||
| } | |||||
| }, | }, | ||||
| config); | config); | ||||
| } | } | ||||
| @@ -187,11 +229,13 @@ public: | |||||
| const LogicNode &node, | const LogicNode &node, | ||||
| const std::string &rung_id, | const std::string &rung_id, | ||||
| const QPointF ¢er, | const QPointF ¢er, | ||||
| const QString ®ister_comment, | |||||
| bool active, | bool active, | ||||
| bool faulted) | bool faulted) | ||||
| : node_id_(node.id), | : node_id_(node.id), | ||||
| rung_id_(rung_id), | rung_id_(rung_id), | ||||
| config_(node.config), | config_(node.config), | ||||
| register_comment_(register_comment), | |||||
| configured_(node.configured), | configured_(node.configured), | ||||
| active_(active), | active_(active), | ||||
| faulted_(faulted) | faulted_(faulted) | ||||
| @@ -199,8 +243,14 @@ public: | |||||
| setPos(center); | setPos(center); | ||||
| setFlag(ItemIsSelectable, true); | setFlag(ItemIsSelectable, true); | ||||
| setZValue(3.0); | setZValue(3.0); | ||||
| setToolTip(configured_ ? nodeToolTip(config_) | |||||
| : LogicEditorWidget::tr("待配置:请在属性区设置地址和参数")); | |||||
| QString tool_tip = configured_ | |||||
| ? nodeToolTip(config_) | |||||
| : LogicEditorWidget::tr("待配置:请在属性区设置地址和参数"); | |||||
| if (!register_comment_.isEmpty()) | |||||
| { | |||||
| tool_tip += LogicEditorWidget::tr("\n注释:%1").arg(register_comment_); | |||||
| } | |||||
| setToolTip(tool_tip); | |||||
| } | } | ||||
| QRectF boundingRect() const override | QRectF boundingRect() const override | ||||
| @@ -241,6 +291,41 @@ public: | |||||
| QRectF(-kCellWidth / 2.0, -40, kCellWidth, 18), | QRectF(-kCellWidth / 2.0, -40, kCellWidth, 18), | ||||
| Qt::AlignCenter, | Qt::AlignCenter, | ||||
| configured_ ? registerAddressText(contact->address) : tr("< M 地址 >")); | configured_ ? registerAddressText(contact->address) : tr("< M 地址 >")); | ||||
| drawRegisterComment(painter, register_comment_); | |||||
| } | |||||
| else if (const auto *edge = std::get_if<EdgeContactNodeConfig>(&config_)) | |||||
| { | |||||
| painter->fillRect(QRectF(-25, -22, 50, 44), Qt::white); | |||||
| painter->drawLine(QPointF(-kNodeTerminalX, 0), QPointF(-18, 0)); | |||||
| painter->drawLine(QPointF(18, 0), QPointF(kNodeTerminalX, 0)); | |||||
| painter->drawLine(QPointF(-18, -15), QPointF(-18, 15)); | |||||
| painter->drawLine(QPointF(18, -15), QPointF(18, 15)); | |||||
| painter->drawText( | |||||
| QRectF(-14, -12, 28, 24), | |||||
| Qt::AlignCenter, | |||||
| edge->mode == EdgeMode::Rising ? QStringLiteral("P") : QStringLiteral("N")); | |||||
| painter->drawText( | |||||
| QRectF(-kCellWidth / 2.0, -40, kCellWidth, 18), | |||||
| Qt::AlignCenter, | |||||
| configured_ ? registerAddressText(edge->address) : tr("< M 地址 >")); | |||||
| drawRegisterComment(painter, register_comment_); | |||||
| } | |||||
| else if (const auto *timer_contact = | |||||
| std::get_if<TimerContactNodeConfig>(&config_)) | |||||
| { | |||||
| painter->fillRect(QRectF(-25, -22, 50, 44), Qt::white); | |||||
| painter->drawLine(QPointF(-kNodeTerminalX, 0), QPointF(-18, 0)); | |||||
| painter->drawLine(QPointF(18, 0), QPointF(kNodeTerminalX, 0)); | |||||
| painter->drawLine(QPointF(-18, -15), QPointF(-18, 15)); | |||||
| painter->drawLine(QPointF(18, -15), QPointF(18, 15)); | |||||
| if (timer_contact->mode == ContactMode::NormallyClosed) | |||||
| { | |||||
| painter->drawLine(QPointF(-23, 18), QPointF(23, -18)); | |||||
| } | |||||
| painter->drawText( | |||||
| QRectF(-kCellWidth / 2.0, -40, kCellWidth, 18), | |||||
| Qt::AlignCenter, | |||||
| configured_ ? timerAddressText(timer_contact->address) : tr("< T 地址 >")); | |||||
| } | } | ||||
| else if (const auto *coil = std::get_if<CoilNodeConfig>(&config_)) | else if (const auto *coil = std::get_if<CoilNodeConfig>(&config_)) | ||||
| { | { | ||||
| @@ -266,6 +351,7 @@ public: | |||||
| QRectF(-kCellWidth / 2.0, -40, kCellWidth, 18), | QRectF(-kCellWidth / 2.0, -40, kCellWidth, 18), | ||||
| Qt::AlignCenter, | Qt::AlignCenter, | ||||
| configured_ ? registerAddressText(coil->address) : tr("< M 地址 >")); | configured_ ? registerAddressText(coil->address) : tr("< M 地址 >")); | ||||
| drawRegisterComment(painter, register_comment_); | |||||
| } | } | ||||
| else if (const auto *comparison = std::get_if<CompareNodeConfig>(&config_)) | else if (const auto *comparison = std::get_if<CompareNodeConfig>(&config_)) | ||||
| { | { | ||||
| @@ -285,6 +371,25 @@ public: | |||||
| QRectF(-kCellWidth / 2.0, 22, kCellWidth, 18), | QRectF(-kCellWidth / 2.0, 22, kCellWidth, 18), | ||||
| Qt::AlignCenter, | Qt::AlignCenter, | ||||
| configured_ ? QString::number(comparison->value) : tr("< 常量 >")); | configured_ ? QString::number(comparison->value) : tr("< 常量 >")); | ||||
| drawRegisterComment(painter, register_comment_, 40.0); | |||||
| } | |||||
| else if (const auto *ton = std::get_if<TonNodeConfig>(&config_)) | |||||
| { | |||||
| const QRectF box(-47, -18, 94, 36); | |||||
| painter->fillRect(box.adjusted(-1, -1, 1, 1), Qt::white); | |||||
| painter->drawLine(QPointF(-kNodeTerminalX, 0), QPointF(-47, 0)); | |||||
| painter->drawLine(QPointF(47, 0), QPointF(kNodeTerminalX, 0)); | |||||
| painter->drawRect(box); | |||||
| painter->drawText(box, Qt::AlignCenter, QStringLiteral("TON")); | |||||
| painter->drawText( | |||||
| QRectF(-kCellWidth / 2.0, -40, kCellWidth, 18), | |||||
| Qt::AlignCenter, | |||||
| configured_ ? timerAddressText(ton->address) : tr("< T 地址 >")); | |||||
| painter->drawText( | |||||
| QRectF(-kCellWidth / 2.0, 22, kCellWidth, 18), | |||||
| Qt::AlignCenter, | |||||
| configured_ ? QStringLiteral("PT %1 ms").arg(ton->presetMs) | |||||
| : tr("< 预设时间 >")); | |||||
| } | } | ||||
| } | } | ||||
| @@ -295,6 +400,7 @@ private: | |||||
| std::string node_id_; | std::string node_id_; | ||||
| std::string rung_id_; | std::string rung_id_; | ||||
| LogicNodeConfig config_; | LogicNodeConfig config_; | ||||
| QString register_comment_; | |||||
| bool configured_ = true; | bool configured_ = true; | ||||
| bool active_ = false; | bool active_ = false; | ||||
| bool faulted_ = false; | bool faulted_ = false; | ||||
| @@ -312,6 +418,7 @@ public: | |||||
| qreal cursor_left) | qreal cursor_left) | ||||
| : rung_id_(rung.id), | : rung_id_(rung.id), | ||||
| name_(QString::fromStdString(rung.name)), | name_(QString::fromStdString(rung.name)), | ||||
| comment_(QString::fromStdString(rung.comment)), | |||||
| number_(number), | number_(number), | ||||
| height_(height), | height_(height), | ||||
| width_(width), | width_(width), | ||||
| @@ -321,6 +428,10 @@ public: | |||||
| setPos(0, top); | setPos(0, top); | ||||
| setFlag(ItemIsSelectable, true); | setFlag(ItemIsSelectable, true); | ||||
| setZValue(-2.0); | setZValue(-2.0); | ||||
| if (!comment_.isEmpty()) | |||||
| { | |||||
| setToolTip(comment_); | |||||
| } | |||||
| } | } | ||||
| QRectF boundingRect() const override { return {kSceneMargin, 0, width_, height_}; } | QRectF boundingRect() const override { return {kSceneMargin, 0, width_, height_}; } | ||||
| @@ -351,6 +462,16 @@ public: | |||||
| QRectF(kSceneMargin + 8, 5, 320, 22), | QRectF(kSceneMargin + 8, 5, 320, 22), | ||||
| Qt::AlignLeft | Qt::AlignVCenter, | Qt::AlignLeft | Qt::AlignVCenter, | ||||
| title); | title); | ||||
| if (!comment_.isEmpty()) | |||||
| { | |||||
| painter->setPen(QColor(QStringLiteral("#7b8790"))); | |||||
| const QString visible_comment = painter->fontMetrics().elidedText( | |||||
| comment_, Qt::ElideRight, static_cast<int>(width_ - 16.0)); | |||||
| painter->drawText( | |||||
| QRectF(kSceneMargin + 8, 24, width_ - 16, 22), | |||||
| Qt::AlignLeft | Qt::AlignVCenter, | |||||
| visible_comment); | |||||
| } | |||||
| painter->setPen(QPen(QColor(QStringLiteral("#d4dce1")), 1)); | painter->setPen(QPen(QColor(QStringLiteral("#d4dce1")), 1)); | ||||
| painter->drawLine( | painter->drawLine( | ||||
| QPointF(kSceneMargin + 8, height_ - 1), | QPointF(kSceneMargin + 8, height_ - 1), | ||||
| @@ -362,6 +483,7 @@ public: | |||||
| private: | private: | ||||
| std::string rung_id_; | std::string rung_id_; | ||||
| QString name_; | QString name_; | ||||
| QString comment_; | |||||
| int number_ = 0; | int number_ = 0; | ||||
| qreal height_ = 0.0; | qreal height_ = 0.0; | ||||
| qreal width_ = 0.0; | qreal width_ = 0.0; | ||||
| @@ -373,6 +495,7 @@ namespace { | |||||
| RenderResult renderExpression( | RenderResult renderExpression( | ||||
| QGraphicsScene &scene, | QGraphicsScene &scene, | ||||
| const LogicEditorService &editor_service, | |||||
| const ConditionExpression &expression, | const ConditionExpression &expression, | ||||
| const std::string &rung_id, | const std::string &rung_id, | ||||
| const QPointF &top_left, | const QPointF &top_left, | ||||
| @@ -399,6 +522,15 @@ RenderResult renderExpression( | |||||
| *expression.node, | *expression.node, | ||||
| rung_id, | rung_id, | ||||
| center, | center, | ||||
| [&editor_service, &expression] | |||||
| { | |||||
| const std::optional<RegisterAddress> address = | |||||
| registerAddressForLogicNode(expression.node->config); | |||||
| return address.has_value() | |||||
| ? QString::fromStdString( | |||||
| editor_service.registerCommentFor(*address)) | |||||
| : QString{}; | |||||
| }(), | |||||
| node_active, | node_active, | ||||
| expression.node->id == fault_node_id)); | expression.node->id == fault_node_id)); | ||||
| return { | return { | ||||
| @@ -416,6 +548,7 @@ RenderResult renderExpression( | |||||
| const ExpressionMetrics child_metrics = measureExpression(expression.children[index]); | const ExpressionMetrics child_metrics = measureExpression(expression.children[index]); | ||||
| const RenderResult current = renderExpression( | const RenderResult current = renderExpression( | ||||
| scene, | scene, | ||||
| editor_service, | |||||
| expression.children[index], | expression.children[index], | ||||
| rung_id, | rung_id, | ||||
| QPointF(x, top_left.y()), | QPointF(x, top_left.y()), | ||||
| @@ -445,6 +578,7 @@ RenderResult renderExpression( | |||||
| const ExpressionMetrics child_metrics = measureExpression(child); | const ExpressionMetrics child_metrics = measureExpression(child); | ||||
| branches.push_back(renderExpression( | branches.push_back(renderExpression( | ||||
| scene, | scene, | ||||
| editor_service, | |||||
| child, | child, | ||||
| rung_id, | rung_id, | ||||
| QPointF(top_left.x(), y), | QPointF(top_left.x(), y), | ||||
| @@ -588,6 +722,7 @@ void LogicEditorWidget::reloadLogic() | |||||
| { | { | ||||
| const RenderResult rendered = renderExpression( | const RenderResult rendered = renderExpression( | ||||
| *scene_, | *scene_, | ||||
| editor_service_, | |||||
| *rung.condition, | *rung.condition, | ||||
| rung.id, | rung.id, | ||||
| QPointF(left_rail_x, grid_top), | QPointF(left_rail_x, grid_top), | ||||
| @@ -620,7 +755,24 @@ void LogicEditorWidget::reloadLogic() | |||||
| *rung.output, | *rung.output, | ||||
| rung.id, | rung.id, | ||||
| QPointF(output_left + kCellWidth / 2.0, main_y), | QPointF(output_left + kCellWidth / 2.0, main_y), | ||||
| rung_active, | |||||
| [&rung, this] | |||||
| { | |||||
| const std::optional<RegisterAddress> address = | |||||
| registerAddressForLogicNode(rung.output->config); | |||||
| return address.has_value() | |||||
| ? QString::fromStdString( | |||||
| editor_service_.registerCommentFor(*address)) | |||||
| : QString{}; | |||||
| }(), | |||||
| [&rung, this, rung_active] | |||||
| { | |||||
| return std::holds_alternative<TonNodeConfig>(rung.output->config) | |||||
| ? traceValue( | |||||
| trace_, | |||||
| &LogicTraceSnapshot::nodeValues, | |||||
| rung.output->id) | |||||
| : rung_active; | |||||
| }(), | |||||
| rung.output->id == fault_node_id_)); | rung.output->id == fault_node_id_)); | ||||
| } | } | ||||
| else | else | ||||
| @@ -2,6 +2,7 @@ | |||||
| #include "hmi_editor_widget.h" | #include "hmi_editor_widget.h" | ||||
| #include "alarm_configuration_dialog.h" | #include "alarm_configuration_dialog.h" | ||||
| #include "register_comment_dialog.h" | |||||
| #include "logic_editor_widget.h" | #include "logic_editor_widget.h" | ||||
| #include "plc_connection_dialog.h" | #include "plc_connection_dialog.h" | ||||
| #include "runtime_monitor_widget.h" | #include "runtime_monitor_widget.h" | ||||
| @@ -15,6 +16,7 @@ | |||||
| #include "services/project_service.h" | #include "services/project_service.h" | ||||
| #include "services/runtime_mode_service.h" | #include "services/runtime_mode_service.h" | ||||
| #include "services/register_monitor_service.h" | #include "services/register_monitor_service.h" | ||||
| #include "services/register_comment_service.h" | |||||
| #include "ui_main_window.h" | #include "ui_main_window.h" | ||||
| #include <QActionGroup> | #include <QActionGroup> | ||||
| @@ -200,6 +202,7 @@ MainWindow::MainWindow( | |||||
| HmiRuntimeService &hmi_runtime_service, | HmiRuntimeService &hmi_runtime_service, | ||||
| AlarmEditorService &alarm_editor_service, | AlarmEditorService &alarm_editor_service, | ||||
| AlarmService &alarm_service, | AlarmService &alarm_service, | ||||
| RegisterCommentService ®ister_comment_service, | |||||
| RegisterMonitorService ®ister_monitor_service, | RegisterMonitorService ®ister_monitor_service, | ||||
| QWidget *parent) | QWidget *parent) | ||||
| : QMainWindow(parent), | : QMainWindow(parent), | ||||
| @@ -211,6 +214,7 @@ MainWindow::MainWindow( | |||||
| hmi_runtime_service_(hmi_runtime_service), | hmi_runtime_service_(hmi_runtime_service), | ||||
| alarm_editor_service_(alarm_editor_service), | alarm_editor_service_(alarm_editor_service), | ||||
| alarm_service_(alarm_service), | alarm_service_(alarm_service), | ||||
| register_comment_service_(register_comment_service), | |||||
| register_monitor_service_(register_monitor_service), | register_monitor_service_(register_monitor_service), | ||||
| owned_hmi_navigation_service_( | owned_hmi_navigation_service_( | ||||
| std::make_unique<HmiNavigationService>(project_service)), | std::make_unique<HmiNavigationService>(project_service)), | ||||
| @@ -228,6 +232,7 @@ MainWindow::MainWindow( | |||||
| AlarmEditorService &alarm_editor_service, | AlarmEditorService &alarm_editor_service, | ||||
| AlarmService &alarm_service, | AlarmService &alarm_service, | ||||
| HmiNavigationService &hmi_navigation_service, | HmiNavigationService &hmi_navigation_service, | ||||
| RegisterCommentService ®ister_comment_service, | |||||
| RegisterMonitorService ®ister_monitor_service, | RegisterMonitorService ®ister_monitor_service, | ||||
| QWidget *parent) | QWidget *parent) | ||||
| : QMainWindow(parent), | : QMainWindow(parent), | ||||
| @@ -239,6 +244,7 @@ MainWindow::MainWindow( | |||||
| hmi_runtime_service_(hmi_runtime_service), | hmi_runtime_service_(hmi_runtime_service), | ||||
| alarm_editor_service_(alarm_editor_service), | alarm_editor_service_(alarm_editor_service), | ||||
| alarm_service_(alarm_service), | alarm_service_(alarm_service), | ||||
| register_comment_service_(register_comment_service), | |||||
| register_monitor_service_(register_monitor_service), | register_monitor_service_(register_monitor_service), | ||||
| hmi_navigation_service_(&hmi_navigation_service) | hmi_navigation_service_(&hmi_navigation_service) | ||||
| { | { | ||||
| @@ -253,6 +259,7 @@ void MainWindow::initializeUi() | |||||
| configurePropertyEditor(); | configurePropertyEditor(); | ||||
| configurePlcConnection(); | configurePlcConnection(); | ||||
| configureAlarms(); | configureAlarms(); | ||||
| configureRegisterComments(); | |||||
| configureHmiEditor(); | configureHmiEditor(); | ||||
| configureLogicEditor(); | configureLogicEditor(); | ||||
| configureRuntimeMonitor(); | configureRuntimeMonitor(); | ||||
| @@ -332,6 +339,9 @@ void MainWindow::configureActions() | |||||
| QMenu *parallel_menu = new QMenu(this); | QMenu *parallel_menu = new QMenu(this); | ||||
| QAction *parallel_open = parallel_menu->addAction(tr("并联常开触点")); | QAction *parallel_open = parallel_menu->addAction(tr("并联常开触点")); | ||||
| QAction *parallel_closed = parallel_menu->addAction(tr("并联常闭触点")); | QAction *parallel_closed = parallel_menu->addAction(tr("并联常闭触点")); | ||||
| QAction *parallel_rising = parallel_menu->addAction(tr("并联上升沿触点")); | |||||
| QAction *parallel_falling = parallel_menu->addAction(tr("并联下降沿触点")); | |||||
| QAction *parallel_timer = parallel_menu->addAction(tr("并联 T 触点")); | |||||
| QAction *parallel_compare = parallel_menu->addAction(tr("并联比较条件")); | QAction *parallel_compare = parallel_menu->addAction(tr("并联比较条件")); | ||||
| connect(parallel_open, &QAction::triggered, | connect(parallel_open, &QAction::triggered, | ||||
| this, | this, | ||||
| @@ -349,6 +359,27 @@ void MainWindow::configureActions() | |||||
| RegisterAddress{RegisterArea::M, 0}, | RegisterAddress{RegisterArea::M, 0}, | ||||
| ContactMode::NormallyClosed}); | ContactMode::NormallyClosed}); | ||||
| }); | }); | ||||
| connect(parallel_rising, &QAction::triggered, | |||||
| this, | |||||
| [this] | |||||
| { | |||||
| addLogicParallelBranch(EdgeContactNodeConfig{ | |||||
| RegisterAddress{RegisterArea::M, 0}, EdgeMode::Rising}); | |||||
| }); | |||||
| connect(parallel_falling, &QAction::triggered, | |||||
| this, | |||||
| [this] | |||||
| { | |||||
| addLogicParallelBranch(EdgeContactNodeConfig{ | |||||
| RegisterAddress{RegisterArea::M, 0}, EdgeMode::Falling}); | |||||
| }); | |||||
| connect(parallel_timer, &QAction::triggered, | |||||
| this, | |||||
| [this] | |||||
| { | |||||
| addLogicParallelBranch(TimerContactNodeConfig{ | |||||
| TimerAddress{0}, ContactMode::NormallyOpen}); | |||||
| }); | |||||
| connect(parallel_compare, &QAction::triggered, | connect(parallel_compare, &QAction::triggered, | ||||
| this, | this, | ||||
| [this] | [this] | ||||
| @@ -380,6 +411,27 @@ void MainWindow::configureActions() | |||||
| RegisterAddress{RegisterArea::M, 0}, | RegisterAddress{RegisterArea::M, 0}, | ||||
| ContactMode::NormallyClosed}); | ContactMode::NormallyClosed}); | ||||
| }); | }); | ||||
| connect(ui_->addRisingEdgeAction, &QAction::triggered, | |||||
| this, | |||||
| [this] | |||||
| { | |||||
| addLogicCondition(EdgeContactNodeConfig{ | |||||
| RegisterAddress{RegisterArea::M, 0}, EdgeMode::Rising}); | |||||
| }); | |||||
| connect(ui_->addFallingEdgeAction, &QAction::triggered, | |||||
| this, | |||||
| [this] | |||||
| { | |||||
| addLogicCondition(EdgeContactNodeConfig{ | |||||
| RegisterAddress{RegisterArea::M, 0}, EdgeMode::Falling}); | |||||
| }); | |||||
| connect(ui_->addTimerContactAction, &QAction::triggered, | |||||
| this, | |||||
| [this] | |||||
| { | |||||
| addLogicCondition(TimerContactNodeConfig{ | |||||
| TimerAddress{0}, ContactMode::NormallyOpen}); | |||||
| }); | |||||
| connect(ui_->addNormalCoilAction, &QAction::triggered, | connect(ui_->addNormalCoilAction, &QAction::triggered, | ||||
| this, | this, | ||||
| [this] | [this] | ||||
| @@ -404,6 +456,12 @@ void MainWindow::configureActions() | |||||
| RegisterAddress{RegisterArea::M, 0}, | RegisterAddress{RegisterArea::M, 0}, | ||||
| CoilMode::Reset}); | CoilMode::Reset}); | ||||
| }); | }); | ||||
| connect(ui_->addTonAction, &QAction::triggered, | |||||
| this, | |||||
| [this] | |||||
| { | |||||
| setLogicOutput(TonNodeConfig{TimerAddress{0}, 1000}); | |||||
| }); | |||||
| connect(ui_->addCompareAction, &QAction::triggered, | connect(ui_->addCompareAction, &QAction::triggered, | ||||
| this, | this, | ||||
| [this] | [this] | ||||
| @@ -415,6 +473,8 @@ void MainWindow::configureActions() | |||||
| }); | }); | ||||
| connect(ui_->deleteLogicAction, &QAction::triggered, | connect(ui_->deleteLogicAction, &QAction::triggered, | ||||
| this, &MainWindow::deleteSelectedLogicObject); | this, &MainWindow::deleteSelectedLogicObject); | ||||
| connect(ui_->editRungCommentAction, &QAction::triggered, | |||||
| this, &MainWindow::editSelectedRungComment); | |||||
| connect(ui_->editorTabWidget, &QTabWidget::currentChanged, | connect(ui_->editorTabWidget, &QTabWidget::currentChanged, | ||||
| this, | this, | ||||
| @@ -481,10 +541,15 @@ void MainWindow::configureAppearance() | |||||
| ui_->parallelInsertAction->setIcon(style()->standardIcon(QStyle::SP_ArrowDown)); | ui_->parallelInsertAction->setIcon(style()->standardIcon(QStyle::SP_ArrowDown)); | ||||
| ui_->addNormallyOpenAction->setIcon(style()->standardIcon(QStyle::SP_ArrowRight)); | ui_->addNormallyOpenAction->setIcon(style()->standardIcon(QStyle::SP_ArrowRight)); | ||||
| ui_->addNormallyClosedAction->setIcon(style()->standardIcon(QStyle::SP_ArrowRight)); | ui_->addNormallyClosedAction->setIcon(style()->standardIcon(QStyle::SP_ArrowRight)); | ||||
| ui_->addRisingEdgeAction->setIcon(style()->standardIcon(QStyle::SP_ArrowRight)); | |||||
| ui_->addFallingEdgeAction->setIcon(style()->standardIcon(QStyle::SP_ArrowRight)); | |||||
| ui_->addTimerContactAction->setIcon(style()->standardIcon(QStyle::SP_ArrowRight)); | |||||
| ui_->addNormalCoilAction->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton)); | ui_->addNormalCoilAction->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton)); | ||||
| ui_->addSetCoilAction->setIcon(style()->standardIcon(QStyle::SP_DialogYesButton)); | ui_->addSetCoilAction->setIcon(style()->standardIcon(QStyle::SP_DialogYesButton)); | ||||
| ui_->addResetCoilAction->setIcon(style()->standardIcon(QStyle::SP_DialogNoButton)); | ui_->addResetCoilAction->setIcon(style()->standardIcon(QStyle::SP_DialogNoButton)); | ||||
| ui_->addTonAction->setIcon(style()->standardIcon(QStyle::SP_BrowserReload)); | |||||
| ui_->addCompareAction->setIcon(style()->standardIcon(QStyle::SP_FileDialogInfoView)); | ui_->addCompareAction->setIcon(style()->standardIcon(QStyle::SP_FileDialogInfoView)); | ||||
| ui_->editRungCommentAction->setIcon(style()->standardIcon(QStyle::SP_FileDialogDetailedView)); | |||||
| ui_->deleteLogicAction->setIcon(style()->standardIcon(QStyle::SP_TrashIcon)); | ui_->deleteLogicAction->setIcon(style()->standardIcon(QStyle::SP_TrashIcon)); | ||||
| ui_->editorTabWidget->setTabIcon(0, style()->standardIcon(QStyle::SP_DesktopIcon)); | ui_->editorTabWidget->setTabIcon(0, style()->standardIcon(QStyle::SP_DesktopIcon)); | ||||
| ui_->editorTabWidget->setTabIcon(1, style()->standardIcon(QStyle::SP_FileDialogListView)); | ui_->editorTabWidget->setTabIcon(1, style()->standardIcon(QStyle::SP_FileDialogListView)); | ||||
| @@ -755,7 +820,22 @@ void MainWindow::configureAlarms() | |||||
| AlarmConfigurationDialog dialog(alarm_editor_service_, this); | AlarmConfigurationDialog dialog(alarm_editor_service_, this); | ||||
| dialog.exec(); | dialog.exec(); | ||||
| refreshProjectUi(); | refreshProjectUi(); | ||||
| }); | |||||
| }); | |||||
| } | |||||
| void MainWindow::configureRegisterComments() | |||||
| { | |||||
| connect( | |||||
| ui_->configureRegisterCommentsAction, | |||||
| &QAction::triggered, | |||||
| this, | |||||
| [this] | |||||
| { | |||||
| RegisterCommentDialog dialog(register_comment_service_, this); | |||||
| dialog.exec(); | |||||
| logic_editor_widget_->reloadLogic(); | |||||
| refreshProjectUi(); | |||||
| }); | |||||
| } | } | ||||
| void MainWindow::refreshProjectUi() | void MainWindow::refreshProjectUi() | ||||
| @@ -1300,6 +1380,7 @@ void MainWindow::showLogicNodeProperties(const std::string &node_id) | |||||
| static_cast<QWidget *>(ui_->logicModeComboBox), | static_cast<QWidget *>(ui_->logicModeComboBox), | ||||
| static_cast<QWidget *>(ui_->logicComparisonComboBox), | static_cast<QWidget *>(ui_->logicComparisonComboBox), | ||||
| static_cast<QWidget *>(ui_->logicValueSpinBox), | static_cast<QWidget *>(ui_->logicValueSpinBox), | ||||
| static_cast<QWidget *>(ui_->logicPresetSpinBox), | |||||
| static_cast<QWidget *>(ui_->applyLogicPropertiesButton)}) | static_cast<QWidget *>(ui_->applyLogicPropertiesButton)}) | ||||
| { | { | ||||
| widget->setEnabled(has_node); | widget->setEnabled(has_node); | ||||
| @@ -1312,11 +1393,15 @@ void MainWindow::showLogicNodeProperties(const std::string &node_id) | |||||
| ui_->logicNodeTypeLabel->clear(); | ui_->logicNodeTypeLabel->clear(); | ||||
| ui_->logicAddressAreaLabel->clear(); | ui_->logicAddressAreaLabel->clear(); | ||||
| ui_->logicModeComboBox->clear(); | ui_->logicModeComboBox->clear(); | ||||
| ui_->logicPresetSpinBox->setValue(TonNodeConfig::kMinimumPresetMs); | |||||
| return; | return; | ||||
| } | } | ||||
| ui_->logicNodeIdEdit->setText(fromUtf8(node->id)); | ui_->logicNodeIdEdit->setText(fromUtf8(node->id)); | ||||
| ui_->logicModeComboBox->clear(); | ui_->logicModeComboBox->clear(); | ||||
| ui_->logicComparisonComboBox->setEnabled(false); | |||||
| ui_->logicValueSpinBox->setEnabled(false); | |||||
| ui_->logicPresetSpinBox->setEnabled(false); | |||||
| if (const auto *contact = std::get_if<ContactNodeConfig>(&node->config)) | if (const auto *contact = std::get_if<ContactNodeConfig>(&node->config)) | ||||
| { | { | ||||
| ui_->logicNodeTypeLabel->setText(tr("触点")); | ui_->logicNodeTypeLabel->setText(tr("触点")); | ||||
| @@ -1327,10 +1412,36 @@ void MainWindow::showLogicNodeProperties(const std::string &node_id) | |||||
| ui_->logicModeComboBox->addItem( | ui_->logicModeComboBox->addItem( | ||||
| tr("常闭"), static_cast<int>(ContactMode::NormallyClosed)); | tr("常闭"), static_cast<int>(ContactMode::NormallyClosed)); | ||||
| ui_->logicModeComboBox->setCurrentIndex( | ui_->logicModeComboBox->setCurrentIndex( | ||||
| ui_->logicModeComboBox->findData(static_cast<int>(contact->mode))); | |||||
| ui_->logicModeComboBox->findData(static_cast<int>(contact->mode))); | |||||
| ui_->logicModeComboBox->setEnabled(true); | |||||
| } | |||||
| else if (const auto *edge = std::get_if<EdgeContactNodeConfig>(&node->config)) | |||||
| { | |||||
| ui_->logicNodeTypeLabel->setText(tr("边沿触点")); | |||||
| ui_->logicAddressAreaLabel->setText(QStringLiteral("M")); | |||||
| ui_->logicAddressSpinBox->setValue(edge->address.index()); | |||||
| ui_->logicModeComboBox->addItem( | |||||
| tr("上升沿"), static_cast<int>(EdgeMode::Rising)); | |||||
| ui_->logicModeComboBox->addItem( | |||||
| tr("下降沿"), static_cast<int>(EdgeMode::Falling)); | |||||
| ui_->logicModeComboBox->setCurrentIndex( | |||||
| ui_->logicModeComboBox->findData(static_cast<int>(edge->mode))); | |||||
| ui_->logicModeComboBox->setEnabled(true); | |||||
| } | |||||
| else if (const auto *timer_contact = | |||||
| std::get_if<TimerContactNodeConfig>(&node->config)) | |||||
| { | |||||
| ui_->logicNodeTypeLabel->setText(tr("T 触点")); | |||||
| ui_->logicAddressAreaLabel->setText(QStringLiteral("T")); | |||||
| ui_->logicAddressSpinBox->setValue(timer_contact->address.index()); | |||||
| ui_->logicModeComboBox->addItem( | |||||
| tr("常开"), static_cast<int>(ContactMode::NormallyOpen)); | |||||
| ui_->logicModeComboBox->addItem( | |||||
| tr("常闭"), static_cast<int>(ContactMode::NormallyClosed)); | |||||
| ui_->logicModeComboBox->setCurrentIndex( | |||||
| ui_->logicModeComboBox->findData( | |||||
| static_cast<int>(timer_contact->mode))); | |||||
| ui_->logicModeComboBox->setEnabled(true); | ui_->logicModeComboBox->setEnabled(true); | ||||
| ui_->logicComparisonComboBox->setEnabled(false); | |||||
| ui_->logicValueSpinBox->setEnabled(false); | |||||
| } | } | ||||
| else if (const auto *coil = std::get_if<CoilNodeConfig>(&node->config)) | else if (const auto *coil = std::get_if<CoilNodeConfig>(&node->config)) | ||||
| { | { | ||||
| @@ -1344,25 +1455,33 @@ void MainWindow::showLogicNodeProperties(const std::string &node_id) | |||||
| ui_->logicModeComboBox->addItem( | ui_->logicModeComboBox->addItem( | ||||
| tr("复位"), static_cast<int>(CoilMode::Reset)); | tr("复位"), static_cast<int>(CoilMode::Reset)); | ||||
| ui_->logicModeComboBox->setCurrentIndex( | ui_->logicModeComboBox->setCurrentIndex( | ||||
| ui_->logicModeComboBox->findData(static_cast<int>(coil->mode))); | |||||
| ui_->logicModeComboBox->findData(static_cast<int>(coil->mode))); | |||||
| ui_->logicModeComboBox->setEnabled(true); | ui_->logicModeComboBox->setEnabled(true); | ||||
| ui_->logicComparisonComboBox->setEnabled(false); | |||||
| ui_->logicValueSpinBox->setEnabled(false); | |||||
| } | } | ||||
| else | |||||
| else if (const auto *compare = std::get_if<CompareNodeConfig>(&node->config)) | |||||
| { | { | ||||
| const auto &compare = std::get<CompareNodeConfig>(node->config); | |||||
| ui_->logicNodeTypeLabel->setText(tr("D 值比较")); | ui_->logicNodeTypeLabel->setText(tr("D 值比较")); | ||||
| ui_->logicAddressAreaLabel->setText(QStringLiteral("D")); | ui_->logicAddressAreaLabel->setText(QStringLiteral("D")); | ||||
| ui_->logicAddressSpinBox->setValue(compare.address.index()); | |||||
| ui_->logicAddressSpinBox->setValue(compare->address.index()); | |||||
| ui_->logicModeComboBox->addItem(tr("不适用")); | ui_->logicModeComboBox->addItem(tr("不适用")); | ||||
| ui_->logicModeComboBox->setEnabled(false); | ui_->logicModeComboBox->setEnabled(false); | ||||
| ui_->logicComparisonComboBox->setEnabled(true); | ui_->logicComparisonComboBox->setEnabled(true); | ||||
| ui_->logicComparisonComboBox->setCurrentIndex( | ui_->logicComparisonComboBox->setCurrentIndex( | ||||
| ui_->logicComparisonComboBox->findData( | ui_->logicComparisonComboBox->findData( | ||||
| static_cast<int>(compare.comparison))); | |||||
| static_cast<int>(compare->comparison))); | |||||
| ui_->logicValueSpinBox->setEnabled(true); | ui_->logicValueSpinBox->setEnabled(true); | ||||
| ui_->logicValueSpinBox->setValue(compare.value); | |||||
| ui_->logicValueSpinBox->setValue(compare->value); | |||||
| } | |||||
| else | |||||
| { | |||||
| const auto &ton = std::get<TonNodeConfig>(node->config); | |||||
| ui_->logicNodeTypeLabel->setText(tr("TON")); | |||||
| ui_->logicAddressAreaLabel->setText(QStringLiteral("T")); | |||||
| ui_->logicAddressSpinBox->setValue(ton.address.index()); | |||||
| ui_->logicModeComboBox->addItem(tr("非保持接通延时")); | |||||
| ui_->logicModeComboBox->setEnabled(false); | |||||
| ui_->logicPresetSpinBox->setEnabled(true); | |||||
| ui_->logicPresetSpinBox->setValue(ton.presetMs); | |||||
| } | } | ||||
| if (!node->configured) | if (!node->configured) | ||||
| { | { | ||||
| @@ -1500,13 +1619,13 @@ void MainWindow::setLogicOutput(const LogicNodeConfig &config) | |||||
| const LogicEditorResult result = logic_editor_widget_->setOutput(config); | const LogicEditorResult result = logic_editor_widget_->setOutput(config); | ||||
| if (!result.succeeded) | if (!result.succeeded) | ||||
| { | { | ||||
| showProjectResult(tr("设置输出线圈"), fromUtf8(result.message), false); | |||||
| showProjectResult(tr("设置逻辑输出"), fromUtf8(result.message), false); | |||||
| return; | return; | ||||
| } | } | ||||
| selected_logic_node_id_ = result.id; | selected_logic_node_id_ = result.id; | ||||
| showLogicNodeProperties(result.id); | showLogicNodeProperties(result.id); | ||||
| refreshProjectUi(); | refreshProjectUi(); | ||||
| statusBar()->showMessage(tr("输出线圈已设置"), 3000); | |||||
| statusBar()->showMessage(tr("逻辑输出已设置"), 3000); | |||||
| } | } | ||||
| void MainWindow::addLogicRung() | void MainWindow::addLogicRung() | ||||
| @@ -1523,6 +1642,43 @@ void MainWindow::addLogicRung() | |||||
| statusBar()->showMessage(tr("已新建网络"), 3000); | statusBar()->showMessage(tr("已新建网络"), 3000); | ||||
| } | } | ||||
| void MainWindow::editSelectedRungComment() | |||||
| { | |||||
| const std::string rung_id = logic_editor_widget_->selectedRungId(); | |||||
| if (rung_id.empty()) | |||||
| { | |||||
| showProjectResult(tr("网络注释"), tr("请先选择一个梯形图网络"), false); | |||||
| return; | |||||
| } | |||||
| const LadderRung *rung = logic_editor_service_.findRung( | |||||
| current_logic_id_, rung_id); | |||||
| if (rung == nullptr) | |||||
| { | |||||
| return; | |||||
| } | |||||
| bool accepted = false; | |||||
| const QString comment = QInputDialog::getMultiLineText( | |||||
| this, | |||||
| tr("网络注释"), | |||||
| tr("说明"), | |||||
| fromUtf8(rung->comment), | |||||
| &accepted); | |||||
| if (!accepted) | |||||
| { | |||||
| return; | |||||
| } | |||||
| const LogicEditorResult result = logic_editor_service_.updateRungComment( | |||||
| current_logic_id_, rung_id, toUtf8(comment)); | |||||
| if (!result.succeeded) | |||||
| { | |||||
| showProjectResult(tr("网络注释"), fromUtf8(result.message), false); | |||||
| return; | |||||
| } | |||||
| logic_editor_widget_->reloadLogic(); | |||||
| refreshProjectUi(); | |||||
| statusBar()->showMessage(tr("网络注释已更新"), 3000); | |||||
| } | |||||
| void MainWindow::deleteSelectedLogicObject() | void MainWindow::deleteSelectedLogicObject() | ||||
| { | { | ||||
| const LogicEditorResult result = logic_editor_widget_->deleteSelected(); | const LogicEditorResult result = logic_editor_widget_->deleteSelected(); | ||||
| @@ -1551,13 +1707,25 @@ void MainWindow::applySelectedLogicNodeProperties() | |||||
| RegisterAddress{RegisterArea::M, ui_->logicAddressSpinBox->value()}, | RegisterAddress{RegisterArea::M, ui_->logicAddressSpinBox->value()}, | ||||
| static_cast<ContactMode>(ui_->logicModeComboBox->currentData().toInt())}; | static_cast<ContactMode>(ui_->logicModeComboBox->currentData().toInt())}; | ||||
| } | } | ||||
| else if (std::holds_alternative<EdgeContactNodeConfig>(config)) | |||||
| { | |||||
| config = EdgeContactNodeConfig{ | |||||
| RegisterAddress{RegisterArea::M, ui_->logicAddressSpinBox->value()}, | |||||
| static_cast<EdgeMode>(ui_->logicModeComboBox->currentData().toInt())}; | |||||
| } | |||||
| else if (std::holds_alternative<TimerContactNodeConfig>(config)) | |||||
| { | |||||
| config = TimerContactNodeConfig{ | |||||
| TimerAddress{ui_->logicAddressSpinBox->value()}, | |||||
| static_cast<ContactMode>(ui_->logicModeComboBox->currentData().toInt())}; | |||||
| } | |||||
| else if (std::holds_alternative<CoilNodeConfig>(config)) | else if (std::holds_alternative<CoilNodeConfig>(config)) | ||||
| { | { | ||||
| config = CoilNodeConfig{ | config = CoilNodeConfig{ | ||||
| RegisterAddress{RegisterArea::M, ui_->logicAddressSpinBox->value()}, | RegisterAddress{RegisterArea::M, ui_->logicAddressSpinBox->value()}, | ||||
| static_cast<CoilMode>(ui_->logicModeComboBox->currentData().toInt())}; | static_cast<CoilMode>(ui_->logicModeComboBox->currentData().toInt())}; | ||||
| } | } | ||||
| else | |||||
| else if (std::holds_alternative<CompareNodeConfig>(config)) | |||||
| { | { | ||||
| config = CompareNodeConfig{ | config = CompareNodeConfig{ | ||||
| RegisterAddress{RegisterArea::D, ui_->logicAddressSpinBox->value()}, | RegisterAddress{RegisterArea::D, ui_->logicAddressSpinBox->value()}, | ||||
| @@ -1565,6 +1733,12 @@ void MainWindow::applySelectedLogicNodeProperties() | |||||
| ui_->logicComparisonComboBox->currentData().toInt()), | ui_->logicComparisonComboBox->currentData().toInt()), | ||||
| static_cast<std::int16_t>(ui_->logicValueSpinBox->value())}; | static_cast<std::int16_t>(ui_->logicValueSpinBox->value())}; | ||||
| } | } | ||||
| else | |||||
| { | |||||
| config = TonNodeConfig{ | |||||
| TimerAddress{ui_->logicAddressSpinBox->value()}, | |||||
| ui_->logicPresetSpinBox->value()}; | |||||
| } | |||||
| const LogicEditorResult result = logic_editor_service_.updateNodeConfig( | const LogicEditorResult result = logic_editor_service_.updateNodeConfig( | ||||
| current_logic_id_, selected_logic_node_id_, config); | current_logic_id_, selected_logic_node_id_, config); | ||||
| @@ -1775,13 +1949,19 @@ void MainWindow::updateModeUi(const QString &message) | |||||
| ui_->addPageJumpAction->setEnabled(policy.allowsProjectEditing); | ui_->addPageJumpAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->addAlarmListAction->setEnabled(policy.allowsProjectEditing); | ui_->addAlarmListAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->configureAlarmsAction->setEnabled(policy.allowsProjectEditing); | ui_->configureAlarmsAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->configureRegisterCommentsAction->setEnabled(policy.allowsProjectEditing); | |||||
| ui_->deleteControlAction->setEnabled(policy.allowsProjectEditing); | ui_->deleteControlAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->addNormallyOpenAction->setEnabled(policy.allowsProjectEditing); | ui_->addNormallyOpenAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->addNormallyClosedAction->setEnabled(policy.allowsProjectEditing); | ui_->addNormallyClosedAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->addRisingEdgeAction->setEnabled(policy.allowsProjectEditing); | |||||
| ui_->addFallingEdgeAction->setEnabled(policy.allowsProjectEditing); | |||||
| ui_->addTimerContactAction->setEnabled(policy.allowsProjectEditing); | |||||
| ui_->addNormalCoilAction->setEnabled(policy.allowsProjectEditing); | ui_->addNormalCoilAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->addSetCoilAction->setEnabled(policy.allowsProjectEditing); | ui_->addSetCoilAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->addResetCoilAction->setEnabled(policy.allowsProjectEditing); | ui_->addResetCoilAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->addTonAction->setEnabled(policy.allowsProjectEditing); | |||||
| ui_->addCompareAction->setEnabled(policy.allowsProjectEditing); | ui_->addCompareAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->editRungCommentAction->setEnabled(policy.allowsProjectEditing); | |||||
| ui_->deleteLogicAction->setEnabled(policy.allowsProjectEditing); | ui_->deleteLogicAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->newProjectAction->setEnabled(policy.allowsProjectEditing); | ui_->newProjectAction->setEnabled(policy.allowsProjectEditing); | ||||
| ui_->saveProjectAction->setEnabled(policy.allowsProjectEditing); | ui_->saveProjectAction->setEnabled(policy.allowsProjectEditing); | ||||
| @@ -35,6 +35,7 @@ class HmiRuntimeService; | |||||
| class AlarmEditorService; | class AlarmEditorService; | ||||
| class AlarmService; | class AlarmService; | ||||
| class HmiNavigationService; | class HmiNavigationService; | ||||
| class RegisterCommentService; | |||||
| class HmiEditorWidget; | class HmiEditorWidget; | ||||
| class LogicEditorService; | class LogicEditorService; | ||||
| class LogicEditorWidget; | class LogicEditorWidget; | ||||
| @@ -67,6 +68,7 @@ public: | |||||
| HmiRuntimeService &hmi_runtime_service, | HmiRuntimeService &hmi_runtime_service, | ||||
| AlarmEditorService &alarm_editor_service, | AlarmEditorService &alarm_editor_service, | ||||
| AlarmService &alarm_service, | AlarmService &alarm_service, | ||||
| RegisterCommentService ®ister_comment_service, | |||||
| RegisterMonitorService ®ister_monitor_service, | RegisterMonitorService ®ister_monitor_service, | ||||
| QWidget *parent = nullptr); | QWidget *parent = nullptr); | ||||
| MainWindow( | MainWindow( | ||||
| @@ -78,6 +80,7 @@ public: | |||||
| AlarmEditorService &alarm_editor_service, | AlarmEditorService &alarm_editor_service, | ||||
| AlarmService &alarm_service, | AlarmService &alarm_service, | ||||
| HmiNavigationService &hmi_navigation_service, | HmiNavigationService &hmi_navigation_service, | ||||
| RegisterCommentService ®ister_comment_service, | |||||
| RegisterMonitorService ®ister_monitor_service, | RegisterMonitorService ®ister_monitor_service, | ||||
| QWidget *parent = nullptr); | QWidget *parent = nullptr); | ||||
| @@ -101,6 +104,7 @@ private: | |||||
| void configurePropertyEditor(); | void configurePropertyEditor(); | ||||
| void configurePlcConnection(); | void configurePlcConnection(); | ||||
| void configureAlarms(); | void configureAlarms(); | ||||
| void configureRegisterComments(); | |||||
| // 刷新工程树和当前 HMI 页面信息 | // 刷新工程树和当前 HMI 页面信息 | ||||
| void refreshProjectUi(); | void refreshProjectUi(); | ||||
| void handleProjectTreeSelection(); | void handleProjectTreeSelection(); | ||||
| @@ -130,6 +134,7 @@ private: | |||||
| void setLogicOutput(const LogicNodeConfig &config); | void setLogicOutput(const LogicNodeConfig &config); | ||||
| // 新增一个梯形图网络 | // 新增一个梯形图网络 | ||||
| void addLogicRung(); | void addLogicRung(); | ||||
| void editSelectedRungComment(); | |||||
| // 删除当前选中的逻辑节点或网络 | // 删除当前选中的逻辑节点或网络 | ||||
| void deleteSelectedLogicObject(); | void deleteSelectedLogicObject(); | ||||
| // 将逻辑属性表单内容应用到当前选中节点 | // 将逻辑属性表单内容应用到当前选中节点 | ||||
| @@ -185,6 +190,7 @@ private: | |||||
| HmiRuntimeService &hmi_runtime_service_; | HmiRuntimeService &hmi_runtime_service_; | ||||
| AlarmEditorService &alarm_editor_service_; | AlarmEditorService &alarm_editor_service_; | ||||
| AlarmService &alarm_service_; | AlarmService &alarm_service_; | ||||
| RegisterCommentService ®ister_comment_service_; | |||||
| RegisterMonitorService ®ister_monitor_service_; | RegisterMonitorService ®ister_monitor_service_; | ||||
| std::unique_ptr<HmiNavigationService> owned_hmi_navigation_service_; | std::unique_ptr<HmiNavigationService> owned_hmi_navigation_service_; | ||||
| HmiNavigationService *hmi_navigation_service_ = nullptr; | HmiNavigationService *hmi_navigation_service_ = nullptr; | ||||
| @@ -254,9 +254,10 @@ | |||||
| <addaction name="disconnectPlcAction"/> | <addaction name="disconnectPlcAction"/> | ||||
| </widget> | </widget> | ||||
| <widget class="QMenu" name="viewMenu"> | <widget class="QMenu" name="viewMenu"> | ||||
| <property name="title"> | |||||
| <string>视图(&V)</string> | |||||
| </property> | |||||
| <property name="title"> | |||||
| <string>视图(&V)</string> | |||||
| </property> | |||||
| <addaction name="configureRegisterCommentsAction"/> | |||||
| </widget> | </widget> | ||||
| <addaction name="fileMenu"/> | <addaction name="fileMenu"/> | ||||
| <addaction name="runMenu"/> | <addaction name="runMenu"/> | ||||
| @@ -326,10 +327,15 @@ | |||||
| <addaction name="separator"/> | <addaction name="separator"/> | ||||
| <addaction name="addNormallyOpenAction"/> | <addaction name="addNormallyOpenAction"/> | ||||
| <addaction name="addNormallyClosedAction"/> | <addaction name="addNormallyClosedAction"/> | ||||
| <addaction name="addRisingEdgeAction"/> | |||||
| <addaction name="addFallingEdgeAction"/> | |||||
| <addaction name="addTimerContactAction"/> | |||||
| <addaction name="addNormalCoilAction"/> | <addaction name="addNormalCoilAction"/> | ||||
| <addaction name="addSetCoilAction"/> | <addaction name="addSetCoilAction"/> | ||||
| <addaction name="addResetCoilAction"/> | <addaction name="addResetCoilAction"/> | ||||
| <addaction name="addTonAction"/> | |||||
| <addaction name="addCompareAction"/> | <addaction name="addCompareAction"/> | ||||
| <addaction name="editRungCommentAction"/> | |||||
| <addaction name="deleteLogicAction"/> | <addaction name="deleteLogicAction"/> | ||||
| </widget> | </widget> | ||||
| <widget class="QDockWidget" name="projectDock"> | <widget class="QDockWidget" name="projectDock"> | ||||
| @@ -758,7 +764,27 @@ | |||||
| </property> | </property> | ||||
| </widget> | </widget> | ||||
| </item> | </item> | ||||
| <item row="7" column="0" colspan="2"> | |||||
| <item row="7" column="0"> | |||||
| <widget class="QLabel" name="logicPresetLabel"> | |||||
| <property name="text"> | |||||
| <string>预设时间(ms)</string> | |||||
| </property> | |||||
| </widget> | |||||
| </item> | |||||
| <item row="7" column="1"> | |||||
| <widget class="QSpinBox" name="logicPresetSpinBox"> | |||||
| <property name="minimum"> | |||||
| <number>1</number> | |||||
| </property> | |||||
| <property name="maximum"> | |||||
| <number>86400000</number> | |||||
| </property> | |||||
| <property name="value"> | |||||
| <number>1000</number> | |||||
| </property> | |||||
| </widget> | |||||
| </item> | |||||
| <item row="8" column="0" colspan="2"> | |||||
| <widget class="QPushButton" name="applyLogicPropertiesButton"> | <widget class="QPushButton" name="applyLogicPropertiesButton"> | ||||
| <property name="text"> | <property name="text"> | ||||
| <string>应用属性</string> | <string>应用属性</string> | ||||
| @@ -848,6 +874,14 @@ | |||||
| <string>配置参数并连接真实 PLC</string> | <string>配置参数并连接真实 PLC</string> | ||||
| </property> | </property> | ||||
| </action> | </action> | ||||
| <action name="configureRegisterCommentsAction"> | |||||
| <property name="text"> | |||||
| <string>软元件注释</string> | |||||
| </property> | |||||
| <property name="toolTip"> | |||||
| <string>维护工程级 M/D 地址注释</string> | |||||
| </property> | |||||
| </action> | |||||
| <action name="disconnectPlcAction"> | <action name="disconnectPlcAction"> | ||||
| <property name="text"> | <property name="text"> | ||||
| <string>断开 PLC</string> | <string>断开 PLC</string> | ||||
| @@ -960,6 +994,30 @@ | |||||
| <string>向当前网络添加常闭触点</string> | <string>向当前网络添加常闭触点</string> | ||||
| </property> | </property> | ||||
| </action> | </action> | ||||
| <action name="addRisingEdgeAction"> | |||||
| <property name="text"> | |||||
| <string>上升沿</string> | |||||
| </property> | |||||
| <property name="toolTip"> | |||||
| <string>添加一个扫描周期的上升沿触点</string> | |||||
| </property> | |||||
| </action> | |||||
| <action name="addFallingEdgeAction"> | |||||
| <property name="text"> | |||||
| <string>下降沿</string> | |||||
| </property> | |||||
| <property name="toolTip"> | |||||
| <string>添加一个扫描周期的下降沿触点</string> | |||||
| </property> | |||||
| </action> | |||||
| <action name="addTimerContactAction"> | |||||
| <property name="text"> | |||||
| <string>T 触点</string> | |||||
| </property> | |||||
| <property name="toolTip"> | |||||
| <string>添加离线仿真定时器触点</string> | |||||
| </property> | |||||
| </action> | |||||
| <action name="addNormalCoilAction"> | <action name="addNormalCoilAction"> | ||||
| <property name="text"> | <property name="text"> | ||||
| <string>线圈</string> | <string>线圈</string> | ||||
| @@ -984,6 +1042,14 @@ | |||||
| <string>设置当前网络的复位线圈</string> | <string>设置当前网络的复位线圈</string> | ||||
| </property> | </property> | ||||
| </action> | </action> | ||||
| <action name="addTonAction"> | |||||
| <property name="text"> | |||||
| <string>TON</string> | |||||
| </property> | |||||
| <property name="toolTip"> | |||||
| <string>设置非保持接通延时定时器输出</string> | |||||
| </property> | |||||
| </action> | |||||
| <action name="addCompareAction"> | <action name="addCompareAction"> | ||||
| <property name="text"> | <property name="text"> | ||||
| <string>D 比较</string> | <string>D 比较</string> | ||||
| @@ -992,6 +1058,14 @@ | |||||
| <string>向当前网络添加 D 值与常量比较</string> | <string>向当前网络添加 D 值与常量比较</string> | ||||
| </property> | </property> | ||||
| </action> | </action> | ||||
| <action name="editRungCommentAction"> | |||||
| <property name="text"> | |||||
| <string>网络注释</string> | |||||
| </property> | |||||
| <property name="toolTip"> | |||||
| <string>编辑当前梯形图网络说明</string> | |||||
| </property> | |||||
| </action> | |||||
| <action name="deleteLogicAction"> | <action name="deleteLogicAction"> | ||||
| <property name="text"> | <property name="text"> | ||||
| <string>删除</string> | <string>删除</string> | ||||
| @@ -0,0 +1,155 @@ | |||||
| #include "register_comment_dialog.h" | |||||
| #include "services/register_comment_service.h" | |||||
| #include "ui_register_comment_dialog.h" | |||||
| #include <QHeaderView> | |||||
| #include <QMessageBox> | |||||
| #include <QTableWidgetItem> | |||||
| #include <optional> | |||||
| namespace { | |||||
| QString fromUtf8(const std::string &value) | |||||
| { | |||||
| return QString::fromUtf8(value.data(), static_cast<int>(value.size())); | |||||
| } | |||||
| std::string toUtf8(const QString &value) | |||||
| { | |||||
| const QByteArray bytes = value.toUtf8(); | |||||
| return std::string(bytes.constData(), static_cast<std::size_t>(bytes.size())); | |||||
| } | |||||
| } // namespace | |||||
| RegisterCommentDialog::RegisterCommentDialog( | |||||
| RegisterCommentService &service, | |||||
| QWidget *parent) | |||||
| : QDialog(parent), | |||||
| ui_(std::make_unique<Ui::RegisterCommentDialog>()), | |||||
| service_(service) | |||||
| { | |||||
| ui_->setupUi(this); | |||||
| ui_->commentTable->horizontalHeader()->setSectionResizeMode( | |||||
| 0, QHeaderView::ResizeToContents); | |||||
| ui_->commentTable->horizontalHeader()->setSectionResizeMode( | |||||
| 1, QHeaderView::Stretch); | |||||
| ui_->areaComboBox->setItemData(0, static_cast<int>(RegisterArea::M)); | |||||
| ui_->areaComboBox->setItemData(1, static_cast<int>(RegisterArea::D)); | |||||
| connect(ui_->commentTable, &QTableWidget::itemSelectionChanged, | |||||
| this, [this] { loadSelectedComment(); }); | |||||
| connect(ui_->saveButton, &QPushButton::clicked, | |||||
| this, [this] { saveComment(); }); | |||||
| connect(ui_->removeButton, &QPushButton::clicked, | |||||
| this, [this] { removeComment(); }); | |||||
| connect(ui_->buttonBox, &QDialogButtonBox::rejected, | |||||
| this, &QDialog::reject); | |||||
| reloadComments(); | |||||
| } | |||||
| RegisterCommentDialog::~RegisterCommentDialog() = default; | |||||
| void RegisterCommentDialog::reloadComments( | |||||
| const RegisterAddress *selected_address) | |||||
| { | |||||
| const auto &comments = service_.comments(); | |||||
| ui_->commentTable->setRowCount(static_cast<int>(comments.size())); | |||||
| int selected_row = -1; | |||||
| for (std::size_t index = 0; index < comments.size(); ++index) | |||||
| { | |||||
| const RegisterComment &comment = comments[index]; | |||||
| const int row = static_cast<int>(index); | |||||
| auto *address_item = new QTableWidgetItem( | |||||
| QString::fromStdString(comment.address.toString())); | |||||
| address_item->setData(Qt::UserRole, static_cast<int>(comment.address.area())); | |||||
| address_item->setData(Qt::UserRole + 1, comment.address.index()); | |||||
| ui_->commentTable->setItem(row, 0, address_item); | |||||
| ui_->commentTable->setItem(row, 1, new QTableWidgetItem(fromUtf8(comment.text))); | |||||
| if (selected_address != nullptr && comment.address == *selected_address) | |||||
| { | |||||
| selected_row = row; | |||||
| } | |||||
| } | |||||
| if (selected_row >= 0) | |||||
| { | |||||
| ui_->commentTable->selectRow(selected_row); | |||||
| } | |||||
| else | |||||
| { | |||||
| ui_->commentTable->clearSelection(); | |||||
| ui_->removeButton->setEnabled(false); | |||||
| } | |||||
| } | |||||
| void RegisterCommentDialog::loadSelectedComment() | |||||
| { | |||||
| const std::optional<RegisterAddress> address = selectedAddress(); | |||||
| const RegisterComment *comment = address.has_value() | |||||
| ? service_.findComment(*address) : nullptr; | |||||
| ui_->removeButton->setEnabled(comment != nullptr); | |||||
| if (comment == nullptr) | |||||
| { | |||||
| return; | |||||
| } | |||||
| ui_->areaComboBox->setCurrentIndex( | |||||
| comment->address.area() == RegisterArea::M ? 0 : 1); | |||||
| ui_->indexSpinBox->setValue(comment->address.index()); | |||||
| ui_->commentEdit->setText(fromUtf8(comment->text)); | |||||
| } | |||||
| void RegisterCommentDialog::saveComment() | |||||
| { | |||||
| const RegisterAddress address = addressFromInputs(); | |||||
| const RegisterCommentResult result = service_.setComment( | |||||
| address, toUtf8(ui_->commentEdit->text())); | |||||
| if (!result.succeeded) | |||||
| { | |||||
| QMessageBox::warning(this, tr("保存注释"), fromUtf8(result.message)); | |||||
| return; | |||||
| } | |||||
| reloadComments(&address); | |||||
| } | |||||
| void RegisterCommentDialog::removeComment() | |||||
| { | |||||
| const std::optional<RegisterAddress> address = selectedAddress(); | |||||
| if (!address.has_value()) | |||||
| { | |||||
| return; | |||||
| } | |||||
| if (QMessageBox::question( | |||||
| this, tr("删除注释"), tr("确定删除当前软元件注释吗?")) | |||||
| != QMessageBox::Yes) | |||||
| { | |||||
| return; | |||||
| } | |||||
| const RegisterCommentResult result = service_.removeComment(*address); | |||||
| if (!result.succeeded) | |||||
| { | |||||
| QMessageBox::warning(this, tr("删除注释"), fromUtf8(result.message)); | |||||
| return; | |||||
| } | |||||
| reloadComments(); | |||||
| } | |||||
| RegisterAddress RegisterCommentDialog::addressFromInputs() const | |||||
| { | |||||
| const RegisterArea area = static_cast<RegisterArea>( | |||||
| ui_->areaComboBox->currentData().toInt()); | |||||
| return RegisterAddress{area, ui_->indexSpinBox->value()}; | |||||
| } | |||||
| std::optional<RegisterAddress> RegisterCommentDialog::selectedAddress() const | |||||
| { | |||||
| const int row = ui_->commentTable->currentRow(); | |||||
| const QTableWidgetItem *item = row >= 0 ? ui_->commentTable->item(row, 0) : nullptr; | |||||
| if (item == nullptr) | |||||
| { | |||||
| return std::nullopt; | |||||
| } | |||||
| return RegisterAddress{ | |||||
| static_cast<RegisterArea>(item->data(Qt::UserRole).toInt()), | |||||
| item->data(Qt::UserRole + 1).toInt()}; | |||||
| } | |||||
| @@ -0,0 +1,34 @@ | |||||
| #pragma once | |||||
| #include "domain/register_address.h" | |||||
| #include <QDialog> | |||||
| #include <memory> | |||||
| #include <optional> | |||||
| namespace Ui { | |||||
| class RegisterCommentDialog; | |||||
| } | |||||
| class RegisterCommentService; | |||||
| class RegisterCommentDialog final : public QDialog | |||||
| { | |||||
| public: | |||||
| explicit RegisterCommentDialog( | |||||
| RegisterCommentService &service, | |||||
| QWidget *parent = nullptr); | |||||
| ~RegisterCommentDialog() override; | |||||
| private: | |||||
| void reloadComments(const RegisterAddress *selected_address = nullptr); | |||||
| void loadSelectedComment(); | |||||
| void saveComment(); | |||||
| void removeComment(); | |||||
| RegisterAddress addressFromInputs() const; | |||||
| std::optional<RegisterAddress> selectedAddress() const; | |||||
| std::unique_ptr<Ui::RegisterCommentDialog> ui_; | |||||
| RegisterCommentService &service_; | |||||
| }; | |||||
| @@ -0,0 +1,40 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <ui version="4.0"> | |||||
| <class>RegisterCommentDialog</class> | |||||
| <widget class="QDialog" name="RegisterCommentDialog"> | |||||
| <property name="windowTitle"><string>软元件注释</string></property> | |||||
| <property name="minimumSize"><size><width>620</width><height>400</height></size></property> | |||||
| <layout class="QVBoxLayout" name="verticalLayout"> | |||||
| <item> | |||||
| <widget class="QTableWidget" name="commentTable"> | |||||
| <property name="selectionBehavior"><enum>QAbstractItemView::SelectRows</enum></property> | |||||
| <property name="selectionMode"><enum>QAbstractItemView::SingleSelection</enum></property> | |||||
| <property name="editTriggers"><set>QAbstractItemView::NoEditTriggers</set></property> | |||||
| <property name="columnCount"><number>2</number></property> | |||||
| <column><property name="text"><string>地址</string></property></column> | |||||
| <column><property name="text"><string>注释</string></property></column> | |||||
| </widget> | |||||
| </item> | |||||
| <item> | |||||
| <layout class="QFormLayout" name="commentForm"> | |||||
| <item row="0" column="0"><widget class="QLabel" name="areaLabel"><property name="text"><string>地址区域</string></property></widget></item> | |||||
| <item row="0" column="1"><widget class="QComboBox" name="areaComboBox"><item><property name="text"><string>M</string></property></item><item><property name="text"><string>D</string></property></item></widget></item> | |||||
| <item row="1" column="0"><widget class="QLabel" name="indexLabel"><property name="text"><string>地址</string></property></widget></item> | |||||
| <item row="1" column="1"><widget class="QSpinBox" name="indexSpinBox"><property name="maximum"><number>4000</number></property></widget></item> | |||||
| <item row="2" column="0"><widget class="QLabel" name="commentLabel"><property name="text"><string>注释</string></property></widget></item> | |||||
| <item row="2" column="1"><widget class="QLineEdit" name="commentEdit"><property name="placeholderText"><string>例如:启动按钮</string></property></widget></item> | |||||
| </layout> | |||||
| </item> | |||||
| <item> | |||||
| <layout class="QHBoxLayout" name="operationLayout"> | |||||
| <item><widget class="QPushButton" name="saveButton"><property name="text"><string>保存</string></property></widget></item> | |||||
| <item><widget class="QPushButton" name="removeButton"><property name="text"><string>删除</string></property></widget></item> | |||||
| <item><spacer name="operationSpacer"><property name="orientation"><enum>Qt::Horizontal</enum></property><property name="sizeHint" stdset="0"><size><width>40</width><height>20</height></size></property></spacer></item> | |||||
| <item><widget class="QDialogButtonBox" name="buttonBox"><property name="standardButtons"><set>QDialogButtonBox::Close</set></property></widget></item> | |||||
| </layout> | |||||
| </item> | |||||
| </layout> | |||||
| </widget> | |||||
| <resources/> | |||||
| <connections/> | |||||
| </ui> | |||||
| @@ -11,6 +11,7 @@ | |||||
| #include "services/project_service.h" | #include "services/project_service.h" | ||||
| #include "services/runtime_mode_service.h" | #include "services/runtime_mode_service.h" | ||||
| #include "services/register_monitor_service.h" | #include "services/register_monitor_service.h" | ||||
| #include "services/register_comment_service.h" | |||||
| #include "ui/hmi_editor_widget.h" | #include "ui/hmi_editor_widget.h" | ||||
| #include "ui/free_monitor_widget.h" | #include "ui/free_monitor_widget.h" | ||||
| #include "ui/logic_editor_widget.h" | #include "ui/logic_editor_widget.h" | ||||
| @@ -29,9 +30,11 @@ | |||||
| #include <QLabel> | #include <QLabel> | ||||
| #include <QLineEdit> | #include <QLineEdit> | ||||
| #include <QListWidget> | #include <QListWidget> | ||||
| #include <QMenu> | |||||
| #include <QPainter> | #include <QPainter> | ||||
| #include <QSpinBox> | #include <QSpinBox> | ||||
| #include <QPushButton> | #include <QPushButton> | ||||
| #include <QStringList> | |||||
| #include <QTableWidget> | #include <QTableWidget> | ||||
| #include <QTimer> | #include <QTimer> | ||||
| #include <QTest> | #include <QTest> | ||||
| @@ -501,6 +504,7 @@ void testModeActionsControlEditingAvailability() | |||||
| HmiRuntimeService runtime_service(repository); | HmiRuntimeService runtime_service(repository); | ||||
| AlarmEditorService alarm_editor_service(project_service); | AlarmEditorService alarm_editor_service(project_service); | ||||
| AlarmService alarm_service(project_service, repository); | AlarmService alarm_service(project_service, repository); | ||||
| RegisterCommentService register_comment_service(project_service); | |||||
| OfflineSimulationService simulation_service(repository); | OfflineSimulationService simulation_service(repository); | ||||
| RuntimeModeService mode_service(project_service, simulation_service); | RuntimeModeService mode_service(project_service, simulation_service); | ||||
| RegisterMonitorService monitor_service(repository); | RegisterMonitorService monitor_service(repository); | ||||
| @@ -512,6 +516,7 @@ void testModeActionsControlEditingAvailability() | |||||
| runtime_service, | runtime_service, | ||||
| alarm_editor_service, | alarm_editor_service, | ||||
| alarm_service, | alarm_service, | ||||
| register_comment_service, | |||||
| monitor_service); | monitor_service); | ||||
| window.resize(1000, 640); | window.resize(1000, 640); | ||||
| window.show(); | window.show(); | ||||
| @@ -711,6 +716,105 @@ void testModeActionsControlEditingAvailability() | |||||
| "rejected online running must restore the editing action"); | "rejected online running must restore the editing action"); | ||||
| } | } | ||||
| void testEdgeTimerPropertyEditingAndParallelMenu() | |||||
| { | |||||
| TestProjectStorage storage; | |||||
| ProjectService project_service(storage); | |||||
| HmiEditorService editor_service(project_service); | |||||
| LogicEditorService logic_editor_service(project_service); | |||||
| VirtualRegisterRepository repository; | |||||
| HmiRuntimeService runtime_service(repository); | |||||
| AlarmEditorService alarm_editor_service(project_service); | |||||
| AlarmService alarm_service(project_service, repository); | |||||
| RegisterCommentService register_comment_service(project_service); | |||||
| OfflineSimulationService simulation_service(repository); | |||||
| RuntimeModeService mode_service(project_service, simulation_service); | |||||
| RegisterMonitorService monitor_service(repository); | |||||
| MainWindow window( | |||||
| mode_service, | |||||
| project_service, | |||||
| editor_service, | |||||
| logic_editor_service, | |||||
| runtime_service, | |||||
| alarm_editor_service, | |||||
| alarm_service, | |||||
| register_comment_service, | |||||
| monitor_service); | |||||
| window.show(); | |||||
| QApplication::processEvents(); | |||||
| QAction *parallel_action = requiredChild<QAction>(window, "parallelInsertAction"); | |||||
| require(parallel_action->menu() != nullptr, | |||||
| "parallel insertion must expose its node type menu"); | |||||
| QStringList parallel_labels; | |||||
| for (QAction *action : parallel_action->menu()->actions()) | |||||
| { | |||||
| parallel_labels.push_back(action->text()); | |||||
| } | |||||
| require(parallel_labels.contains(QStringLiteral("并联上升沿触点")) | |||||
| && parallel_labels.contains(QStringLiteral("并联下降沿触点")) | |||||
| && parallel_labels.contains(QStringLiteral("并联 T 触点")), | |||||
| "parallel insertion must include edge and T contact choices"); | |||||
| QAction *rising_action = requiredChild<QAction>(window, "addRisingEdgeAction"); | |||||
| QAction *falling_action = requiredChild<QAction>(window, "addFallingEdgeAction"); | |||||
| QAction *timer_contact_action = requiredChild<QAction>( | |||||
| window, "addTimerContactAction"); | |||||
| QAction *ton_action = requiredChild<QAction>(window, "addTonAction"); | |||||
| requiredChild<QAction>(window, "editRungCommentAction"); | |||||
| requiredChild<QAction>(window, "configureRegisterCommentsAction"); | |||||
| QSpinBox *address = requiredChild<QSpinBox>(window, "logicAddressSpinBox"); | |||||
| QSpinBox *preset = requiredChild<QSpinBox>(window, "logicPresetSpinBox"); | |||||
| QComboBox *mode = requiredChild<QComboBox>(window, "logicModeComboBox"); | |||||
| QPushButton *apply = requiredChild<QPushButton>( | |||||
| window, "applyLogicPropertiesButton"); | |||||
| const std::string logic_id = logic_editor_service.firstLogicId(); | |||||
| rising_action->trigger(); | |||||
| address->setValue(8); | |||||
| mode->setCurrentIndex(mode->findData(static_cast<int>(EdgeMode::Falling))); | |||||
| apply->click(); | |||||
| const LogicNode *edge = logic_editor_service.findNode(logic_id, "edge-1"); | |||||
| require(edge != nullptr && edge->configured | |||||
| && std::get<EdgeContactNodeConfig>(edge->config).address.index() == 8 | |||||
| && std::get<EdgeContactNodeConfig>(edge->config).mode | |||||
| == EdgeMode::Falling, | |||||
| "edge properties must update M address and edge mode"); | |||||
| timer_contact_action->trigger(); | |||||
| address->setValue(9); | |||||
| mode->setCurrentIndex(mode->findData( | |||||
| static_cast<int>(ContactMode::NormallyClosed))); | |||||
| apply->click(); | |||||
| const LogicNode *timer_contact = logic_editor_service.findNode( | |||||
| logic_id, "timer-contact-1"); | |||||
| require(timer_contact != nullptr && timer_contact->configured | |||||
| && std::get<TimerContactNodeConfig>(timer_contact->config).address | |||||
| == TimerAddress{9} | |||||
| && std::get<TimerContactNodeConfig>(timer_contact->config).mode | |||||
| == ContactMode::NormallyClosed, | |||||
| "T contact properties must update T address and contact mode"); | |||||
| ton_action->trigger(); | |||||
| require(preset->isEnabled() && !mode->isEnabled(), | |||||
| "TON properties must enable PT and disable irrelevant mode editing"); | |||||
| address->setValue(9); | |||||
| preset->setValue(1234); | |||||
| apply->click(); | |||||
| const LogicNode *ton = logic_editor_service.findNode(logic_id, "ton-1"); | |||||
| require(ton != nullptr && ton->configured | |||||
| && std::get<TonNodeConfig>(ton->config).address == TimerAddress{9} | |||||
| && std::get<TonNodeConfig>(ton->config).presetMs == 1234, | |||||
| "TON properties must update T address and preset milliseconds"); | |||||
| falling_action->trigger(); | |||||
| const LogicNode *falling = logic_editor_service.findNode(logic_id, "edge-2"); | |||||
| require(falling != nullptr | |||||
| && std::get<EdgeContactNodeConfig>(falling->config).mode | |||||
| == EdgeMode::Falling, | |||||
| "falling edge action must create a falling-edge condition"); | |||||
| } | |||||
| void testPlcConfigurationUsesDialog() | void testPlcConfigurationUsesDialog() | ||||
| { | { | ||||
| PlcSerialConfiguration initial; | PlcSerialConfiguration initial; | ||||
| @@ -746,6 +850,7 @@ void testPlcConfigurationUsesDialog() | |||||
| HmiRuntimeService runtime_service(repository); | HmiRuntimeService runtime_service(repository); | ||||
| AlarmEditorService alarm_editor_service(project_service); | AlarmEditorService alarm_editor_service(project_service); | ||||
| AlarmService alarm_service(project_service, repository); | AlarmService alarm_service(project_service, repository); | ||||
| RegisterCommentService register_comment_service(project_service); | |||||
| OfflineSimulationService simulation_service(repository); | OfflineSimulationService simulation_service(repository); | ||||
| RuntimeModeService mode_service(project_service, simulation_service); | RuntimeModeService mode_service(project_service, simulation_service); | ||||
| RegisterMonitorService monitor_service(repository); | RegisterMonitorService monitor_service(repository); | ||||
| @@ -757,6 +862,7 @@ void testPlcConfigurationUsesDialog() | |||||
| runtime_service, | runtime_service, | ||||
| alarm_editor_service, | alarm_editor_service, | ||||
| alarm_service, | alarm_service, | ||||
| register_comment_service, | |||||
| monitor_service); | monitor_service); | ||||
| require(window.findChild<QComboBox *>(QStringLiteral("serialPortComboBox")) == nullptr, | require(window.findChild<QComboBox *>(QStringLiteral("serialPortComboBox")) == nullptr, | ||||
| "serial configuration controls must not be embedded in the main window"); | "serial configuration controls must not be embedded in the main window"); | ||||
| @@ -791,6 +897,7 @@ void testRepeatedPlcStatusNotificationsAreCoalesced() | |||||
| HmiRuntimeService runtime_service(active_repository); | HmiRuntimeService runtime_service(active_repository); | ||||
| AlarmEditorService alarm_editor_service(project_service); | AlarmEditorService alarm_editor_service(project_service); | ||||
| AlarmService alarm_service(project_service, active_repository); | AlarmService alarm_service(project_service, active_repository); | ||||
| RegisterCommentService register_comment_service(project_service); | |||||
| OfflineSimulationService simulation_service(virtual_repository); | OfflineSimulationService simulation_service(virtual_repository); | ||||
| RepeatedStatusGateway gateway; | RepeatedStatusGateway gateway; | ||||
| RuntimeModeService mode_service(project_service, simulation_service); | RuntimeModeService mode_service(project_service, simulation_service); | ||||
| @@ -805,6 +912,7 @@ void testRepeatedPlcStatusNotificationsAreCoalesced() | |||||
| runtime_service, | runtime_service, | ||||
| alarm_editor_service, | alarm_editor_service, | ||||
| alarm_service, | alarm_service, | ||||
| register_comment_service, | |||||
| monitor_service); | monitor_service); | ||||
| QListWidget *output = requiredChild<QListWidget>(window, "outputList"); | QListWidget *output = requiredChild<QListWidget>(window, "outputList"); | ||||
| const int initial_count = output->count(); | const int initial_count = output->count(); | ||||
| @@ -839,6 +947,7 @@ void testOnlineWorkspaceShowsHmiAndFreeMonitorOnly() | |||||
| HmiRuntimeService runtime_service(active_repository); | HmiRuntimeService runtime_service(active_repository); | ||||
| AlarmEditorService alarm_editor_service(project_service); | AlarmEditorService alarm_editor_service(project_service); | ||||
| AlarmService alarm_service(project_service, active_repository); | AlarmService alarm_service(project_service, active_repository); | ||||
| RegisterCommentService register_comment_service(project_service); | |||||
| OfflineSimulationService simulation_service(virtual_repository); | OfflineSimulationService simulation_service(virtual_repository); | ||||
| OnlineReadyGateway gateway; | OnlineReadyGateway gateway; | ||||
| RuntimeModeService mode_service(project_service, simulation_service); | RuntimeModeService mode_service(project_service, simulation_service); | ||||
| @@ -853,6 +962,7 @@ void testOnlineWorkspaceShowsHmiAndFreeMonitorOnly() | |||||
| runtime_service, | runtime_service, | ||||
| alarm_editor_service, | alarm_editor_service, | ||||
| alarm_service, | alarm_service, | ||||
| register_comment_service, | |||||
| monitor_service); | monitor_service); | ||||
| window.resize(1200, 760); | window.resize(1200, 760); | ||||
| window.show(); | window.show(); | ||||
| @@ -965,6 +1075,7 @@ void testMultiPageAndLogicMainWindowIntegration() | |||||
| HmiRuntimeService runtime_service(repository); | HmiRuntimeService runtime_service(repository); | ||||
| AlarmEditorService alarm_editor_service(project_service); | AlarmEditorService alarm_editor_service(project_service); | ||||
| AlarmService alarm_service(project_service, repository); | AlarmService alarm_service(project_service, repository); | ||||
| RegisterCommentService register_comment_service(project_service); | |||||
| OfflineSimulationService simulation_service(repository); | OfflineSimulationService simulation_service(repository); | ||||
| RuntimeModeService mode_service(project_service, simulation_service); | RuntimeModeService mode_service(project_service, simulation_service); | ||||
| RegisterMonitorService monitor_service(repository); | RegisterMonitorService monitor_service(repository); | ||||
| @@ -978,6 +1089,7 @@ void testMultiPageAndLogicMainWindowIntegration() | |||||
| alarm_editor_service, | alarm_editor_service, | ||||
| alarm_service, | alarm_service, | ||||
| navigation_service, | navigation_service, | ||||
| register_comment_service, | |||||
| monitor_service); | monitor_service); | ||||
| window.resize(1400, 820); | window.resize(1400, 820); | ||||
| window.show(); | window.show(); | ||||
| @@ -1078,6 +1190,7 @@ int main(int argc, char *argv[]) | |||||
| testRuntimePageJumpDoesNotRequireRegisterWritePermission(); | testRuntimePageJumpDoesNotRequireRegisterWritePermission(); | ||||
| testRuntimeAlarmListInteraction(); | testRuntimeAlarmListInteraction(); | ||||
| testModeActionsControlEditingAvailability(); | testModeActionsControlEditingAvailability(); | ||||
| testEdgeTimerPropertyEditingAndParallelMenu(); | |||||
| testPlcConfigurationUsesDialog(); | testPlcConfigurationUsesDialog(); | ||||
| testRepeatedPlcStatusNotificationsAreCoalesced(); | testRepeatedPlcStatusNotificationsAreCoalesced(); | ||||
| testOnlineWorkspaceShowsHmiAndFreeMonitorOnly(); | testOnlineWorkspaceShowsHmiAndFreeMonitorOnly(); | ||||
| @@ -12,6 +12,7 @@ SOURCES += \ | |||||
| main_window_tests.cpp \ | main_window_tests.cpp \ | ||||
| ../src/ui/main_window.cpp \ | ../src/ui/main_window.cpp \ | ||||
| ../src/ui/alarm_configuration_dialog.cpp \ | ../src/ui/alarm_configuration_dialog.cpp \ | ||||
| ../src/ui/register_comment_dialog.cpp \ | |||||
| ../src/ui/plc_connection_dialog.cpp \ | ../src/ui/plc_connection_dialog.cpp \ | ||||
| ../src/ui/free_monitor_widget.cpp \ | ../src/ui/free_monitor_widget.cpp \ | ||||
| ../src/ui/runtime_monitor_widget.cpp \ | ../src/ui/runtime_monitor_widget.cpp \ | ||||
| @@ -36,11 +37,13 @@ SOURCES += \ | |||||
| ../src/services/software_logic_executor.cpp \ | ../src/services/software_logic_executor.cpp \ | ||||
| ../src/services/offline_simulation_service.cpp \ | ../src/services/offline_simulation_service.cpp \ | ||||
| ../src/services/runtime_mode_service.cpp \ | ../src/services/runtime_mode_service.cpp \ | ||||
| ../src/services/register_monitor_service.cpp | |||||
| ../src/services/register_monitor_service.cpp \ | |||||
| ../src/services/register_comment_service.cpp | |||||
| HEADERS += \ | HEADERS += \ | ||||
| ../src/ui/main_window.h \ | ../src/ui/main_window.h \ | ||||
| ../src/ui/alarm_configuration_dialog.h \ | ../src/ui/alarm_configuration_dialog.h \ | ||||
| ../src/ui/register_comment_dialog.h \ | |||||
| ../src/ui/plc_connection_dialog.h \ | ../src/ui/plc_connection_dialog.h \ | ||||
| ../src/ui/free_monitor_widget.h \ | ../src/ui/free_monitor_widget.h \ | ||||
| ../src/ui/runtime_monitor_widget.h \ | ../src/ui/runtime_monitor_widget.h \ | ||||
| @@ -66,7 +69,8 @@ HEADERS += \ | |||||
| ../src/services/software_logic_executor.h \ | ../src/services/software_logic_executor.h \ | ||||
| ../src/services/offline_simulation_service.h \ | ../src/services/offline_simulation_service.h \ | ||||
| ../src/services/runtime_mode_service.h \ | ../src/services/runtime_mode_service.h \ | ||||
| ../src/services/register_monitor_service.h | |||||
| ../src/services/register_monitor_service.h \ | |||||
| ../src/services/register_comment_service.h | |||||
| HEADERS += \ | HEADERS += \ | ||||
| ../src/services/plc_communication_gateway.h | ../src/services/plc_communication_gateway.h | ||||
| @@ -74,6 +78,7 @@ HEADERS += \ | |||||
| FORMS += \ | FORMS += \ | ||||
| ../src/ui/main_window.ui \ | ../src/ui/main_window.ui \ | ||||
| ../src/ui/alarm_configuration_dialog.ui \ | ../src/ui/alarm_configuration_dialog.ui \ | ||||
| ../src/ui/register_comment_dialog.ui \ | |||||
| ../src/ui/plc_connection_dialog.ui \ | ../src/ui/plc_connection_dialog.ui \ | ||||
| ../src/ui/free_monitor_widget.ui \ | ../src/ui/free_monitor_widget.ui \ | ||||
| ../src/ui/runtime_monitor_widget.ui | ../src/ui/runtime_monitor_widget.ui | ||||