| @@ -21,13 +21,11 @@ SOURCES += \ | |||
| src/domain/register_address.cpp \ | |||
| src/domain/register_repository.cpp \ | |||
| src/domain/active_register_repository.cpp \ | |||
| src/domain/data_point_model.cpp \ | |||
| src/domain/hmi_model.cpp \ | |||
| src/domain/control_logic_model.cpp \ | |||
| src/domain/project_model.cpp \ | |||
| src/domain/runtime_state.cpp \ | |||
| src/services/project_service.cpp \ | |||
| src/services/data_point_service.cpp \ | |||
| src/services/hmi_editor_service.cpp \ | |||
| src/services/logic_editor_service.cpp \ | |||
| src/services/hmi_runtime_service.cpp \ | |||
| @@ -46,14 +44,12 @@ HEADERS += \ | |||
| src/domain/register_address.h \ | |||
| src/domain/register_repository.h \ | |||
| src/domain/active_register_repository.h \ | |||
| src/domain/data_point_model.h \ | |||
| src/domain/hmi_model.h \ | |||
| src/domain/control_logic_model.h \ | |||
| src/domain/project_model.h \ | |||
| src/domain/runtime_state.h \ | |||
| src/domain/project_storage.h \ | |||
| src/services/project_service.h \ | |||
| src/services/data_point_service.h \ | |||
| src/services/hmi_editor_service.h \ | |||
| src/services/logic_editor_service.h \ | |||
| src/services/hmi_runtime_service.h \ | |||
| @@ -1,37 +0,0 @@ | |||
| #include "data_point_model.h" | |||
| #include <algorithm> | |||
| #include <cctype> | |||
| namespace { | |||
| bool isBlank(const std::string &value) | |||
| { | |||
| return value.empty() | |||
| || std::all_of( | |||
| value.cbegin(), value.cend(), | |||
| [](unsigned char character) { return std::isspace(character) != 0; }); | |||
| } | |||
| } // namespace | |||
| bool DataPoint::validate(std::string *error) const | |||
| { | |||
| if (!address.isValid()) | |||
| { | |||
| if (error != nullptr) | |||
| { | |||
| *error = "data point requires a valid M or D address"; | |||
| } | |||
| return false; | |||
| } | |||
| if (isBlank(name)) | |||
| { | |||
| if (error != nullptr) | |||
| { | |||
| *error = "data point name must not be empty"; | |||
| } | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| @@ -1,14 +0,0 @@ | |||
| #pragma once | |||
| #include "register_address.h" | |||
| #include <string> | |||
| struct DataPoint | |||
| { | |||
| RegisterAddress address; | |||
| std::string name; | |||
| std::string comment; | |||
| bool validate(std::string *error = nullptr) const; | |||
| }; | |||
| @@ -52,29 +52,6 @@ bool Project::validate(std::string *error) const | |||
| setError(error, "control logic ids must be unique within a project"); | |||
| return false; | |||
| } | |||
| for (auto current = dataPoints.cbegin(); current != dataPoints.cend(); ++current) | |||
| { | |||
| if (!current->validate(error)) | |||
| { | |||
| return false; | |||
| } | |||
| const auto duplicate = std::find_if( | |||
| current + 1, | |||
| dataPoints.cend(), | |||
| [¤t](const DataPoint &candidate) | |||
| { | |||
| return candidate.address == current->address | |||
| || candidate.name == current->name; | |||
| }); | |||
| if (duplicate != dataPoints.cend()) | |||
| { | |||
| if (error != nullptr) | |||
| { | |||
| *error = "data point addresses and names must be unique"; | |||
| } | |||
| return false; | |||
| } | |||
| } | |||
| for (const HmiPage &page : hmiPages) | |||
| { | |||
| // 工程聚合校验会向下委托页面和控件的完整规则 | |||
| @@ -1,7 +1,6 @@ | |||
| #pragma once | |||
| #include "control_logic_model.h" | |||
| #include "data_point_model.h" | |||
| #include "hmi_model.h" | |||
| #include <string> | |||
| @@ -27,8 +26,6 @@ struct Project | |||
| std::vector<HmiPage> hmiPages; | |||
| // 工程包含的控制逻辑集合 | |||
| std::vector<ControlLogic> controlLogics; | |||
| // M/D 地址的工程级名称和注释定义 | |||
| std::vector<DataPoint> dataPoints; | |||
| // 校验工程配置并通过 error 返回失败原因 | |||
| bool validate(std::string *error = nullptr) const; | |||
| @@ -253,28 +253,6 @@ bool parseAddress( | |||
| return true; | |||
| } | |||
| QJsonObject serializeDataPoint(const DataPoint &data_point) | |||
| { | |||
| QJsonObject object; | |||
| object.insert(QStringLiteral("address"), serializeAddress(data_point.address)); | |||
| object.insert(QStringLiteral("name"), fromUtf8(data_point.name)); | |||
| object.insert(QStringLiteral("comment"), fromUtf8(data_point.comment)); | |||
| return object; | |||
| } | |||
| bool parseDataPoint( | |||
| const QJsonObject &object, | |||
| const std::string &context, | |||
| DataPoint *data_point, | |||
| ParseState *state) | |||
| { | |||
| QJsonObject address; | |||
| return readObject(object, "address", context, &address, state) | |||
| && parseAddress(address, context + ".address", &data_point->address, state) | |||
| && readString(object, "name", context, &data_point->name, state) | |||
| && readString(object, "comment", context, &data_point->comment, state); | |||
| } | |||
| // 将 HMI 控件类型枚举转换为工程文件中的稳定字符串 | |||
| QString hmiControlTypeName(HmiControlType type) | |||
| { | |||
| @@ -1088,19 +1066,12 @@ QJsonObject serializeProject(const Project &project) | |||
| logics.append(serializeControlLogic(logic)); | |||
| } | |||
| QJsonArray data_points; | |||
| for (const DataPoint &data_point : project.dataPoints) | |||
| { | |||
| data_points.append(serializeDataPoint(data_point)); | |||
| } | |||
| QJsonObject object; | |||
| object.insert(QStringLiteral("formatVersion"), fromUtf8(project.metadata.formatVersion)); | |||
| object.insert(QStringLiteral("id"), fromUtf8(project.metadata.id)); | |||
| object.insert(QStringLiteral("name"), fromUtf8(project.metadata.name)); | |||
| object.insert(QStringLiteral("hmiPages"), pages); | |||
| object.insert(QStringLiteral("controlLogics"), logics); | |||
| object.insert(QStringLiteral("dataPoints"), data_points); | |||
| return object; | |||
| } | |||
| @@ -1110,7 +1081,6 @@ bool parseProject( | |||
| { | |||
| QJsonArray pages; | |||
| QJsonArray logics; | |||
| QJsonArray data_points; | |||
| if (!readString( | |||
| object, | |||
| "formatVersion", | |||
| @@ -1131,8 +1101,7 @@ bool parseProject( | |||
| if (!readString(object, "id", "project", &project->metadata.id, state) | |||
| || !readString(object, "name", "project", &project->metadata.name, state) | |||
| || !readArray(object, "hmiPages", "project", &pages, state) | |||
| || !readArray(object, "controlLogics", "project", &logics, state) | |||
| || !readArray(object, "dataPoints", "project", &data_points, state)) | |||
| || !readArray(object, "controlLogics", "project", &logics, state)) | |||
| { | |||
| return false; | |||
| } | |||
| @@ -1179,26 +1148,6 @@ bool parseProject( | |||
| } | |||
| project->controlLogics.push_back(std::move(logic)); | |||
| } | |||
| project->dataPoints.reserve(static_cast<std::size_t>(data_points.size())); | |||
| for (int index = 0; index < data_points.size(); ++index) | |||
| { | |||
| if (!data_points.at(index).isObject()) | |||
| { | |||
| return state->fail( | |||
| ProjectStorageError::InvalidField, | |||
| "project.dataPoints items must be objects"); | |||
| } | |||
| DataPoint data_point{RegisterAddress{RegisterArea::M, 0}, {}, {}}; | |||
| if (!parseDataPoint( | |||
| data_points.at(index).toObject(), | |||
| "project.dataPoints[" + std::to_string(index) + ']', | |||
| &data_point, | |||
| state)) | |||
| { | |||
| return false; | |||
| } | |||
| project->dataPoints.push_back(std::move(data_point)); | |||
| } | |||
| return true; | |||
| } | |||
| @@ -13,7 +13,6 @@ | |||
| #include "infrastructure/plc_register_repository.h" | |||
| #include "domain/active_register_repository.h" | |||
| #include "services/hmi_editor_service.h" | |||
| #include "services/data_point_service.h" | |||
| #include "services/hmi_runtime_service.h" | |||
| #include "services/logic_editor_service.h" | |||
| #include "services/offline_simulation_service.h" | |||
| @@ -31,7 +30,6 @@ int main(int argc, char *argv[]) | |||
| JsonProjectStorage project_storage; | |||
| ProjectService project_service(project_storage); | |||
| HmiEditorService hmi_editor_service(project_service); | |||
| DataPointService data_point_service(project_service); | |||
| LogicEditorService logic_editor_service(project_service); | |||
| // 当前离线模式使用内存仓库,后续真机模式替换为 PLC 缓存实现 | |||
| VirtualRegisterRepository virtual_register_repository; | |||
| @@ -51,7 +49,6 @@ int main(int argc, char *argv[]) | |||
| runtime_mode_service, | |||
| project_service, | |||
| hmi_editor_service, | |||
| data_point_service, | |||
| logic_editor_service, | |||
| hmi_runtime_service); | |||
| main_window.show(); | |||
| @@ -1,170 +0,0 @@ | |||
| #include "data_point_service.h" | |||
| #include "project_service.h" | |||
| #include <algorithm> | |||
| namespace { | |||
| bool nodeUsesAddress(const LogicNode &node, const RegisterAddress &address) | |||
| { | |||
| return std::visit( | |||
| [&address](const auto &config) { return config.address == address; }, | |||
| node.config); | |||
| } | |||
| } // namespace | |||
| DataPointService::DataPointService(ProjectService &project_service) | |||
| : project_service_(project_service) | |||
| { | |||
| } | |||
| const std::vector<DataPoint> &DataPointService::dataPoints() const | |||
| { | |||
| return project_service_.project().dataPoints; | |||
| } | |||
| const DataPoint *DataPointService::find(const RegisterAddress &address) const | |||
| { | |||
| const auto &points = dataPoints(); | |||
| const auto found = std::find_if( | |||
| points.cbegin(), points.cend(), | |||
| [&address](const DataPoint &point) { return point.address == address; }); | |||
| return found == points.cend() ? nullptr : &*found; | |||
| } | |||
| DataPointResult DataPointService::add(const DataPoint &data_point) | |||
| { | |||
| const DataPointResult validation = validateUnique(data_point, nullptr); | |||
| if (!validation.succeeded) | |||
| { | |||
| return validation; | |||
| } | |||
| project_service_.editProject().dataPoints.push_back(data_point); | |||
| return {true, DataPointError::None, {}}; | |||
| } | |||
| DataPointResult DataPointService::update( | |||
| const RegisterAddress &old_address, const DataPoint &data_point) | |||
| { | |||
| const auto existing = std::find_if( | |||
| dataPoints().cbegin(), dataPoints().cend(), | |||
| [&old_address](const DataPoint &candidate) | |||
| { | |||
| return candidate.address == old_address; | |||
| }); | |||
| if (existing == dataPoints().cend()) | |||
| { | |||
| return {false, DataPointError::NotFound, "data point was not found"}; | |||
| } | |||
| const DataPointResult validation = validateUnique(data_point, &old_address); | |||
| if (!validation.succeeded) | |||
| { | |||
| return validation; | |||
| } | |||
| if (data_point.address != old_address && !references(old_address).empty()) | |||
| { | |||
| return {false, DataPointError::AddressInUse, | |||
| "referenced data point address cannot be changed"}; | |||
| } | |||
| Project &project = project_service_.editProject(); | |||
| const auto point = std::find_if( | |||
| project.dataPoints.begin(), project.dataPoints.end(), | |||
| [&old_address](const DataPoint &candidate) | |||
| { | |||
| return candidate.address == old_address; | |||
| }); | |||
| *point = data_point; | |||
| return {true, DataPointError::None, {}}; | |||
| } | |||
| DataPointResult DataPointService::remove(const RegisterAddress &address) | |||
| { | |||
| if (find(address) == nullptr) | |||
| { | |||
| return {false, DataPointError::NotFound, "data point was not found"}; | |||
| } | |||
| if (!references(address).empty()) | |||
| { | |||
| return {false, DataPointError::AddressInUse, | |||
| "data point is still referenced by HMI or ladder logic"}; | |||
| } | |||
| Project &project = project_service_.editProject(); | |||
| project.dataPoints.erase( | |||
| std::remove_if( | |||
| project.dataPoints.begin(), project.dataPoints.end(), | |||
| [&address](const DataPoint &point) { return point.address == address; }), | |||
| project.dataPoints.end()); | |||
| return {true, DataPointError::None, {}}; | |||
| } | |||
| std::vector<DataPointReference> DataPointService::references( | |||
| const RegisterAddress &address) const | |||
| { | |||
| std::vector<DataPointReference> result; | |||
| const Project &project = project_service_.project(); | |||
| for (const HmiPage &page : project.hmiPages) | |||
| { | |||
| for (const HmiControl &control : page.controls) | |||
| { | |||
| if (control.binding.has_value() && *control.binding == address) | |||
| { | |||
| result.push_back({"HMI/" + page.name + '/' + control.id}); | |||
| } | |||
| } | |||
| } | |||
| for (const ControlLogic &logic : project.controlLogics) | |||
| { | |||
| for (const LadderRung &rung : logic.rungs) | |||
| { | |||
| if (rung.condition.has_value()) | |||
| { | |||
| std::vector<const LogicNode *> nodes; | |||
| collectConditionNodes(*rung.condition, &nodes); | |||
| for (const LogicNode *node : nodes) | |||
| { | |||
| if (nodeUsesAddress(*node, address)) | |||
| { | |||
| result.push_back({"LAD/" + logic.name + '/' + rung.name + '/' + node->id}); | |||
| } | |||
| } | |||
| } | |||
| if (rung.output.has_value() && nodeUsesAddress(*rung.output, address)) | |||
| { | |||
| result.push_back({"LAD/" + logic.name + '/' + rung.name + '/' | |||
| + rung.output->id}); | |||
| } | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| DataPointResult DataPointService::validateUnique( | |||
| const DataPoint &data_point, | |||
| const RegisterAddress *ignored_address) const | |||
| { | |||
| std::string error; | |||
| if (!data_point.validate(&error)) | |||
| { | |||
| return {false, DataPointError::InvalidDataPoint, error}; | |||
| } | |||
| for (const DataPoint &existing : dataPoints()) | |||
| { | |||
| if (ignored_address != nullptr && existing.address == *ignored_address) | |||
| { | |||
| continue; | |||
| } | |||
| if (existing.address == data_point.address) | |||
| { | |||
| return {false, DataPointError::DuplicateAddress, | |||
| "data point address already exists"}; | |||
| } | |||
| if (existing.name == data_point.name) | |||
| { | |||
| return {false, DataPointError::DuplicateName, | |||
| "data point name already exists"}; | |||
| } | |||
| } | |||
| return {true, DataPointError::None, {}}; | |||
| } | |||
| @@ -1,51 +0,0 @@ | |||
| #pragma once | |||
| #include "domain/data_point_model.h" | |||
| #include <string> | |||
| #include <vector> | |||
| class ProjectService; | |||
| enum class DataPointError | |||
| { | |||
| None, | |||
| InvalidDataPoint, | |||
| DuplicateAddress, | |||
| DuplicateName, | |||
| NotFound, | |||
| AddressInUse | |||
| }; | |||
| struct DataPointResult | |||
| { | |||
| bool succeeded = false; | |||
| DataPointError error = DataPointError::None; | |||
| std::string message; | |||
| }; | |||
| struct DataPointReference | |||
| { | |||
| std::string location; | |||
| }; | |||
| class DataPointService | |||
| { | |||
| public: | |||
| explicit DataPointService(ProjectService &project_service); | |||
| const std::vector<DataPoint> &dataPoints() const; | |||
| const DataPoint *find(const RegisterAddress &address) const; | |||
| DataPointResult add(const DataPoint &data_point); | |||
| DataPointResult update( | |||
| const RegisterAddress &old_address, const DataPoint &data_point); | |||
| DataPointResult remove(const RegisterAddress &address); | |||
| std::vector<DataPointReference> references(const RegisterAddress &address) const; | |||
| private: | |||
| DataPointResult validateUnique( | |||
| const DataPoint &data_point, | |||
| const RegisterAddress *ignored_address) const; | |||
| ProjectService &project_service_; | |||
| }; | |||
| @@ -180,10 +180,6 @@ PlcCommunicationResult RuntimeModeService::connectPlc( | |||
| } | |||
| std::vector<RegisterAddress> addresses; | |||
| const Project &project = project_service_.project(); | |||
| for (const DataPoint &point : project.dataPoints) | |||
| { | |||
| addresses.push_back(point.address); | |||
| } | |||
| for (const HmiPage &page : project.hmiPages) | |||
| { | |||
| for (const HmiControl &control : page.controls) | |||
| @@ -4,7 +4,6 @@ | |||
| #include "logic_editor_widget.h" | |||
| #include "plc_connection_dialog.h" | |||
| #include "services/hmi_editor_service.h" | |||
| #include "services/data_point_service.h" | |||
| #include "services/hmi_runtime_service.h" | |||
| #include "services/logic_editor_service.h" | |||
| #include "services/project_service.h" | |||
| @@ -15,7 +14,6 @@ | |||
| #include <QApplication> | |||
| #include <QComboBox> | |||
| #include <QFileDialog> | |||
| #include <QHeaderView> | |||
| #include <QInputDialog> | |||
| #include <QLabel> | |||
| #include <QLineEdit> | |||
| @@ -27,7 +25,6 @@ | |||
| #include <QStyle> | |||
| #include <QTimer> | |||
| #include <QTabWidget> | |||
| #include <QTableWidget> | |||
| #include <QToolButton> | |||
| #include <QTreeWidget> | |||
| #include <QTreeWidgetItem> | |||
| @@ -137,7 +134,6 @@ MainWindow::MainWindow( | |||
| RuntimeModeService &runtime_mode_service, | |||
| ProjectService &project_service, | |||
| HmiEditorService &hmi_editor_service, | |||
| DataPointService &data_point_service, | |||
| LogicEditorService &logic_editor_service, | |||
| HmiRuntimeService &hmi_runtime_service, | |||
| QWidget *parent) | |||
| @@ -146,7 +142,6 @@ MainWindow::MainWindow( | |||
| runtime_mode_service_(runtime_mode_service), | |||
| project_service_(project_service), | |||
| hmi_editor_service_(hmi_editor_service), | |||
| data_point_service_(data_point_service), | |||
| logic_editor_service_(logic_editor_service), | |||
| hmi_runtime_service_(hmi_runtime_service) | |||
| { | |||
| @@ -154,7 +149,6 @@ MainWindow::MainWindow( | |||
| configureAppearance(); | |||
| configureActions(); | |||
| configurePropertyEditor(); | |||
| configureDataPointEditor(); | |||
| configurePlcConnection(); | |||
| configureHmiEditor(); | |||
| configureLogicEditor(); | |||
| @@ -433,7 +427,6 @@ void MainWindow::configureHmiEditor() | |||
| if (runtime_mode_service_.mode() != ApplicationMode::Editing) | |||
| { | |||
| hmi_editor_widget_->refreshRuntimeValues(); | |||
| refreshDataPointUi(); | |||
| updateSimulationUi(false); | |||
| } | |||
| }); | |||
| @@ -492,62 +485,9 @@ void MainWindow::configurePropertyEditor() | |||
| this, &MainWindow::applySelectedControlProperties); | |||
| connect(ui_->applyLogicPropertiesButton, &QPushButton::clicked, | |||
| this, &MainWindow::applySelectedLogicNodeProperties); | |||
| connect(ui_->hmiDataPointComboBox, qOverload<int>(&QComboBox::currentIndexChanged), | |||
| this, | |||
| [this](int index) | |||
| { | |||
| if (index <= 0) | |||
| { | |||
| return; | |||
| } | |||
| const QString address = ui_->hmiDataPointComboBox->currentData().toString(); | |||
| ui_->bindingAreaComboBox->setCurrentIndex(address.startsWith('M') ? 1 : 2); | |||
| ui_->bindingIndexSpinBox->setValue(address.mid(1).toInt()); | |||
| }); | |||
| connect(ui_->logicDataPointComboBox, qOverload<int>(&QComboBox::currentIndexChanged), | |||
| this, | |||
| [this](int index) | |||
| { | |||
| if (index <= 0) | |||
| { | |||
| return; | |||
| } | |||
| ui_->logicAddressSpinBox->setValue( | |||
| ui_->logicDataPointComboBox->currentData().toString().mid(1).toInt()); | |||
| }); | |||
| showControlProperties({}); | |||
| } | |||
| void MainWindow::configureDataPointEditor() | |||
| { | |||
| ui_->dataPointTable->horizontalHeader()->setSectionResizeMode( | |||
| QHeaderView::ResizeToContents); | |||
| ui_->dataPointTable->horizontalHeader()->setStretchLastSection(true); | |||
| tabifyDockWidget(ui_->outputDock, ui_->dataPointDock); | |||
| ui_->viewMenu->addAction(ui_->dataPointDock->toggleViewAction()); | |||
| connect(ui_->addDataPointButton, &QPushButton::clicked, this, &MainWindow::addDataPoint); | |||
| connect(ui_->updateDataPointButton, &QPushButton::clicked, | |||
| this, &MainWindow::updateSelectedDataPoint); | |||
| connect(ui_->deleteDataPointButton, &QPushButton::clicked, | |||
| this, &MainWindow::deleteSelectedDataPoint); | |||
| connect(ui_->dataPointTable, &QTableWidget::itemSelectionChanged, | |||
| this, | |||
| [this] | |||
| { | |||
| const int row = ui_->dataPointTable->currentRow(); | |||
| if (row < 0) | |||
| { | |||
| return; | |||
| } | |||
| const QString address = ui_->dataPointTable->item(row, 0)->text(); | |||
| ui_->dataPointAreaComboBox->setCurrentIndex(address.startsWith('M') ? 0 : 1); | |||
| ui_->dataPointAddressSpinBox->setValue(address.mid(1).toInt()); | |||
| ui_->dataPointNameEdit->setText(ui_->dataPointTable->item(row, 2)->text()); | |||
| ui_->dataPointCommentEdit->setText(ui_->dataPointTable->item(row, 3)->text()); | |||
| }); | |||
| } | |||
| void MainWindow::configurePlcConnection() | |||
| { | |||
| connect(ui_->configurePlcAction, &QAction::triggered, this, &MainWindow::connectPlc); | |||
| @@ -590,127 +530,6 @@ void MainWindow::refreshProjectUi() | |||
| logic_root, {fromUtf8(logic.name) + suffix})); | |||
| } | |||
| ui_->projectTree->expandAll(); | |||
| refreshDataPointUi(); | |||
| } | |||
| void MainWindow::refreshDataPointUi() | |||
| { | |||
| const auto &points = data_point_service_.dataPoints(); | |||
| ui_->dataPointTable->setRowCount(static_cast<int>(points.size())); | |||
| ui_->hmiDataPointComboBox->blockSignals(true); | |||
| ui_->logicDataPointComboBox->blockSignals(true); | |||
| ui_->hmiDataPointComboBox->clear(); | |||
| ui_->logicDataPointComboBox->clear(); | |||
| ui_->hmiDataPointComboBox->addItem(tr("手动输入")); | |||
| ui_->logicDataPointComboBox->addItem(tr("手动输入")); | |||
| for (int row = 0; row < static_cast<int>(points.size()); ++row) | |||
| { | |||
| const DataPoint &point = points.at(static_cast<std::size_t>(row)); | |||
| const QString address = fromUtf8(point.address.toString()); | |||
| const QString display = tr("%1 - %2").arg(address, fromUtf8(point.name)); | |||
| ui_->hmiDataPointComboBox->addItem(display, address); | |||
| ui_->logicDataPointComboBox->addItem(display, address); | |||
| QString value = QStringLiteral("-"); | |||
| if (runtime_mode_service_.mode() != ApplicationMode::Editing) | |||
| { | |||
| if (point.address.area() == RegisterArea::M) | |||
| { | |||
| const BitReadResult read = hmi_runtime_service_.readBit(point.address); | |||
| value = read.succeeded ? (read.value ? QStringLiteral("ON") : QStringLiteral("OFF")) | |||
| : tr("不可用"); | |||
| } | |||
| else | |||
| { | |||
| const WordReadResult read = hmi_runtime_service_.readWord(point.address); | |||
| value = read.succeeded ? QString::number(read.value) : tr("不可用"); | |||
| } | |||
| } | |||
| QStringList references; | |||
| for (const DataPointReference &reference : data_point_service_.references(point.address)) | |||
| { | |||
| references.push_back(fromUtf8(reference.location)); | |||
| } | |||
| const QStringList columns{ | |||
| address, | |||
| point.address.area() == RegisterArea::M ? tr("位") : tr("16 位字"), | |||
| fromUtf8(point.name), | |||
| fromUtf8(point.comment), | |||
| value, | |||
| references.join(QStringLiteral("; "))}; | |||
| for (int column = 0; column < columns.size(); ++column) | |||
| { | |||
| ui_->dataPointTable->setItem( | |||
| row, column, new QTableWidgetItem(columns.at(column))); | |||
| } | |||
| } | |||
| ui_->hmiDataPointComboBox->blockSignals(false); | |||
| ui_->logicDataPointComboBox->blockSignals(false); | |||
| } | |||
| void MainWindow::addDataPoint() | |||
| { | |||
| const RegisterArea area = ui_->dataPointAreaComboBox->currentIndex() == 0 | |||
| ? RegisterArea::M : RegisterArea::D; | |||
| const DataPointResult result = data_point_service_.add({ | |||
| RegisterAddress{area, ui_->dataPointAddressSpinBox->value()}, | |||
| toUtf8(ui_->dataPointNameEdit->text()), | |||
| toUtf8(ui_->dataPointCommentEdit->text())}); | |||
| if (!result.succeeded) | |||
| { | |||
| showProjectResult(tr("新增数据点"), fromUtf8(result.message), false); | |||
| return; | |||
| } | |||
| refreshProjectUi(); | |||
| statusBar()->showMessage(tr("数据点已新增"), 3000); | |||
| } | |||
| void MainWindow::updateSelectedDataPoint() | |||
| { | |||
| const int row = ui_->dataPointTable->currentRow(); | |||
| if (row < 0) | |||
| { | |||
| statusBar()->showMessage(tr("请先选择要更新的数据点"), 3000); | |||
| return; | |||
| } | |||
| const QString old_text = ui_->dataPointTable->item(row, 0)->text(); | |||
| const RegisterAddress old_address{ | |||
| old_text.startsWith('M') ? RegisterArea::M : RegisterArea::D, | |||
| old_text.mid(1).toInt()}; | |||
| const RegisterArea area = ui_->dataPointAreaComboBox->currentIndex() == 0 | |||
| ? RegisterArea::M : RegisterArea::D; | |||
| const DataPointResult result = data_point_service_.update( | |||
| old_address, | |||
| {RegisterAddress{area, ui_->dataPointAddressSpinBox->value()}, | |||
| toUtf8(ui_->dataPointNameEdit->text()), | |||
| toUtf8(ui_->dataPointCommentEdit->text())}); | |||
| if (!result.succeeded) | |||
| { | |||
| showProjectResult(tr("更新数据点"), fromUtf8(result.message), false); | |||
| return; | |||
| } | |||
| refreshProjectUi(); | |||
| statusBar()->showMessage(tr("数据点已更新"), 3000); | |||
| } | |||
| void MainWindow::deleteSelectedDataPoint() | |||
| { | |||
| const int row = ui_->dataPointTable->currentRow(); | |||
| if (row < 0) | |||
| { | |||
| statusBar()->showMessage(tr("请先选择要删除的数据点"), 3000); | |||
| return; | |||
| } | |||
| const QString address_text = ui_->dataPointTable->item(row, 0)->text(); | |||
| const DataPointResult result = data_point_service_.remove({ | |||
| address_text.startsWith('M') ? RegisterArea::M : RegisterArea::D, | |||
| address_text.mid(1).toInt()}); | |||
| if (!result.succeeded) | |||
| { | |||
| showProjectResult(tr("删除数据点"), fromUtf8(result.message), false); | |||
| return; | |||
| } | |||
| refreshProjectUi(); | |||
| statusBar()->showMessage(tr("数据点已删除"), 3000); | |||
| } | |||
| void MainWindow::connectPlc() | |||
| @@ -1224,7 +1043,6 @@ void MainWindow::updateModeUi(const QString &message) | |||
| } | |||
| ui_->hmiToolBar->setEnabled(policy.allowsProjectEditing); | |||
| ui_->logicToolBar->setEnabled(policy.allowsProjectEditing); | |||
| ui_->dataPointDock->setEnabled(policy.allowsProjectEditing); | |||
| ui_->addButtonAction->setEnabled(policy.allowsProjectEditing); | |||
| ui_->addIndicatorAction->setEnabled(policy.allowsProjectEditing); | |||
| ui_->addNumericDisplayAction->setEnabled(policy.allowsProjectEditing); | |||
| @@ -31,7 +31,6 @@ class RuntimeModeService; | |||
| class ProjectService; | |||
| class HmiEditorService; | |||
| class HmiRuntimeService; | |||
| class DataPointService; | |||
| class HmiEditorWidget; | |||
| class LogicEditorService; | |||
| class LogicEditorWidget; | |||
| @@ -58,7 +57,6 @@ public: | |||
| RuntimeModeService &runtime_mode_service, | |||
| ProjectService &project_service, | |||
| HmiEditorService &hmi_editor_service, | |||
| DataPointService &data_point_service, | |||
| LogicEditorService &logic_editor_service, | |||
| HmiRuntimeService &hmi_runtime_service, | |||
| QWidget *parent = nullptr); | |||
| @@ -76,7 +74,6 @@ private: | |||
| void configureLogicEditor(); | |||
| // 创建并连接控件属性编辑表单 | |||
| void configurePropertyEditor(); | |||
| void configureDataPointEditor(); | |||
| void configurePlcConnection(); | |||
| // 刷新工程树和当前 HMI 页面信息 | |||
| void refreshProjectUi(); | |||
| @@ -102,10 +99,6 @@ private: | |||
| void deleteSelectedLogicObject(); | |||
| // 将逻辑属性表单内容应用到当前选中节点 | |||
| void applySelectedLogicNodeProperties(); | |||
| void refreshDataPointUi(); | |||
| void addDataPoint(); | |||
| void updateSelectedDataPoint(); | |||
| void deleteSelectedDataPoint(); | |||
| void connectPlc(); | |||
| void disconnectPlc(); | |||
| // 创建新的工程并刷新编辑界面 | |||
| @@ -150,7 +143,6 @@ private: | |||
| */ | |||
| ProjectService &project_service_; | |||
| HmiEditorService &hmi_editor_service_; | |||
| DataPointService &data_point_service_; | |||
| LogicEditorService &logic_editor_service_; | |||
| HmiRuntimeService &hmi_runtime_service_; | |||
| QActionGroup *mode_action_group_ = nullptr; | |||
| @@ -41,6 +41,9 @@ | |||
| </property> | |||
| <item> | |||
| <widget class="QTabWidget" name="editorTabWidget"> | |||
| <property name="currentIndex"> | |||
| <number>0</number> | |||
| </property> | |||
| <property name="documentMode"> | |||
| <bool>true</bool> | |||
| </property> | |||
| @@ -214,19 +217,19 @@ | |||
| <x>0</x> | |||
| <y>0</y> | |||
| <width>1280</width> | |||
| <height>23</height> | |||
| <height>17</height> | |||
| </rect> | |||
| </property> | |||
| <widget class="QMenu" name="fileMenu"> | |||
| <property name="title"> | |||
| <string>文件(&F)</string> | |||
| </property> | |||
| <addaction name="newProjectAction"/> | |||
| <addaction name="saveProjectAction"/> | |||
| <addaction name="saveAsProjectAction"/> | |||
| <addaction name="loadProjectAction"/> | |||
| <addaction name="separator"/> | |||
| <addaction name="exitAction"/> | |||
| <addaction name="newProjectAction"/> | |||
| <addaction name="saveProjectAction"/> | |||
| <addaction name="saveAsProjectAction"/> | |||
| <addaction name="loadProjectAction"/> | |||
| <addaction name="separator"/> | |||
| <addaction name="exitAction"/> | |||
| </widget> | |||
| <widget class="QMenu" name="runMenu"> | |||
| <property name="title"> | |||
| @@ -234,10 +237,10 @@ | |||
| </property> | |||
| <addaction name="editingModeAction"/> | |||
| <addaction name="offlineModeAction"/> | |||
| <addaction name="onlineModeAction"/> | |||
| <addaction name="separator"/> | |||
| <addaction name="configurePlcAction"/> | |||
| <addaction name="disconnectPlcAction"/> | |||
| <addaction name="onlineModeAction"/> | |||
| <addaction name="separator"/> | |||
| <addaction name="configurePlcAction"/> | |||
| <addaction name="disconnectPlcAction"/> | |||
| </widget> | |||
| <widget class="QMenu" name="viewMenu"> | |||
| <property name="title"> | |||
| @@ -268,36 +271,52 @@ | |||
| <addaction name="editingModeAction"/> | |||
| <addaction name="offlineModeAction"/> | |||
| <addaction name="onlineModeAction"/> | |||
| <addaction name="configurePlcAction"/> | |||
| <addaction name="disconnectPlcAction"/> | |||
| </widget> | |||
| <widget class="QToolBar" name="hmiToolBar"> | |||
| <property name="windowTitle"><string>HMI 控件</string></property> | |||
| <property name="toolButtonStyle"><enum>Qt::ToolButtonTextBesideIcon</enum></property> | |||
| <attribute name="toolBarArea"><enum>TopToolBarArea</enum></attribute> | |||
| <attribute name="toolBarBreak"><bool>false</bool></attribute> | |||
| <addaction name="addButtonAction"/> | |||
| <addaction name="addIndicatorAction"/> | |||
| <addaction name="addNumericDisplayAction"/> | |||
| <addaction name="addNumericInputAction"/> | |||
| <addaction name="deleteControlAction"/> | |||
| </widget> | |||
| <widget class="QToolBar" name="logicToolBar"> | |||
| <property name="windowTitle"><string>控制逻辑节点</string></property> | |||
| <property name="toolButtonStyle"><enum>Qt::ToolButtonTextBesideIcon</enum></property> | |||
| <attribute name="toolBarArea"><enum>TopToolBarArea</enum></attribute> | |||
| <attribute name="toolBarBreak"><bool>false</bool></attribute> | |||
| <addaction name="addRungAction"/> | |||
| <addaction name="parallelInsertAction"/> | |||
| <addaction name="separator"/> | |||
| <addaction name="addNormallyOpenAction"/> | |||
| <addaction name="addNormallyClosedAction"/> | |||
| <addaction name="addNormalCoilAction"/> | |||
| <addaction name="addSetCoilAction"/> | |||
| <addaction name="addResetCoilAction"/> | |||
| <addaction name="addCompareAction"/> | |||
| <addaction name="deleteLogicAction"/> | |||
| </widget> | |||
| <addaction name="configurePlcAction"/> | |||
| <addaction name="disconnectPlcAction"/> | |||
| </widget> | |||
| <widget class="QToolBar" name="hmiToolBar"> | |||
| <property name="windowTitle"> | |||
| <string>HMI 控件</string> | |||
| </property> | |||
| <property name="toolButtonStyle"> | |||
| <enum>Qt::ToolButtonTextBesideIcon</enum> | |||
| </property> | |||
| <attribute name="toolBarArea"> | |||
| <enum>TopToolBarArea</enum> | |||
| </attribute> | |||
| <attribute name="toolBarBreak"> | |||
| <bool>false</bool> | |||
| </attribute> | |||
| <addaction name="addButtonAction"/> | |||
| <addaction name="addIndicatorAction"/> | |||
| <addaction name="addNumericDisplayAction"/> | |||
| <addaction name="addNumericInputAction"/> | |||
| <addaction name="deleteControlAction"/> | |||
| </widget> | |||
| <widget class="QToolBar" name="logicToolBar"> | |||
| <property name="windowTitle"> | |||
| <string>控制逻辑节点</string> | |||
| </property> | |||
| <property name="toolButtonStyle"> | |||
| <enum>Qt::ToolButtonTextBesideIcon</enum> | |||
| </property> | |||
| <attribute name="toolBarArea"> | |||
| <enum>TopToolBarArea</enum> | |||
| </attribute> | |||
| <attribute name="toolBarBreak"> | |||
| <bool>false</bool> | |||
| </attribute> | |||
| <addaction name="addRungAction"/> | |||
| <addaction name="parallelInsertAction"/> | |||
| <addaction name="separator"/> | |||
| <addaction name="addNormallyOpenAction"/> | |||
| <addaction name="addNormallyClosedAction"/> | |||
| <addaction name="addNormalCoilAction"/> | |||
| <addaction name="addSetCoilAction"/> | |||
| <addaction name="addResetCoilAction"/> | |||
| <addaction name="addCompareAction"/> | |||
| <addaction name="deleteLogicAction"/> | |||
| </widget> | |||
| <widget class="QDockWidget" name="projectDock"> | |||
| <property name="minimumSize"> | |||
| <size> | |||
| @@ -327,10 +346,10 @@ | |||
| </property> | |||
| <item> | |||
| <widget class="QTreeWidget" name="projectTree"> | |||
| <property name="headerHidden"> | |||
| <property name="rootIsDecorated"> | |||
| <bool>true</bool> | |||
| </property> | |||
| <property name="rootIsDecorated"> | |||
| <property name="headerHidden"> | |||
| <bool>true</bool> | |||
| </property> | |||
| <column> | |||
| @@ -393,86 +412,315 @@ | |||
| <rect> | |||
| <x>0</x> | |||
| <y>0</y> | |||
| <width>232</width> | |||
| <height>164</height> | |||
| <width>234</width> | |||
| <height>455</height> | |||
| </rect> | |||
| </property> | |||
| <layout class="QVBoxLayout" name="propertiesPageLayout"> | |||
| <item> | |||
| <layout class="QFormLayout" name="selectionForm"> | |||
| <item row="0" column="0"><widget class="QLabel" name="selectionCaptionLabel"><property name="text"><string>当前对象</string></property></widget></item> | |||
| <item row="0" column="1"><widget class="QLabel" name="selectionValueLabel"><property name="text"><string>未选择</string></property></widget></item> | |||
| </layout> | |||
| </item> | |||
| <item> | |||
| <widget class="QStackedWidget" name="propertyStack"> | |||
| <widget class="QWidget" name="hmiPropertiesPage"> | |||
| <layout class="QFormLayout" name="hmiPropertiesForm"> | |||
| <property name="fieldGrowthPolicy"><enum>QFormLayout::AllNonFixedFieldsGrow</enum></property> | |||
| <item row="0" column="0"><widget class="QLabel" name="controlIdLabel"><property name="text"><string>控件 ID</string></property></widget></item> | |||
| <item row="0" column="1"><widget class="QLineEdit" name="controlIdEdit"/></item> | |||
| <item row="1" column="0"><widget class="QLabel" name="controlTextLabel"><property name="text"><string>显示文本</string></property></widget></item> | |||
| <item row="1" column="1"><widget class="QLineEdit" name="controlTextEdit"/></item> | |||
| <item row="2" column="0"><widget class="QLabel" name="controlXLabel"><property name="text"><string>X</string></property></widget></item> | |||
| <item row="2" column="1"><widget class="QSpinBox" name="controlXSpinBox"><property name="maximum"><number>4000</number></property></widget></item> | |||
| <item row="3" column="0"><widget class="QLabel" name="controlYLabel"><property name="text"><string>Y</string></property></widget></item> | |||
| <item row="3" column="1"><widget class="QSpinBox" name="controlYSpinBox"><property name="maximum"><number>4000</number></property></widget></item> | |||
| <item row="4" column="0"><widget class="QLabel" name="controlWidthLabel"><property name="text"><string>宽度</string></property></widget></item> | |||
| <item row="4" column="1"><widget class="QSpinBox" name="controlWidthSpinBox"><property name="minimum"><number>1</number></property><property name="maximum"><number>4000</number></property></widget></item> | |||
| <item row="5" column="0"><widget class="QLabel" name="controlHeightLabel"><property name="text"><string>高度</string></property></widget></item> | |||
| <item row="5" column="1"><widget class="QSpinBox" name="controlHeightSpinBox"><property name="minimum"><number>1</number></property><property name="maximum"><number>4000</number></property></widget></item> | |||
| <item row="6" column="0"><widget class="QLabel" name="bindingAreaLabel"><property name="text"><string>绑定区域</string></property></widget></item> | |||
| <item row="6" column="1"><widget class="QComboBox" name="bindingAreaComboBox"> | |||
| <item><property name="text"><string>未绑定</string></property><property name="userData"><string notr="true">-1</string></property></item> | |||
| <item><property name="text"><string>M</string></property><property name="userData"><string notr="true">0</string></property></item> | |||
| <item><property name="text"><string>D</string></property><property name="userData"><string notr="true">1</string></property></item> | |||
| </widget></item> | |||
| <item row="7" column="0"><widget class="QLabel" name="bindingIndexLabel"><property name="text"><string>绑定地址</string></property></widget></item> | |||
| <item row="7" column="1"><widget class="QSpinBox" name="bindingIndexSpinBox"><property name="maximum"><number>4000</number></property></widget></item> | |||
| <item row="8" column="0"><widget class="QLabel" name="hmiDataPointLabel"><property name="text"><string>选择数据点</string></property></widget></item> | |||
| <item row="8" column="1"><widget class="QComboBox" name="hmiDataPointComboBox"/></item> | |||
| <item row="9" column="0" colspan="2"><widget class="QPushButton" name="applyPropertiesButton"><property name="text"><string>应用属性</string></property></widget></item> | |||
| </layout> | |||
| <layout class="QVBoxLayout" name="propertiesPageLayout"> | |||
| <item> | |||
| <layout class="QFormLayout" name="selectionForm"> | |||
| <item row="0" column="0"> | |||
| <widget class="QLabel" name="selectionCaptionLabel"> | |||
| <property name="text"> | |||
| <string>当前对象</string> | |||
| </property> | |||
| </widget> | |||
| <widget class="QWidget" name="logicPropertiesPage"> | |||
| <layout class="QFormLayout" name="logicPropertiesForm"> | |||
| <property name="fieldGrowthPolicy"><enum>QFormLayout::AllNonFixedFieldsGrow</enum></property> | |||
| <item row="0" column="0"><widget class="QLabel" name="logicNodeIdLabel"><property name="text"><string>节点 ID</string></property></widget></item> | |||
| <item row="0" column="1"><widget class="QLineEdit" name="logicNodeIdEdit"><property name="readOnly"><bool>true</bool></property></widget></item> | |||
| <item row="1" column="0"><widget class="QLabel" name="logicNodeTypeCaption"><property name="text"><string>节点类型</string></property></widget></item> | |||
| <item row="1" column="1"><widget class="QLabel" name="logicNodeTypeLabel"><property name="text"><string/></property></widget></item> | |||
| <item row="2" column="0"><widget class="QLabel" name="logicAddressAreaCaption"><property name="text"><string>地址区域</string></property></widget></item> | |||
| <item row="2" column="1"><widget class="QLabel" name="logicAddressAreaLabel"><property name="text"><string/></property></widget></item> | |||
| <item row="3" column="0"><widget class="QLabel" name="logicAddressLabel"><property name="text"><string>地址</string></property></widget></item> | |||
| <item row="3" column="1"><widget class="QSpinBox" name="logicAddressSpinBox"><property name="maximum"><number>4000</number></property></widget></item> | |||
| <item row="4" column="0"><widget class="QLabel" name="logicDataPointLabel"><property name="text"><string>选择数据点</string></property></widget></item> | |||
| <item row="4" column="1"><widget class="QComboBox" name="logicDataPointComboBox"/></item> | |||
| <item row="5" column="0"><widget class="QLabel" name="logicModeLabel"><property name="text"><string>工作方式</string></property></widget></item> | |||
| <item row="5" column="1"><widget class="QComboBox" name="logicModeComboBox"/></item> | |||
| <item row="6" column="0"><widget class="QLabel" name="logicComparisonLabel"><property name="text"><string>比较运算</string></property></widget></item> | |||
| <item row="6" column="1"><widget class="QComboBox" name="logicComparisonComboBox"> | |||
| <item><property name="text"><string>等于</string></property><property name="userData"><string notr="true">0</string></property></item> | |||
| <item><property name="text"><string>不等于</string></property><property name="userData"><string notr="true">1</string></property></item> | |||
| <item><property name="text"><string>小于</string></property><property name="userData"><string notr="true">2</string></property></item> | |||
| <item><property name="text"><string>小于等于</string></property><property name="userData"><string notr="true">3</string></property></item> | |||
| <item><property name="text"><string>大于</string></property><property name="userData"><string notr="true">4</string></property></item> | |||
| <item><property name="text"><string>大于等于</string></property><property name="userData"><string notr="true">5</string></property></item> | |||
| </widget></item> | |||
| <item row="7" column="0"><widget class="QLabel" name="logicValueLabel"><property name="text"><string>比较常量</string></property></widget></item> | |||
| <item row="7" column="1"><widget class="QSpinBox" name="logicValueSpinBox"><property name="minimum"><number>-32768</number></property><property name="maximum"><number>32767</number></property></widget></item> | |||
| <item row="8" column="0" colspan="2"><widget class="QPushButton" name="applyLogicPropertiesButton"><property name="text"><string>应用属性</string></property></widget></item> | |||
| </layout> | |||
| </item> | |||
| <item row="0" column="1"> | |||
| <widget class="QLabel" name="selectionValueLabel"> | |||
| <property name="text"> | |||
| <string>未选择</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| </layout> | |||
| </item> | |||
| <item> | |||
| <widget class="QStackedWidget" name="propertyStack"> | |||
| <widget class="QWidget" name="hmiPropertiesPage"> | |||
| <layout class="QFormLayout" name="hmiPropertiesForm"> | |||
| <property name="fieldGrowthPolicy"> | |||
| <enum>QFormLayout::AllNonFixedFieldsGrow</enum> | |||
| </property> | |||
| <item row="0" column="0"> | |||
| <widget class="QLabel" name="controlIdLabel"> | |||
| <property name="text"> | |||
| <string>控件 ID</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="0" column="1"> | |||
| <widget class="QLineEdit" name="controlIdEdit"/> | |||
| </item> | |||
| <item row="1" column="0"> | |||
| <widget class="QLabel" name="controlTextLabel"> | |||
| <property name="text"> | |||
| <string>显示文本</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="1" column="1"> | |||
| <widget class="QLineEdit" name="controlTextEdit"/> | |||
| </item> | |||
| <item row="2" column="0"> | |||
| <widget class="QLabel" name="controlXLabel"> | |||
| <property name="text"> | |||
| <string>X</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="2" column="1"> | |||
| <widget class="QSpinBox" name="controlXSpinBox"> | |||
| <property name="maximum"> | |||
| <number>4000</number> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="3" column="0"> | |||
| <widget class="QLabel" name="controlYLabel"> | |||
| <property name="text"> | |||
| <string>Y</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="3" column="1"> | |||
| <widget class="QSpinBox" name="controlYSpinBox"> | |||
| <property name="maximum"> | |||
| <number>4000</number> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="4" column="0"> | |||
| <widget class="QLabel" name="controlWidthLabel"> | |||
| <property name="text"> | |||
| <string>宽度</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="4" column="1"> | |||
| <widget class="QSpinBox" name="controlWidthSpinBox"> | |||
| <property name="minimum"> | |||
| <number>1</number> | |||
| </property> | |||
| <property name="maximum"> | |||
| <number>4000</number> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="5" column="0"> | |||
| <widget class="QLabel" name="controlHeightLabel"> | |||
| <property name="text"> | |||
| <string>高度</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="5" column="1"> | |||
| <widget class="QSpinBox" name="controlHeightSpinBox"> | |||
| <property name="minimum"> | |||
| <number>1</number> | |||
| </property> | |||
| <property name="maximum"> | |||
| <number>4000</number> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="6" column="0"> | |||
| <widget class="QLabel" name="bindingAreaLabel"> | |||
| <property name="text"> | |||
| <string>绑定区域</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="6" column="1"> | |||
| <widget class="QComboBox" name="bindingAreaComboBox"> | |||
| <item> | |||
| <property name="text"> | |||
| <string>未绑定</string> | |||
| </property> | |||
| </item> | |||
| <item> | |||
| <property name="text"> | |||
| <string>M</string> | |||
| </property> | |||
| </item> | |||
| <item> | |||
| <property name="text"> | |||
| <string>D</string> | |||
| </property> | |||
| </item> | |||
| </widget> | |||
| </item> | |||
| <item row="7" column="0"> | |||
| <widget class="QLabel" name="bindingIndexLabel"> | |||
| <property name="text"> | |||
| <string>绑定地址</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="7" column="1"> | |||
| <widget class="QSpinBox" name="bindingIndexSpinBox"> | |||
| <property name="maximum"> | |||
| <number>4000</number> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="8" column="0" colspan="2"> | |||
| <widget class="QPushButton" name="applyPropertiesButton"> | |||
| <property name="text"> | |||
| <string>应用属性</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| </layout> | |||
| </widget> | |||
| <widget class="QWidget" name="logicPropertiesPage"> | |||
| <layout class="QFormLayout" name="logicPropertiesForm"> | |||
| <property name="fieldGrowthPolicy"> | |||
| <enum>QFormLayout::AllNonFixedFieldsGrow</enum> | |||
| </property> | |||
| <item row="0" column="0"> | |||
| <widget class="QLabel" name="logicNodeIdLabel"> | |||
| <property name="text"> | |||
| <string>节点 ID</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="0" column="1"> | |||
| <widget class="QLineEdit" name="logicNodeIdEdit"> | |||
| <property name="readOnly"> | |||
| <bool>true</bool> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="1" column="0"> | |||
| <widget class="QLabel" name="logicNodeTypeCaption"> | |||
| <property name="text"> | |||
| <string>节点类型</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="1" column="1"> | |||
| <widget class="QLabel" name="logicNodeTypeLabel"> | |||
| <property name="text"> | |||
| <string/> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="2" column="0"> | |||
| <widget class="QLabel" name="logicAddressAreaCaption"> | |||
| <property name="text"> | |||
| <string>地址区域</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="2" column="1"> | |||
| <widget class="QLabel" name="logicAddressAreaLabel"> | |||
| <property name="text"> | |||
| <string/> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="3" column="0"> | |||
| <widget class="QLabel" name="logicAddressLabel"> | |||
| <property name="text"> | |||
| <string>地址</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="3" column="1"> | |||
| <widget class="QSpinBox" name="logicAddressSpinBox"> | |||
| <property name="maximum"> | |||
| <number>4000</number> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="4" column="0"> | |||
| <widget class="QLabel" name="logicModeLabel"> | |||
| <property name="text"> | |||
| <string>工作方式</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="4" column="1"> | |||
| <widget class="QComboBox" name="logicModeComboBox"/> | |||
| </item> | |||
| <item row="5" column="0"> | |||
| <widget class="QLabel" name="logicComparisonLabel"> | |||
| <property name="text"> | |||
| <string>比较运算</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="5" column="1"> | |||
| <widget class="QComboBox" name="logicComparisonComboBox"> | |||
| <item> | |||
| <property name="text"> | |||
| <string>等于</string> | |||
| </property> | |||
| </item> | |||
| <item> | |||
| <property name="text"> | |||
| <string>不等于</string> | |||
| </property> | |||
| </item> | |||
| <item> | |||
| <property name="text"> | |||
| <string>小于</string> | |||
| </property> | |||
| </item> | |||
| <item> | |||
| <property name="text"> | |||
| <string>小于等于</string> | |||
| </property> | |||
| </item> | |||
| <item> | |||
| <property name="text"> | |||
| <string>大于</string> | |||
| </property> | |||
| </item> | |||
| <item> | |||
| <property name="text"> | |||
| <string>大于等于</string> | |||
| </property> | |||
| </item> | |||
| </widget> | |||
| </item> | |||
| <item row="6" column="0"> | |||
| <widget class="QLabel" name="logicValueLabel"> | |||
| <property name="text"> | |||
| <string>比较常量</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="6" column="1"> | |||
| <widget class="QSpinBox" name="logicValueSpinBox"> | |||
| <property name="minimum"> | |||
| <number>-32768</number> | |||
| </property> | |||
| <property name="maximum"> | |||
| <number>32767</number> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| <item row="7" column="0" colspan="2"> | |||
| <widget class="QPushButton" name="applyLogicPropertiesButton"> | |||
| <property name="text"> | |||
| <string>应用属性</string> | |||
| </property> | |||
| </widget> | |||
| </item> | |||
| </layout> | |||
| </widget> | |||
| </item> | |||
| </layout> | |||
| </widget> | |||
| </item> | |||
| </layout> | |||
| </widget> | |||
| </widget> | |||
| </item> | |||
| </layout> | |||
| </widget> | |||
| </widget> | |||
| <widget class="QDockWidget" name="outputDock"> | |||
| <widget class="QDockWidget" name="outputDock"> | |||
| <property name="minimumSize"> | |||
| <size> | |||
| <width>300</width> | |||
| @@ -509,74 +757,161 @@ | |||
| </widget> | |||
| </item> | |||
| </layout> | |||
| </widget> | |||
| </widget> | |||
| <widget class="QDockWidget" name="dataPointDock"> | |||
| <property name="minimumSize"><size><width>420</width><height>170</height></size></property> | |||
| <property name="windowTitle"><string>数据点</string></property> | |||
| <attribute name="dockWidgetArea"><number>8</number></attribute> | |||
| <widget class="QWidget" name="dataPointDockContents"> | |||
| <layout class="QVBoxLayout" name="dataPointDockLayout"> | |||
| <property name="leftMargin"><number>6</number></property> | |||
| <property name="topMargin"><number>6</number></property> | |||
| <property name="rightMargin"><number>6</number></property> | |||
| <property name="bottomMargin"><number>6</number></property> | |||
| <item> | |||
| <widget class="QTableWidget" name="dataPointTable"> | |||
| <property name="selectionMode"><enum>QAbstractItemView::SingleSelection</enum></property> | |||
| <property name="selectionBehavior"><enum>QAbstractItemView::SelectRows</enum></property> | |||
| <property name="editTriggers"><set>QAbstractItemView::NoEditTriggers</set></property> | |||
| <column><property name="text"><string>地址</string></property></column> | |||
| <column><property name="text"><string>类型</string></property></column> | |||
| <column><property name="text"><string>名称</string></property></column> | |||
| <column><property name="text"><string>注释</string></property></column> | |||
| <column><property name="text"><string>当前值</string></property></column> | |||
| <column><property name="text"><string>引用位置</string></property></column> | |||
| </widget> | |||
| </item> | |||
| <item> | |||
| <layout class="QHBoxLayout" name="dataPointEditorLayout"> | |||
| <item><widget class="QLabel" name="dataPointAreaLabel"><property name="text"><string>区域</string></property></widget></item> | |||
| <item><widget class="QComboBox" name="dataPointAreaComboBox"><item><property name="text"><string>M</string></property><property name="userData"><string notr="true">0</string></property></item><item><property name="text"><string>D</string></property><property name="userData"><string notr="true">1</string></property></item></widget></item> | |||
| <item><widget class="QLabel" name="dataPointAddressLabel"><property name="text"><string>地址</string></property></widget></item> | |||
| <item><widget class="QSpinBox" name="dataPointAddressSpinBox"><property name="maximum"><number>4000</number></property></widget></item> | |||
| <item><widget class="QLabel" name="dataPointNameLabel"><property name="text"><string>名称</string></property></widget></item> | |||
| <item><widget class="QLineEdit" name="dataPointNameEdit"/></item> | |||
| <item><widget class="QLabel" name="dataPointCommentLabel"><property name="text"><string>注释</string></property></widget></item> | |||
| <item><widget class="QLineEdit" name="dataPointCommentEdit"/></item> | |||
| <item><widget class="QPushButton" name="addDataPointButton"><property name="text"><string>新增</string></property></widget></item> | |||
| <item><widget class="QPushButton" name="updateDataPointButton"><property name="text"><string>更新</string></property></widget></item> | |||
| <item><widget class="QPushButton" name="deleteDataPointButton"><property name="text"><string>删除</string></property></widget></item> | |||
| </layout> | |||
| </item> | |||
| </layout> | |||
| </widget> | |||
| </widget> | |||
| <action name="exitAction"> | |||
| </widget> | |||
| <action name="exitAction"> | |||
| <property name="text"> | |||
| <string>退出(&X)</string> | |||
| </property> | |||
| </action> | |||
| <action name="newProjectAction"><property name="text"><string>新建工程</string></property></action> | |||
| <action name="saveProjectAction"><property name="text"><string>保存工程</string></property></action> | |||
| <action name="saveAsProjectAction"><property name="text"><string>工程另存为</string></property></action> | |||
| <action name="loadProjectAction"><property name="text"><string>加载工程</string></property></action> | |||
| <action name="configurePlcAction"><property name="text"><string>PLC 配置</string></property><property name="toolTip"><string>配置参数并连接真实 PLC</string></property></action> | |||
| <action name="disconnectPlcAction"><property name="text"><string>断开 PLC</string></property><property name="toolTip"><string>断开当前 PLC 连接</string></property></action> | |||
| <action name="addButtonAction"><property name="text"><string>按钮</string></property><property name="toolTip"><string>添加按钮控件</string></property></action> | |||
| <action name="addIndicatorAction"><property name="text"><string>指示灯</string></property><property name="toolTip"><string>添加指示灯控件</string></property></action> | |||
| <action name="addNumericDisplayAction"><property name="text"><string>数值显示</string></property><property name="toolTip"><string>添加数值显示控件</string></property></action> | |||
| <action name="addNumericInputAction"><property name="text"><string>数值输入</string></property><property name="toolTip"><string>添加数值输入控件</string></property></action> | |||
| <action name="deleteControlAction"><property name="text"><string>删除</string></property><property name="toolTip"><string>删除当前控件</string></property></action> | |||
| <action name="addRungAction"><property name="text"><string>新建网络</string></property><property name="toolTip"><string>在梯形图末尾新增网络</string></property></action> | |||
| <action name="parallelInsertAction"><property name="text"><string>并联支路</string></property><property name="toolTip"><string>为画布中选中的连续节点建立并联支路</string></property></action> | |||
| <action name="addNormallyOpenAction"><property name="text"><string>常开</string></property><property name="toolTip"><string>向当前网络添加常开触点</string></property></action> | |||
| <action name="addNormallyClosedAction"><property name="text"><string>常闭</string></property><property name="toolTip"><string>向当前网络添加常闭触点</string></property></action> | |||
| <action name="addNormalCoilAction"><property name="text"><string>线圈</string></property><property name="toolTip"><string>设置当前网络的普通线圈</string></property></action> | |||
| <action name="addSetCoilAction"><property name="text"><string>置位</string></property><property name="toolTip"><string>设置当前网络的置位线圈</string></property></action> | |||
| <action name="addResetCoilAction"><property name="text"><string>复位</string></property><property name="toolTip"><string>设置当前网络的复位线圈</string></property></action> | |||
| <action name="addCompareAction"><property name="text"><string>D 比较</string></property><property name="toolTip"><string>向当前网络添加 D 值与常量比较</string></property></action> | |||
| <action name="deleteLogicAction"><property name="text"><string>删除</string></property><property name="toolTip"><string>删除选中的逻辑节点或网络</string></property></action> | |||
| </action> | |||
| <action name="newProjectAction"> | |||
| <property name="text"> | |||
| <string>新建工程</string> | |||
| </property> | |||
| </action> | |||
| <action name="saveProjectAction"> | |||
| <property name="text"> | |||
| <string>保存工程</string> | |||
| </property> | |||
| </action> | |||
| <action name="saveAsProjectAction"> | |||
| <property name="text"> | |||
| <string>工程另存为</string> | |||
| </property> | |||
| </action> | |||
| <action name="loadProjectAction"> | |||
| <property name="text"> | |||
| <string>加载工程</string> | |||
| </property> | |||
| </action> | |||
| <action name="configurePlcAction"> | |||
| <property name="text"> | |||
| <string>PLC 配置</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>配置参数并连接真实 PLC</string> | |||
| </property> | |||
| </action> | |||
| <action name="disconnectPlcAction"> | |||
| <property name="text"> | |||
| <string>断开 PLC</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>断开当前 PLC 连接</string> | |||
| </property> | |||
| </action> | |||
| <action name="addButtonAction"> | |||
| <property name="text"> | |||
| <string>按钮</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>添加按钮控件</string> | |||
| </property> | |||
| </action> | |||
| <action name="addIndicatorAction"> | |||
| <property name="text"> | |||
| <string>指示灯</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>添加指示灯控件</string> | |||
| </property> | |||
| </action> | |||
| <action name="addNumericDisplayAction"> | |||
| <property name="text"> | |||
| <string>数值显示</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>添加数值显示控件</string> | |||
| </property> | |||
| </action> | |||
| <action name="addNumericInputAction"> | |||
| <property name="text"> | |||
| <string>数值输入</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>添加数值输入控件</string> | |||
| </property> | |||
| </action> | |||
| <action name="deleteControlAction"> | |||
| <property name="text"> | |||
| <string>删除</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>删除当前控件</string> | |||
| </property> | |||
| </action> | |||
| <action name="addRungAction"> | |||
| <property name="text"> | |||
| <string>新建网络</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>在梯形图末尾新增网络</string> | |||
| </property> | |||
| </action> | |||
| <action name="parallelInsertAction"> | |||
| <property name="text"> | |||
| <string>并联支路</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>为画布中选中的连续节点建立并联支路</string> | |||
| </property> | |||
| </action> | |||
| <action name="addNormallyOpenAction"> | |||
| <property name="text"> | |||
| <string>常开</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>向当前网络添加常开触点</string> | |||
| </property> | |||
| </action> | |||
| <action name="addNormallyClosedAction"> | |||
| <property name="text"> | |||
| <string>常闭</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>向当前网络添加常闭触点</string> | |||
| </property> | |||
| </action> | |||
| <action name="addNormalCoilAction"> | |||
| <property name="text"> | |||
| <string>线圈</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>设置当前网络的普通线圈</string> | |||
| </property> | |||
| </action> | |||
| <action name="addSetCoilAction"> | |||
| <property name="text"> | |||
| <string>置位</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>设置当前网络的置位线圈</string> | |||
| </property> | |||
| </action> | |||
| <action name="addResetCoilAction"> | |||
| <property name="text"> | |||
| <string>复位</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>设置当前网络的复位线圈</string> | |||
| </property> | |||
| </action> | |||
| <action name="addCompareAction"> | |||
| <property name="text"> | |||
| <string>D 比较</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>向当前网络添加 D 值与常量比较</string> | |||
| </property> | |||
| </action> | |||
| <action name="deleteLogicAction"> | |||
| <property name="text"> | |||
| <string>删除</string> | |||
| </property> | |||
| <property name="toolTip"> | |||
| <string>删除选中的逻辑节点或网络</string> | |||
| </property> | |||
| </action> | |||
| <action name="editingModeAction"> | |||
| <property name="checkable"> | |||
| <bool>true</bool> | |||
| @@ -1,107 +0,0 @@ | |||
| #include "domain/project_storage.h" | |||
| #include "services/data_point_service.h" | |||
| #include "services/project_service.h" | |||
| #include <iostream> | |||
| #include <stdexcept> | |||
| namespace { | |||
| class TestProjectStorage final : public ProjectStorage | |||
| { | |||
| public: | |||
| ProjectSaveResult save(const Project &, const std::string &) override | |||
| { | |||
| return {true, ProjectStorageError::None, {}}; | |||
| } | |||
| ProjectLoadResult load(const std::string &) override | |||
| { | |||
| return {false, {}, ProjectStorageError::FileReadFailed, {}}; | |||
| } | |||
| }; | |||
| void require(bool condition, const std::string &message) | |||
| { | |||
| if (!condition) | |||
| { | |||
| throw std::runtime_error(message); | |||
| } | |||
| } | |||
| void testUniqueDataPointsAndReferences() | |||
| { | |||
| TestProjectStorage storage; | |||
| ProjectService project_service(storage); | |||
| DataPointService service(project_service); | |||
| const RegisterAddress m0{RegisterArea::M, 0}; | |||
| const RegisterAddress m1{RegisterArea::M, 1}; | |||
| const RegisterAddress d0{RegisterArea::D, 0}; | |||
| require(service.add({m0, "StartCommand", "启动命令"}).succeeded, | |||
| "a valid data point must be added"); | |||
| require(service.add({d0, "Temperature", "温度"}).succeeded, | |||
| "M and D data points must coexist"); | |||
| require(service.add({m0, "DuplicateAddress", {}}).error | |||
| == DataPointError::DuplicateAddress, | |||
| "duplicate data point addresses must be rejected"); | |||
| require(service.add({m1, "StartCommand", {}}).error | |||
| == DataPointError::DuplicateName, | |||
| "duplicate data point names must be rejected"); | |||
| HmiControl button; | |||
| button.id = "start-button"; | |||
| button.type = HmiControlType::Button; | |||
| button.bounds = {0, 0, 100, 40}; | |||
| button.text = "启动"; | |||
| button.binding = m0; | |||
| HmiPage page; | |||
| page.id = "main-page"; | |||
| page.name = "主画面"; | |||
| page.controls.push_back(button); | |||
| project_service.editProject().hmiPages.push_back(page); | |||
| LogicNode contact; | |||
| contact.id = "start-contact"; | |||
| contact.config = ContactNodeConfig{m0, ContactMode::NormallyOpen}; | |||
| LadderRung rung; | |||
| rung.id = "rung-1"; | |||
| rung.name = "网络 1"; | |||
| rung.condition = ConditionExpression::fromNode(contact); | |||
| ControlLogic logic; | |||
| logic.id = "logic-1"; | |||
| logic.name = "控制逻辑 1"; | |||
| logic.rungs.push_back(rung); | |||
| project_service.editProject().controlLogics.push_back(logic); | |||
| const auto references = service.references(m0); | |||
| require(references.size() == 2U, | |||
| "data point references must include HMI and ladder locations"); | |||
| require(service.remove(m0).error == DataPointError::AddressInUse, | |||
| "referenced data points must not be deleted"); | |||
| require(service.update(m0, {m1, "StartCommand", "启动命令"}).error | |||
| == DataPointError::AddressInUse, | |||
| "referenced data point addresses must not change"); | |||
| require(service.update(m0, {m0, "Start", "启动命令"}).succeeded, | |||
| "referenced data point names and comments may change"); | |||
| require(service.remove(RegisterAddress{RegisterArea::D, 99}).error | |||
| == DataPointError::NotFound, | |||
| "removing an unknown data point must report not found"); | |||
| } | |||
| } // namespace | |||
| int main() | |||
| { | |||
| try | |||
| { | |||
| testUniqueDataPointsAndReferences(); | |||
| } | |||
| catch (const std::exception &error) | |||
| { | |||
| std::cerr << "data point service tests failed: " << error.what() << '\n'; | |||
| return 1; | |||
| } | |||
| std::cout << "data point service tests passed\n"; | |||
| return 0; | |||
| } | |||
| @@ -1,29 +0,0 @@ | |||
| TEMPLATE = app | |||
| TARGET = data_point_service_tests | |||
| CONFIG += console c++17 testcase warn_on | |||
| CONFIG -= app_bundle qt | |||
| INCLUDEPATH += ../src | |||
| SOURCES += \ | |||
| data_point_service_tests.cpp \ | |||
| ../src/domain/register_address.cpp \ | |||
| ../src/domain/register_repository.cpp \ | |||
| ../src/domain/data_point_model.cpp \ | |||
| ../src/domain/hmi_model.cpp \ | |||
| ../src/domain/control_logic_model.cpp \ | |||
| ../src/domain/project_model.cpp \ | |||
| ../src/services/project_service.cpp \ | |||
| ../src/services/data_point_service.cpp | |||
| HEADERS += \ | |||
| ../src/domain/register_address.h \ | |||
| ../src/domain/register_repository.h \ | |||
| ../src/domain/data_point_model.h \ | |||
| ../src/domain/hmi_model.h \ | |||
| ../src/domain/control_logic_model.h \ | |||
| ../src/domain/project_model.h \ | |||
| ../src/domain/project_storage.h \ | |||
| ../src/services/project_service.h \ | |||
| ../src/services/data_point_service.h | |||
| @@ -10,7 +10,6 @@ SOURCES += \ | |||
| domain_tests.cpp \ | |||
| ../src/domain/register_address.cpp \ | |||
| ../src/domain/register_repository.cpp \ | |||
| ../src/domain/data_point_model.cpp \ | |||
| ../src/domain/hmi_model.cpp \ | |||
| ../src/domain/control_logic_model.cpp \ | |||
| ../src/domain/project_model.cpp \ | |||
| @@ -19,7 +18,6 @@ SOURCES += \ | |||
| HEADERS += \ | |||
| ../src/domain/register_address.h \ | |||
| ../src/domain/register_repository.h \ | |||
| ../src/domain/data_point_model.h \ | |||
| ../src/domain/hmi_model.h \ | |||
| ../src/domain/control_logic_model.h \ | |||
| ../src/domain/project_model.h \ | |||
| @@ -10,7 +10,6 @@ SOURCES += \ | |||
| hmi_editor_service_tests.cpp \ | |||
| ../src/domain/register_address.cpp \ | |||
| ../src/domain/register_repository.cpp \ | |||
| ../src/domain/data_point_model.cpp \ | |||
| ../src/domain/hmi_model.cpp \ | |||
| ../src/domain/control_logic_model.cpp \ | |||
| ../src/domain/project_model.cpp \ | |||
| @@ -21,7 +20,6 @@ SOURCES += \ | |||
| HEADERS += \ | |||
| ../src/domain/register_address.h \ | |||
| ../src/domain/register_repository.h \ | |||
| ../src/domain/data_point_model.h \ | |||
| ../src/domain/hmi_model.h \ | |||
| ../src/domain/control_logic_model.h \ | |||
| ../src/domain/project_model.h \ | |||
| @@ -9,7 +9,6 @@ INCLUDEPATH += ../src | |||
| SOURCES += \ | |||
| logic_editor_service_tests.cpp \ | |||
| ../src/domain/register_address.cpp \ | |||
| ../src/domain/data_point_model.cpp \ | |||
| ../src/domain/control_logic_model.cpp \ | |||
| ../src/domain/project_model.cpp \ | |||
| ../src/domain/hmi_model.cpp \ | |||
| @@ -19,7 +18,6 @@ SOURCES += \ | |||
| HEADERS += \ | |||
| ../src/domain/register_address.h \ | |||
| ../src/domain/data_point_model.h \ | |||
| ../src/domain/control_logic_model.h \ | |||
| ../src/domain/project_model.h \ | |||
| ../src/domain/hmi_model.h \ | |||
| @@ -2,7 +2,6 @@ | |||
| #include "domain/register_repository.h" | |||
| #include "services/hmi_editor_service.h" | |||
| #include "services/hmi_runtime_service.h" | |||
| #include "services/data_point_service.h" | |||
| #include "services/logic_editor_service.h" | |||
| #include "services/offline_simulation_service.h" | |||
| #include "services/project_service.h" | |||
| @@ -66,7 +65,6 @@ void testModeActionsControlEditingAvailability() | |||
| TestProjectStorage storage; | |||
| ProjectService project_service(storage); | |||
| HmiEditorService editor_service(project_service); | |||
| DataPointService data_point_service(project_service); | |||
| LogicEditorService logic_editor_service(project_service); | |||
| VirtualRegisterRepository repository; | |||
| HmiRuntimeService runtime_service(repository); | |||
| @@ -76,13 +74,18 @@ void testModeActionsControlEditingAvailability() | |||
| mode_service, | |||
| project_service, | |||
| editor_service, | |||
| data_point_service, | |||
| logic_editor_service, | |||
| runtime_service); | |||
| window.resize(1000, 640); | |||
| window.show(); | |||
| QApplication::processEvents(); | |||
| require(window.findChild<QWidget *>(QStringLiteral("dataPointDock")) == nullptr, | |||
| "the removed data point dock must not remain in the main window"); | |||
| require(window.findChild<QWidget *>(QStringLiteral("hmiDataPointComboBox")) == nullptr | |||
| && window.findChild<QWidget *>(QStringLiteral("logicDataPointComboBox")) == nullptr, | |||
| "HMI and ladder properties must use direct M/D address inputs"); | |||
| QAction *editing_action = requiredChild<QAction>(window, "editingModeAction"); | |||
| QAction *offline_action = requiredChild<QAction>(window, "offlineModeAction"); | |||
| QAction *online_action = requiredChild<QAction>(window, "onlineModeAction"); | |||
| @@ -238,7 +241,6 @@ void testPlcConfigurationUsesDialog() | |||
| TestProjectStorage storage; | |||
| ProjectService project_service(storage); | |||
| HmiEditorService editor_service(project_service); | |||
| DataPointService data_point_service(project_service); | |||
| LogicEditorService logic_editor_service(project_service); | |||
| VirtualRegisterRepository repository; | |||
| HmiRuntimeService runtime_service(repository); | |||
| @@ -248,7 +250,6 @@ void testPlcConfigurationUsesDialog() | |||
| mode_service, | |||
| project_service, | |||
| editor_service, | |||
| data_point_service, | |||
| logic_editor_service, | |||
| runtime_service); | |||
| require(window.findChild<QComboBox *>(QStringLiteral("serialPortComboBox")) == nullptr, | |||
| @@ -17,13 +17,11 @@ SOURCES += \ | |||
| ../src/domain/register_address.cpp \ | |||
| ../src/domain/register_repository.cpp \ | |||
| ../src/domain/active_register_repository.cpp \ | |||
| ../src/domain/data_point_model.cpp \ | |||
| ../src/domain/hmi_model.cpp \ | |||
| ../src/domain/control_logic_model.cpp \ | |||
| ../src/domain/project_model.cpp \ | |||
| ../src/domain/runtime_state.cpp \ | |||
| ../src/services/project_service.cpp \ | |||
| ../src/services/data_point_service.cpp \ | |||
| ../src/services/hmi_editor_service.cpp \ | |||
| ../src/services/logic_editor_service.cpp \ | |||
| ../src/services/hmi_runtime_service.cpp \ | |||
| @@ -39,14 +37,12 @@ HEADERS += \ | |||
| ../src/domain/register_address.h \ | |||
| ../src/domain/register_repository.h \ | |||
| ../src/domain/active_register_repository.h \ | |||
| ../src/domain/data_point_model.h \ | |||
| ../src/domain/hmi_model.h \ | |||
| ../src/domain/control_logic_model.h \ | |||
| ../src/domain/project_model.h \ | |||
| ../src/domain/project_storage.h \ | |||
| ../src/domain/runtime_state.h \ | |||
| ../src/services/project_service.h \ | |||
| ../src/services/data_point_service.h \ | |||
| ../src/services/hmi_editor_service.h \ | |||
| ../src/services/logic_editor_service.h \ | |||
| ../src/services/hmi_runtime_service.h \ | |||
| @@ -154,8 +154,17 @@ void testRuntimeRepositorySwitchingAndDisconnect() | |||
| { | |||
| TestProjectStorage storage; | |||
| ProjectService project_service(storage); | |||
| project_service.editProject().dataPoints.push_back( | |||
| {{RegisterArea::M, 5}, "RunState", "运行状态"}); | |||
| HmiPage page; | |||
| page.id = "main-page"; | |||
| page.name = "Main"; | |||
| HmiControl indicator; | |||
| indicator.id = "run-state"; | |||
| indicator.type = HmiControlType::Indicator; | |||
| indicator.bounds = {0, 0, 80, 40}; | |||
| indicator.text = "Run"; | |||
| indicator.binding = RegisterAddress{RegisterArea::M, 5}; | |||
| page.controls.push_back(indicator); | |||
| project_service.editProject().hmiPages.push_back(page); | |||
| VirtualRegisterRepository virtual_repository; | |||
| PlcRegisterRepository plc_repository; | |||
| ActiveRegisterRepository active_repository(virtual_repository); | |||
| @@ -13,7 +13,6 @@ SOURCES += \ | |||
| ../src/domain/register_address.cpp \ | |||
| ../src/domain/register_repository.cpp \ | |||
| ../src/domain/active_register_repository.cpp \ | |||
| ../src/domain/data_point_model.cpp \ | |||
| ../src/domain/hmi_model.cpp \ | |||
| ../src/domain/control_logic_model.cpp \ | |||
| ../src/domain/project_model.cpp \ | |||
| @@ -28,7 +27,6 @@ HEADERS += \ | |||
| ../src/domain/register_address.h \ | |||
| ../src/domain/register_repository.h \ | |||
| ../src/domain/active_register_repository.h \ | |||
| ../src/domain/data_point_model.h \ | |||
| ../src/domain/hmi_model.h \ | |||
| ../src/domain/control_logic_model.h \ | |||
| ../src/domain/project_model.h \ | |||
| @@ -115,9 +115,6 @@ Project makeExampleProject() | |||
| project.metadata = {"example-project", "Example project", "1.0"}; | |||
| project.hmiPages.push_back(page); | |||
| project.controlLogics.push_back(logic); | |||
| project.dataPoints = { | |||
| {RegisterAddress{RegisterArea::M, 0}, "StartCommand", "启动命令"}, | |||
| {RegisterAddress{RegisterArea::D, 2}, "Temperature", "当前温度"}}; | |||
| return project; | |||
| } | |||
| @@ -179,9 +176,10 @@ void testExampleProjectRoundTrip() | |||
| require(saved_json.contains("\"rungs\"") | |||
| && saved_json.contains("\"condition\"") | |||
| && saved_json.contains("\"children\"") | |||
| && saved_json.contains("\"dataPoints\"") | |||
| && saved_json.contains("\"output\""), | |||
| "saved project must use structured ladder expressions and data points"); | |||
| "saved project must use structured ladder expressions"); | |||
| require(!saved_json.contains("\"dataPoints\""), | |||
| "saved project must not contain the removed data point model"); | |||
| require(!saved_json.contains("\"stages\"") | |||
| && !saved_json.contains("\"branches\""), | |||
| "current project format must not contain the removed stage model"); | |||
| @@ -226,10 +224,6 @@ void testExampleProjectRoundTrip() | |||
| rung.condition->children.at(1).node->config); | |||
| require(compare.address.index() == 2 && compare.value == 100, | |||
| "comparison configuration must survive round trip"); | |||
| require(project.dataPoints.size() == 2 | |||
| && project.dataPoints.front().name == "StartCommand", | |||
| "data points must survive round trip"); | |||
| require(service.saveAs(second_path.toStdString()).succeeded, | |||
| "save as must succeed after load"); | |||
| require(readBytes(first_path) == readBytes(second_path), | |||
| @@ -12,7 +12,6 @@ SOURCES += \ | |||
| project_management_tests.cpp \ | |||
| ../src/domain/register_address.cpp \ | |||
| ../src/domain/register_repository.cpp \ | |||
| ../src/domain/data_point_model.cpp \ | |||
| ../src/domain/hmi_model.cpp \ | |||
| ../src/domain/control_logic_model.cpp \ | |||
| ../src/domain/project_model.cpp \ | |||
| @@ -23,7 +22,6 @@ SOURCES += \ | |||
| HEADERS += \ | |||
| ../src/domain/register_address.h \ | |||
| ../src/domain/register_repository.h \ | |||
| ../src/domain/data_point_model.h \ | |||
| ../src/domain/hmi_model.h \ | |||
| ../src/domain/control_logic_model.h \ | |||
| ../src/domain/project_model.h \ | |||
| @@ -13,7 +13,6 @@ SOURCES += \ | |||
| ../src/domain/register_address.cpp \ | |||
| ../src/domain/register_repository.cpp \ | |||
| ../src/domain/active_register_repository.cpp \ | |||
| ../src/domain/data_point_model.cpp \ | |||
| ../src/domain/hmi_model.cpp \ | |||
| ../src/domain/control_logic_model.cpp \ | |||
| ../src/domain/project_model.cpp \ | |||
| @@ -27,7 +26,6 @@ HEADERS += \ | |||
| ../src/domain/register_address.h \ | |||
| ../src/domain/register_repository.h \ | |||
| ../src/domain/active_register_repository.h \ | |||
| ../src/domain/data_point_model.h \ | |||
| ../src/domain/hmi_model.h \ | |||
| ../src/domain/control_logic_model.h \ | |||
| ../src/domain/project_model.h \ | |||