/** * @file hmi_model.h * @brief 定义可保存的 HMI 页面、控件和寄存器绑定模型 * @author suyu * @date 2026-08-10 */ #pragma once #include "register_address.h" #include "register_value_type.h" #include "project_limits.h" #include #include #include #include #include namespace HmiAppearanceProperty { inline constexpr const char kTextColor[] = "textColor"; inline constexpr const char kFontSize[] = "fontSize"; inline constexpr const char kFontBold[] = "fontBold"; inline constexpr const char kFontItalic[] = "fontItalic"; } // namespace HmiAppearanceProperty /** * @brief 描述 HMI 控件在页面坐标系中的位置和尺寸 * * 页面校验要求矩形的左上角非负且完整位于页面范围内 */ struct HmiRect { static constexpr int kDefaultWidth = 80; static constexpr int kDefaultHeight = 32; int x = 0; int y = 0; int width = kDefaultWidth; int height = kDefaultHeight; }; /** * @brief HMI 控件的基础类型 * * 按钮和指示灯只能绑定 M 位,数值控件只能绑定 D 字 */ enum class HmiControlType { Button, // 按钮 Indicator, // 指示灯 NumericDisplay, // 数值显示 NumericInput, // 数值输入 Label, // 标签 StatusText, // 状态文本 PageJump, // 页面跳转 AlarmList, // 报警列表 Count // 已注册控件类型数量,不作为实际控件使用 }; /** * @brief 按钮对绑定 M 位执行的操作 */ enum class HmiButtonOperation { SetOn, // 按下时写入 1 SetOff, // 按下时写入 0 Toggle, // 按下时写入当前读回值的反值 MomentaryOn // 按下时写入 1,释放时写入 0 }; struct HmiPageJumpConfig { std::string targetPageId; }; struct HmiStatusBitTextConfig { std::string offText; std::string onText; bool operator==(const HmiStatusBitTextConfig &other) const { return offText == other.offText && onText == other.onText; } }; struct HmiStatusValueRange { std::optional lowerBound; std::optional upperBound; std::string text; bool operator==(const HmiStatusValueRange &other) const { return lowerBound == other.lowerBound && upperBound == other.upperBound && text == other.text; } }; struct HmiStatusWordTextConfig { std::vector ranges; bool operator==(const HmiStatusWordTextConfig &other) const { return ranges == other.ranges; } }; using HmiStatusTextConfig = std::variant< HmiStatusBitTextConfig, HmiStatusWordTextConfig>; /** * @brief 描述一个可保存的 HMI 控件及其显示和寄存器配置 * * `properties` 保存未纳入公共字段的字符串扩展属性 */ struct HmiControl { std::string id; HmiControlType type = HmiControlType::Label; HmiRect bounds; std::string text; std::optional binding; RegisterDataType dataType = RegisterDataType::Int16; std::map properties; HmiButtonOperation buttonOperation = HmiButtonOperation::MomentaryOn; std::optional pageJump; std::optional statusText; /** * @brief 校验控件的标识、尺寸、扩展属性和寄存器绑定 * @param error 可选错误输出,失败时写入首个校验原因 * @return 配置完整且满足控件类型绑定规则时返回 true * * 位控件必须绑定有效 M 地址,字控件必须绑定有效 D 地址 */ bool validate(std::string *error = nullptr) const; bool isConfigured() const; }; /** * @brief 描述一个 HMI 页面及其控件集合 * * 控件标识在同一页面内必须唯一,所有控件必须完整位于页面边界内 */ struct HmiPage { std::string id; std::string name; int width = ProjectLimits::kDefaultHmiPageWidth; int height = ProjectLimits::kDefaultHmiPageHeight; std::vector controls; /** * @brief 校验页面尺寸、控件标识唯一性和全部控件配置 * @param error 可选错误输出,失败时写入首个校验原因 * @return 页面及其全部控件有效时返回 true */ bool validate( const ProjectLimitSettings &limits, std::string *error = nullptr) const; bool validateForRunning( const ProjectLimitSettings &limits, std::string *error = nullptr) const; };