155 lines
4.5 KiB
Go
155 lines
4.5 KiB
Go
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
|
|
}
|