修改设备模型

This commit is contained in:
2025-09-29 16:58:32 +08:00
parent f007e3b207
commit facbbfe6a1
2 changed files with 117 additions and 104 deletions

View File

@@ -6,41 +6,6 @@ import (
"gorm.io/datatypes"
"gorm.io/gorm"
"git.huangwc.com/pig/pig-farm-controller/internal/infra/utils"
)
// DeviceType 定义了设备的高级类别
type DeviceType string
const (
// DeviceTypeAreaController 区域主控,负责管理一个片区的设备
DeviceTypeAreaController DeviceType = "area_controller"
// DeviceTypeDevice 普通设备,如传感器、阀门等
DeviceTypeDevice DeviceType = "device"
)
// DeviceSubType 定义了普通设备的具体子类别
type DeviceSubType string
const (
// SubTypeNone 未指定或不适用的子类型
SubTypeNone DeviceSubType = ""
// SubTypeSensorTemp 温度传感器
SubTypeSensorTemp DeviceSubType = "temperature"
// SubTypeSensorHumidity 湿度传感器
SubTypeSensorHumidity DeviceSubType = "humidity"
// SubTypeSensorAmmonia 氨气传感器
SubTypeSensorAmmonia DeviceSubType = "ammonia"
// SubTypeSensorWeight 电子秤
SubTypeSensorWeight DeviceSubType = "weight"
// SubTypeValveFeed 下料阀门
SubTypeValveFeed DeviceSubType = "feed_valve"
// SubTypeFan 风机
SubTypeFan DeviceSubType = "fan"
// SubTypeWaterCurtain 水帘
SubTypeWaterCurtain DeviceSubType = "water_curtain"
)
// 设备属性名大全
@@ -64,34 +29,59 @@ type LoraProperties struct {
// BusProperties 定义了总线设备的特有属性
type BusProperties struct {
BusID int `json:"bus_id"` // 485 总线号
BusAddress int `json:"bus_address"` // 485 总线地址
BusID int `json:"bus_id"` // 485 总线号
BusAddress int `json:"bus_address"` // 485 总线地址
RelayChannel int `json:"relay_channel"` // 继电器通道号
}
// Device 代表系统中的所有设备
// AreaController 是一个LoRa转总线(如485)的通信网关
type AreaController struct {
gorm.Model
// Name 是主控的业务名称,例如 "1号猪舍主控"
Name string `gorm:"not null;unique" json:"name"`
// NetworkID 是主控在通信网络中的唯一标识,例如 LoRaWAN 的 DevEUI。
// 这是 transport 层用来寻址的关键。
NetworkID string `gorm:"not null;unique;index" json:"network_id"`
// Location 描述了主控的物理安装位置。
Location string `gorm:"index" json:"location"`
// Status 表示主控的在线状态等,可以后续扩展。
Status string `gorm:"default:'unknown'" json:"status"`
// Properties 用于存储其他与主控相关的属性,例如硬件版本、固件版本等。
Properties datatypes.JSON `json:"properties"`
}
// TableName 自定义 GORM 使用的数据库表名
func (AreaController) TableName() string {
return "area_controllers"
}
// Device 代表系统中的所有普通设备
type Device struct {
// gorm.Model 内嵌了标准模型字段 (ID, CreatedAt, UpdatedAt, DeletedAt)
gorm.Model
// Name 是设备的业务名称,应清晰可读,例如 "1号猪舍温度传感器" 或 "做料车间主控"
// Name 是设备的业务名称,应清晰可读,例如 "1号猪舍温度传感器"
Name string `gorm:"not null" json:"name"`
// Type 是设备的高级类别,用于区分区域主控和普通设备。建立索引以优化按类型查询。
Type DeviceType `gorm:"not null;index" json:"type"`
// SubType 是设备的子类别,用于描述普通设备的具体功能,例如 "temperature", "fan" 等。建立索引以优化按子类型查询。
SubType DeviceSubType `gorm:"index" json:"sub_type"`
// ParentID 指向其父级设备的ID。对于顶层设备如区域主控此值为 NULL。
// 使用指针类型 *uint 来允许 NULL 值,从而清晰地表示“无父级”,避免了使用 0 作为魔术数字的歧义。建立索引以优化层级查询。
ParentID *uint `gorm:"index" json:"parent_id"`
// Location 描述了设备的物理安装位置,例如 "1号猪舍东侧",方便运维。建立索引以优化按位置查询。
Location string `gorm:"index" json:"location"`
// Command 存储了与设备交互所需的具体指令。
// 例如,对于传感器,这里存储 Modbus 采集指令;对于开关和区域主控,这里可以为空。
Command string `gorm:"type:varchar(255)" json:"command"`
// DeviceTemplateID 是设备模板的外键
DeviceTemplateID uint `gorm:"not null;index" json:"device_template_id"`
// DeviceTemplate 是设备的模板,包含了设备的通用信息
DeviceTemplate DeviceTemplate `json:"device_template"`
// AreaControllerID 是区域主控的外键
AreaControllerID uint `gorm:"not null;index" json:"area_controller_id"`
// AreaController 是设备所属的区域主控
AreaController AreaController `json:"area_controller"`
// Properties 用于存储特定类型设备的独有属性采用JSON格式。
// 建议在应用层为不同子类型的设备定义专用的属性结构体(如 LoraProperties, BusProperties以保证数据一致性。
@@ -115,55 +105,3 @@ func (d *Device) ParseProperties(v interface{}) error {
}
return json.Unmarshal(d.Properties, v)
}
// SelfCheck 进行参数自检, 返回检测结果
// 方法会根据自身类型进行参数检查, 参数不全时返回false
func (d *Device) SelfCheck() bool {
// 使用清晰的 switch 结构,确保所有情况都被覆盖
switch d.Type {
case DeviceTypeAreaController:
props := make(map[string]interface{})
if err := d.ParseProperties(&props); err != nil {
return false
}
_, ok := props[LoRaAddress].(string)
return ok
case DeviceTypeDevice:
// 所有普通设备都必须有父级
if d.ParentID == nil || *d.ParentID == 0 {
return false
}
props := make(map[string]interface{})
if err := d.ParseProperties(&props); err != nil {
return false
}
// hasPureNumeric 检查一个key是否存在于map中并且其值是纯数字整数或可解析为整数的字符串
hasPureNumeric := func(key string) bool {
val, ok := props[key]
if !ok {
return false // Key不存在
}
return utils.IsPureNumeric(val)
}
// 根据子类型进行具体校验
switch d.SubType {
// 所有传感器类型都必须有 Command 和总线信息,且总线信息为纯数字
case SubTypeSensorTemp, SubTypeSensorHumidity, SubTypeSensorWeight, SubTypeSensorAmmonia:
return d.Command != "" && hasPureNumeric(BusNumber) && hasPureNumeric(BusAddress)
// 所有开关类型都必须有继电器和总线信息,且都为纯数字
case SubTypeFan, SubTypeWaterCurtain, SubTypeValveFeed:
return hasPureNumeric(BusNumber) && hasPureNumeric(BusAddress) && hasPureNumeric(RelayChannel)
// 如果是未知的子类型,或者没有子类型,则认为自检失败
default:
return false
}
// 如果设备类型不是已知的任何一种,则自检失败
default:
return false
}
}