优化设备模板, 一个设备只能干一件事

This commit is contained in:
2025-09-29 17:55:43 +08:00
parent 8706d8c913
commit ccea087f6c

View File

@@ -1,11 +1,14 @@
package models
import (
"encoding/json"
"errors"
"gorm.io/datatypes"
"gorm.io/gorm"
)
// DeviceCategory 定义了设备模板的宽泛类别
// DeviceCategory 定义了设备模板的宽泛类别 (移除了 Compound)
type DeviceCategory string
const (
@@ -13,35 +16,51 @@ const (
CategoryActuator DeviceCategory = "actuator"
// CategorySensor 代表一个传感器,用于报告测量值(例如:温度计)
CategorySensor DeviceCategory = "sensor"
// CategoryCompound 代表一个复合设备,既是执行器也是传感器
CategoryCompound DeviceCategory = "compound"
)
// ValueDescriptor 描述了传感器可以报告的单个数值。
// 它提供了必要的元数据,以便应用程序能够正确解释从设备读取的原始数据。
type ValueDescriptor struct {
Name string `json:"name"` // 值的业务名称, 例如 "temperature", "humidity"
Unit string `json:"unit"` // 测量单位, 例如 "°C", "%RH", "ppm"
DataType string `json:"data_type"` // 期望的数据类型, 例如 "float", "int", "boolean"
Multiplier float64 `json:"multiplier"` // 乘以原始值的系数 (例如 0.1)
Offset float64 `json:"offset"` // 乘法之后再增加的偏移量 (最终值 = 原始值 * multiplier + offset)
Name string `json:"name"`
Unit string `json:"unit"`
DataType string `json:"data_type"`
Multiplier float64 `json:"multiplier"`
Offset float64 `json:"offset"`
}
// DeviceCommands 定义了设备模板支持的指令集合。
// 使用指针类型来表示指令的可选性,如果一个模板不支持某个指令,则该字段为 nil。
// json tag 中的 "omitempty" 确保了在序列化回 JSON 时nil 字段会被省略,保持数据库数据的整洁。
type DeviceCommands struct {
On *string `json:"on,omitempty"` // 开指令
Off *string `json:"off,omitempty"` // 关指令
Read *string `json:"read,omitempty"` // 读取传感器数值的指令
// --- 指令结构体 (Command Structs) ---
// 为了未来的扩展性,可以预留一些通用指令
SetSpeed *string `json:"set_speed,omitempty"` // 设置速度/档位
SetValue *string `json:"set_value,omitempty"` // 设置某个具体值(例如设定温度)
// 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
@@ -73,3 +92,63 @@ type DeviceTemplate struct {
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
}