package models import ( "encoding/json" "errors" "gorm.io/datatypes" "gorm.io/gorm" ) // DeviceCategory 定义了设备模板的宽泛类别 (移除了 Compound) type DeviceCategory string const ( // CategoryActuator 代表一个执行器,可以被控制(例如:风机、阀门) CategoryActuator DeviceCategory = "actuator" // CategorySensor 代表一个传感器,用于报告测量值(例如:温度计) CategorySensor DeviceCategory = "sensor" ) // ValueDescriptor 描述了传感器可以报告的单个数值。 // 它提供了必要的元数据,以便应用程序能够正确解释从设备读取的原始数据。 type ValueDescriptor struct { Name string `json:"name"` Unit string `json:"unit"` DataType string `json:"data_type"` Multiplier float64 `json:"multiplier"` Offset float64 `json:"offset"` } // --- 指令结构体 (Command Structs) --- // SwitchCommands 定义了开关类指令 type SwitchCommands struct { On string `json:"on"` Off string `json:"off"` } // SelfCheck 校验开关指令的有效性 func (sc *SwitchCommands) SelfCheck() error { if sc.On == "" { return errors.New("'switch' 指令集缺少 'on' 指令") } if sc.Off == "" { return errors.New("'switch' 指令集缺少 'off' 指令") } return nil } // SensorCommands 定义了传感器读取指令 type SensorCommands struct { Read string `json:"read"` } // SelfCheck 校验读取指令的有效性 func (sc *SensorCommands) SelfCheck() error { if sc.Read == "" { return errors.New("'sensor' 指令集缺少 'read' 指令") } return nil } // DeviceTemplate 代表一种物理设备的类型。 type DeviceTemplate struct { gorm.Model // Name 是此模板的唯一名称, 例如 "FanModel-XYZ-2000" 或 "TempSensor-T1" Name string `gorm:"not null;unique" json:"name"` // Manufacturer 是设备的制造商。 Manufacturer string `json:"manufacturer"` // Description 提供了关于此设备类型的更多详细信息。 Description string `json:"description"` // Category 将模板分类为传感器、执行器或复合设备。 Category DeviceCategory `gorm:"not null;index" json:"category"` // Commands 存储了一个从“动作名称”到“原始指令”的映射。 // 使用 JSON 格式,具有良好的可扩展性。 // 例如,对于风机: {"ON": "01050000FF008C3A", "OFF": "010500000000CDCA"} // 例如,对于传感器: {"READ": "010300000001840A"} Commands datatypes.JSON `json:"commands"` // Values 描述了传感器模板所能提供的数据点。 // 当 Category 是 "sensor" 或 "compound" 时,此字段尤为重要。 // 它是一个 ValueDescriptor 对象的 JSON 数组。 Values datatypes.JSON `json:"values"` } // TableName 自定义 GORM 使用的数据库表名 func (DeviceTemplate) TableName() string { return "device_templates" } // ParseCommands ... func (dt *DeviceTemplate) ParseCommands(v interface{}) error { if dt.Commands == nil { return errors.New("设备模板的 Commands 属性为空,无法解析") } return json.Unmarshal(dt.Commands, v) } // ParseValues ... func (dt *DeviceTemplate) ParseValues(v interface{}) error { if dt.Values == nil { return errors.New("设备模板的 Values 属性为空,无法解析") } return json.Unmarshal(dt.Values, v) } // SelfCheck 对 DeviceTemplate 进行彻底的、基于角色的校验 func (dt *DeviceTemplate) SelfCheck() error { if dt.Commands == nil { return errors.New("所有设备模板都必须有 Commands 定义") } switch dt.Category { case CategoryActuator: var cmd SwitchCommands if err := dt.ParseCommands(&cmd); err != nil { return errors.New("执行器模板的 Commands 无法被解析为 'switch' 指令集") } if err := cmd.SelfCheck(); err != nil { return err } case CategorySensor: var cmd SensorCommands if err := dt.ParseCommands(&cmd); err != nil { return errors.New("传感器模板的 Commands 无法被解析为 'sensor' 指令集") } if err := cmd.SelfCheck(); err != nil { return err } if dt.Values == nil { return errors.New("传感器类型的设备模板缺少 Values 定义") } var values []*ValueDescriptor if err := dt.ParseValues(&values); err != nil { return errors.New("无法解析传感器模板的 Values 属性") } // 黄金准则: 一个传感器模板只能定义一种数值的解析方式 if len(values) != 1 { return errors.New("传感器模板的 Values 定义必须且只能包含一个描述符") } default: return errors.New("未知的设备模板类别") } return nil }