|
- #include "domain/project_storage.h"
- #include "domain/register_repository.h"
- #include "services/hmi_editor_service.h"
- #include "services/hmi_runtime_service.h"
- #include "services/project_service.h"
-
- #include <exception>
- #include <iostream>
- #include <stdexcept>
- #include <string>
-
- 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 testControlEditing()
- {
- // 覆盖控件创建、移动、绑定校验、重命名冲突和删除流程
- TestProjectStorage storage;
- ProjectService project_service(storage);
- HmiEditorService service(project_service);
- const HmiEditorResult page_result = service.ensureDefaultPage();
- require(page_result.succeeded, "default HMI page creation must succeed");
-
- const std::string page_id = page_result.id;
- const HmiEditorResult button = service.addControl(page_id, HmiControlType::Button);
- const HmiEditorResult indicator = service.addControl(page_id, HmiControlType::Indicator);
- const HmiEditorResult display = service.addControl(
- page_id, HmiControlType::NumericDisplay);
- const HmiEditorResult input = service.addControl(page_id, HmiControlType::NumericInput);
- require(button.succeeded && indicator.succeeded && display.succeeded && input.succeeded,
- "four basic HMI controls must be added");
-
- const HmiPage *page = service.findPage(page_id);
- require(page != nullptr && page->controls.size() == 4,
- "all added controls must be kept in the page model");
- require(service.moveControl(page_id, button.id, {120, 80, 120, 40}).succeeded,
- "a valid control move must succeed");
- require(!service.moveControl(page_id, button.id, {790, 460, 120, 40}).succeeded,
- "a move outside the page must be rejected");
-
- HmiControl updated = *service.findControl(page_id, button.id);
- updated.binding = RegisterAddress{RegisterArea::D, 0};
- require(!service.updateControl(page_id, button.id, updated).succeeded,
- "buttons must reject D address bindings during editing");
- updated.binding = RegisterAddress{RegisterArea::M, 7};
- updated.text = "启动";
- require(service.updateControl(page_id, button.id, updated).succeeded,
- "a button M binding and edited text must be accepted");
-
- HmiControl duplicate = *service.findControl(page_id, indicator.id);
- duplicate.id = button.id;
- require(service.updateControl(page_id, indicator.id, duplicate).error
- == HmiEditorError::DuplicateId,
- "duplicate control ids must be rejected");
-
- require(service.removeControl(page_id, indicator.id).succeeded,
- "deleting a selected control must succeed");
- require(service.findControl(page_id, indicator.id) == nullptr,
- "deleted controls must not remain in the model");
- }
-
- void testRuntimeUsesRegisterRepository()
- {
- // 运行服务只能经由仓库接口读写 M/D,不依赖具体离线实现
- VirtualRegisterRepository repository;
- HmiRuntimeService runtime_service(repository);
-
- HmiControl button;
- button.id = "start";
- button.type = HmiControlType::Button;
- button.binding = RegisterAddress{RegisterArea::M, 12};
- require(button.buttonOperation == HmiButtonOperation::MomentaryOn,
- "new buttons must default to momentary ON");
- require(runtime_service.operateButton(button, HmiButtonEvent::Pressed).succeeded,
- "momentary button press must be accepted by the repository");
- require(repository.readBit(*button.binding).value,
- "momentary button press must write ON");
- require(runtime_service.operateButton(button, HmiButtonEvent::Released).succeeded,
- "momentary button release must be accepted by the repository");
- require(!repository.readBit(*button.binding).value,
- "momentary button release must write OFF");
-
- button.buttonOperation = HmiButtonOperation::SetOn;
- require(runtime_service.operateButton(button, HmiButtonEvent::Pressed).succeeded,
- "set ON button press must succeed");
- require(repository.readBit(*button.binding).value,
- "set ON button press must write ON");
- require(runtime_service.operateButton(button, HmiButtonEvent::Released).succeeded,
- "set ON button release must be ignored successfully");
- require(repository.readBit(*button.binding).value,
- "set ON button release must keep the bit ON");
-
- button.buttonOperation = HmiButtonOperation::SetOff;
- require(runtime_service.operateButton(button, HmiButtonEvent::Pressed).succeeded,
- "set OFF button press must succeed");
- require(!repository.readBit(*button.binding).value,
- "set OFF button press must write OFF");
-
- button.buttonOperation = HmiButtonOperation::Toggle;
- require(runtime_service.operateButton(button, HmiButtonEvent::Pressed).succeeded,
- "toggle button press must succeed");
- require(repository.readBit(*button.binding).value,
- "toggle button press must invert the current value");
-
- HmiControl indicator;
- indicator.id = "running";
- indicator.type = HmiControlType::Indicator;
- indicator.binding = RegisterAddress{RegisterArea::M, 12};
- const HmiRuntimeReadResult indicator_value = runtime_service.readControl(indicator);
- require(indicator_value.succeeded && indicator_value.bit_value,
- "indicators must read M values through the repository");
-
- HmiControl numeric_input;
- numeric_input.id = "target";
- numeric_input.type = HmiControlType::NumericInput;
- numeric_input.binding = RegisterAddress{RegisterArea::D, 9};
- require(runtime_service.writeNumericInput(numeric_input, -18).succeeded,
- "numeric input runtime writes must be accepted by the repository");
-
- HmiControl numeric_display;
- numeric_display.id = "actual";
- numeric_display.type = HmiControlType::NumericDisplay;
- numeric_display.binding = RegisterAddress{RegisterArea::D, 9};
- const HmiRuntimeReadResult numeric_value = runtime_service.readControl(numeric_display);
- require(numeric_value.succeeded && numeric_value.word_value == -18,
- "numeric display must read D values through the repository");
- }
-
- } // namespace
-
- int main()
- {
- try
- {
- // 编辑和运行场景分别验证服务层两条独立职责
- testControlEditing();
- testRuntimeUsesRegisterRepository();
- }
- catch (const std::exception &error)
- {
- std::cerr << "HMI editor service tests failed: " << error.what() << '\n';
- return 1;
- }
-
- std::cout << "HMI editor service tests passed\n";
- return 0;
- }
|