实现 GeneralDeviceService
This commit is contained in:
@@ -6,15 +6,15 @@ import "git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
|||||||
type DeviceAction string
|
type DeviceAction string
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DeviceActionStart = "start" // 启动
|
DeviceActionStart DeviceAction = "start" // 启动
|
||||||
DeviceActionStop = "stop" // 停止
|
DeviceActionStop DeviceAction = "stop" // 停止
|
||||||
)
|
)
|
||||||
|
|
||||||
// 指令类型
|
// 指令类型
|
||||||
type Method string
|
type Method string
|
||||||
|
|
||||||
var (
|
var (
|
||||||
MethodPing = "ping" // ping指令
|
MethodSwitch Method = "switch" // 启停指令
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service 抽象了一组方法用于控制设备行为
|
// Service 抽象了一组方法用于控制设备行为
|
||||||
|
|||||||
@@ -1,20 +1,103 @@
|
|||||||
package device
|
package device
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/app/service/device/proto"
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport"
|
||||||
|
gproto "google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GeneralDeviceService struct {
|
type GeneralDeviceService struct {
|
||||||
|
deviceRepo repository.DeviceRepository
|
||||||
|
logger *logs.Logger
|
||||||
|
|
||||||
|
deviceID uint // 区域主控的设备ID
|
||||||
|
|
||||||
|
// regionalController 是执行命令的区域主控, 所有的指令都会发往区域主控
|
||||||
|
regionalController *models.Device
|
||||||
|
|
||||||
comm transport.Communicator
|
comm transport.Communicator
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GeneralDeviceService) Switch(device models.Device, action DeviceAction) error {
|
// NewGeneralDeviceService 创建一个通用设备服务
|
||||||
//TODO implement me
|
func NewGeneralDeviceService(deviceID uint, deviceRepo repository.DeviceRepository, logger *logs.Logger, comm transport.Communicator) *GeneralDeviceService {
|
||||||
panic("implement me")
|
return &GeneralDeviceService{
|
||||||
|
deviceID: deviceID,
|
||||||
|
deviceRepo: deviceRepo,
|
||||||
|
logger: logger,
|
||||||
|
comm: comm,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GeneralDeviceService) Ping() error {
|
func (g *GeneralDeviceService) Switch(device models.Device, action DeviceAction) error {
|
||||||
//TODO implement me
|
|
||||||
panic("implement me")
|
// 校验设备参数及生成指令
|
||||||
|
if *device.ParentID != g.deviceID {
|
||||||
|
return fmt.Errorf("设备 %v(id=%v) 的上级区域主控是(id=%v), 不是当前区域主控(id=%v)下属设备, 无法执行指令", device.Name, device.ID, device.ParentID, g.deviceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !device.SelfCheck() {
|
||||||
|
return fmt.Errorf("设备 %v(id=%v) 缺少必要信息, 无法发送指令", device.Name, device.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceInfo := make(map[string]interface{})
|
||||||
|
if err := device.ParseProperties(&deviceInfo); err != nil {
|
||||||
|
return fmt.Errorf("解析设备 %v(id=%v) 配置失败: %v", device.Name, device.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
busNumber, err := strconv.Atoi(fmt.Sprintf("%v", deviceInfo[models.BusNumber]))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("无效的总线号: %v", err)
|
||||||
|
}
|
||||||
|
busAddress, err := strconv.Atoi(fmt.Sprintf("%v", deviceInfo[models.BusAddress]))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("无效的总线地址: %v", err)
|
||||||
|
}
|
||||||
|
relayChannel, err := strconv.Atoi(fmt.Sprintf("%v", deviceInfo[models.RelayChannel]))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("无效的继电器通道: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := anypb.New(&proto.Switch{
|
||||||
|
DeviceAction: string(action),
|
||||||
|
BusNumber: int32(busNumber),
|
||||||
|
BusAddress: int32(busAddress),
|
||||||
|
RelayChannel: int32(relayChannel),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("创建指令失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction := &proto.Instruction{
|
||||||
|
Method: proto.MethodType_SWITCH,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取自身LoRa设备ID, 因为可能变更, 所以每次都现获取
|
||||||
|
thisDevice, err := g.deviceRepo.FindByID(g.deviceID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("获取区域主控(id=%v)信息失败: %v", g.deviceID, err)
|
||||||
|
}
|
||||||
|
if !thisDevice.SelfCheck() {
|
||||||
|
return fmt.Errorf("区域主控 %v(id=%v) 缺少必要信息, 无法发送指令", thisDevice.Name, thisDevice.ID)
|
||||||
|
}
|
||||||
|
thisDeviceinfo := make(map[string]interface{})
|
||||||
|
if err := thisDevice.ParseProperties(&thisDeviceinfo); err != nil {
|
||||||
|
return fmt.Errorf("解析区域主控 %v(id=%v) 配置失败: %v", device.Name, device.ID, err)
|
||||||
|
}
|
||||||
|
loraAddress := fmt.Sprintf("%v", thisDeviceinfo[models.LoRaAddress])
|
||||||
|
|
||||||
|
// 生成消息并发送
|
||||||
|
message, err := gproto.Marshal(instruction)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("序列化指令失败: %v", err)
|
||||||
|
}
|
||||||
|
return g.comm.Send(loraAddress, message)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,18 @@ const (
|
|||||||
SubTypeWaterCurtain DeviceSubType = "water_curtain"
|
SubTypeWaterCurtain DeviceSubType = "water_curtain"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 设备属性名大全
|
||||||
|
var (
|
||||||
|
|
||||||
|
// 普通开关式设备
|
||||||
|
BusNumber = "bus_number" // 总线号
|
||||||
|
BusAddress = "bus_address" // 总线地址
|
||||||
|
RelayChannel = "relay_channel" // 继电器通道号
|
||||||
|
|
||||||
|
// 区域主控
|
||||||
|
LoRaAddress = "lora_address" // 区域主控 LoRa 地址, 如果使用LoRa网关也可能是LoRa网关记录的设备ID
|
||||||
|
)
|
||||||
|
|
||||||
// --- Properties 结构体定义 ---
|
// --- Properties 结构体定义 ---
|
||||||
|
|
||||||
// LoraProperties 定义了区域主控的特有属性
|
// LoraProperties 定义了区域主控的特有属性
|
||||||
@@ -95,3 +107,31 @@ func (d *Device) ParseProperties(v interface{}) error {
|
|||||||
}
|
}
|
||||||
return json.Unmarshal(d.Properties, v)
|
return json.Unmarshal(d.Properties, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelfCheck 进行参数自检, 返回检测结果
|
||||||
|
// 方法会根据自身类型进行参数检查, 参数不全时返回false
|
||||||
|
// TODO 没写单测
|
||||||
|
func (d *Device) SelfCheck() bool {
|
||||||
|
|
||||||
|
properties := make(map[string]interface{})
|
||||||
|
if err := d.ParseProperties(&properties); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
has := func(key string) bool {
|
||||||
|
_, ok := properties[key]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
switch d.SubType {
|
||||||
|
case SubTypeFan:
|
||||||
|
if !has(BusNumber) || !has(BusAddress) || !has(RelayChannel) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// 不应该有类型未知的设备
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ func NewChirpStackTransport(config ChirpStackConfig, logger *logs.Logger) *Chirp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ChirpStackTransport) Send(deviceID string, payload []byte) error {
|
func (c *ChirpStackTransport) Send(address string, payload []byte) error {
|
||||||
// 1. 构建 API 请求体。
|
// 1. 构建 API 请求体。
|
||||||
// - Confirmed: true 表示确认消息, 设为false将不保证消息送达(但可以节约下行容量)。
|
// - Confirmed: true 表示确认消息, 设为false将不保证消息送达(但可以节约下行容量)。
|
||||||
// - Data: 经过 Base64 编码的数据。
|
// - Data: 经过 Base64 编码的数据。
|
||||||
@@ -75,17 +75,17 @@ func (c *ChirpStackTransport) Send(deviceID string, payload []byte) error {
|
|||||||
// - WithBody 设置请求体。
|
// - WithBody 设置请求体。
|
||||||
params := device_service.NewDeviceServiceEnqueueParams().
|
params := device_service.NewDeviceServiceEnqueueParams().
|
||||||
WithTimeout(10 * time.Second).
|
WithTimeout(10 * time.Second).
|
||||||
WithQueueItemDevEui(deviceID).
|
WithQueueItemDevEui(address).
|
||||||
WithBody(body)
|
WithBody(body)
|
||||||
|
|
||||||
// 3. 调用生成的客户端方法来发送请求。
|
// 3. 调用生成的客户端方法来发送请求。
|
||||||
// c.authInfo 是您在 NewChirpStackTransport 中创建的认证信息。
|
// c.authInfo 是您在 NewChirpStackTransport 中创建的认证信息。
|
||||||
_, err := c.client.DeviceService.DeviceServiceEnqueue(params, c.authInfo)
|
_, err := c.client.DeviceService.DeviceServiceEnqueue(params, c.authInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logger.Errorf("设备 %s 调用ChirpStack Enqueue失败: %v", deviceID, err)
|
c.logger.Errorf("设备 %s 调用ChirpStack Enqueue失败: %v", address, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Infof("设备 %s 调用ChirpStack Enqueue成功", deviceID)
|
c.logger.Infof("设备 %s 调用ChirpStack Enqueue成功", address)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ package transport
|
|||||||
// Communicator 用于其他设备通信
|
// Communicator 用于其他设备通信
|
||||||
type Communicator interface {
|
type Communicator interface {
|
||||||
// Send 用于发送一条单向数据(不等待回信)
|
// Send 用于发送一条单向数据(不等待回信)
|
||||||
Send(deviceID string, payload []byte) error
|
Send(address string, payload []byte) error
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user