issue_25 #26
@@ -10,6 +10,8 @@ import (
|
|||||||
"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/repository"
|
||||||
"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport"
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport"
|
||||||
|
"git.huangwc.com/pig/pig-farm-controller/internal/infra/utils/command_generater"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
gproto "google.golang.org/protobuf/proto"
|
gproto "google.golang.org/protobuf/proto"
|
||||||
"google.golang.org/protobuf/types/known/anypb"
|
"google.golang.org/protobuf/types/known/anypb"
|
||||||
@@ -61,27 +63,40 @@ func (g *GeneralDeviceService) Switch(device *models.Device, action DeviceAction
|
|||||||
return fmt.Errorf("解析设备 %v(id=%v) 的属性失败: %w", device.Name, device.ID, err)
|
return fmt.Errorf("解析设备 %v(id=%v) 的属性失败: %w", device.Name, device.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var command models.SwitchCommands
|
// 4. 解析设备模板中的开关指令参数
|
||||||
// 前面的 device.DeviceTemplate.SelfCheck()保障了解析一定成功
|
var switchCmd models.SwitchCommands
|
||||||
_ = device.DeviceTemplate.ParseCommands(&command)
|
if err := device.DeviceTemplate.ParseCommands(&switchCmd); err != nil {
|
||||||
deviceAction := command.On
|
return fmt.Errorf("解析设备 %v(id=%v) 的开关指令失败: %w", device.Name, device.ID, err)
|
||||||
if action == DeviceActionStop {
|
|
||||||
deviceAction = command.Off
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 构建 Protobuf 指令
|
// 5. 根据 action 生成 Modbus RTU 写入指令
|
||||||
data, err := anypb.New(&proto.Switch{
|
onOffState := true // 默认为开启
|
||||||
DeviceAction: deviceAction,
|
if action == DeviceActionStop { // 如果是停止动作,则设置为关闭
|
||||||
BusNumber: int32(deviceProps.BusNumber),
|
onOffState = false
|
||||||
BusAddress: int32(deviceProps.BusAddress),
|
}
|
||||||
RelayChannel: int32(deviceProps.RelayChannel),
|
|
||||||
})
|
modbusCommandBytes, err := command_generater.GenerateModbusRTUSwitchCommand(
|
||||||
|
deviceProps.BusAddress,
|
||||||
|
switchCmd.ModbusStartAddress,
|
||||||
|
onOffState,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("创建指令失败: %w", err)
|
return fmt.Errorf("生成Modbus RTU写入指令失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 构建 Protobuf Raw485Command,包含总线号
|
||||||
|
raw485Cmd := &proto.Raw485Command{
|
||||||
|
BusNumber: int32(deviceProps.BusNumber), // 添加总线号
|
||||||
|
CommandBytes: modbusCommandBytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := anypb.New(raw485Cmd)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("创建 Raw485Command Any Protobuf 失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
instruction := &proto.Instruction{
|
instruction := &proto.Instruction{
|
||||||
Method: proto.MethodType_SWITCH,
|
Method: proto.MethodType_INSTRUCTION, // 使用通用指令类型
|
||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,14 +105,14 @@ func (g *GeneralDeviceService) Switch(device *models.Device, action DeviceAction
|
|||||||
return fmt.Errorf("序列化指令失败: %w", err)
|
return fmt.Errorf("序列化指令失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 发送指令
|
// 7. 发送指令
|
||||||
networkID := areaController.NetworkID
|
networkID := areaController.NetworkID
|
||||||
sendResult, err := g.comm.Send(networkID, message)
|
sendResult, err := g.comm.Send(networkID, message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("发送指令到 %s 失败: %w", networkID, err)
|
return fmt.Errorf("发送指令到 %s 失败: %w", networkID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 创建并保存命令日志
|
// 8. 创建并保存命令日志
|
||||||
logRecord := &models.DeviceCommandLog{
|
logRecord := &models.DeviceCommandLog{
|
||||||
MessageID: sendResult.MessageID,
|
MessageID: sendResult.MessageID,
|
||||||
DeviceID: areaController.ID,
|
DeviceID: areaController.ID,
|
||||||
@@ -144,7 +159,7 @@ func (g *GeneralDeviceService) Collect(regionalControllerID uint, devicesToColle
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用模板的 ParseCommands 方法获取指令
|
// 使用模板的 ParseCommands 方法获取传感器指令参数
|
||||||
var sensorCmd models.SensorCommands
|
var sensorCmd models.SensorCommands
|
||||||
if err := dev.DeviceTemplate.ParseCommands(&sensorCmd); err != nil {
|
if err := dev.DeviceTemplate.ParseCommands(&sensorCmd); err != nil {
|
||||||
g.logger.Warnf("跳过设备 %d,因其模板指令无法解析为 SensorCommands: %v", dev.ID, err)
|
g.logger.Warnf("跳过设备 %d,因其模板指令无法解析为 SensorCommands: %v", dev.ID, err)
|
||||||
@@ -158,10 +173,26 @@ func (g *GeneralDeviceService) Collect(regionalControllerID uint, devicesToColle
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成 Modbus RTU 读取指令
|
||||||
|
modbusCommandBytes, err := command_generater.GenerateModbusRTUReadCommand(
|
||||||
|
deviceProps.BusAddress,
|
||||||
|
sensorCmd.ModbusFunctionCode,
|
||||||
|
sensorCmd.ModbusStartAddress,
|
||||||
|
sensorCmd.ModbusQuantity,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
g.logger.Warnf("跳过设备 %d,因生成Modbus RTU读取指令失败: %v", dev.ID, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建 Raw485Command,包含总线号
|
||||||
|
raw485Cmd := &proto.Raw485Command{
|
||||||
|
BusNumber: int32(deviceProps.BusNumber), // 添加总线号
|
||||||
|
CommandBytes: modbusCommandBytes,
|
||||||
|
}
|
||||||
|
|
||||||
collectTasks = append(collectTasks, &proto.CollectTask{
|
collectTasks = append(collectTasks, &proto.CollectTask{
|
||||||
DeviceAction: sensorCmd.Read, // 使用从模板中获取的指令
|
Command: raw485Cmd,
|
||||||
BusNumber: int32(deviceProps.BusNumber),
|
|
||||||
BusAddress: int32(deviceProps.BusAddress),
|
|
||||||
})
|
})
|
||||||
childDeviceIDs = append(childDeviceIDs, dev.ID)
|
childDeviceIDs = append(childDeviceIDs, dev.ID)
|
||||||
}
|
}
|
||||||
@@ -199,7 +230,7 @@ func (g *GeneralDeviceService) Collect(regionalControllerID uint, devicesToColle
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
instruction := &proto.Instruction{
|
instruction := &proto.Instruction{
|
||||||
Method: proto.MethodType_COLLECT,
|
Method: proto.MethodType_COLLECT, // 使用 COLLECT 指令类型
|
||||||
Data: anyData,
|
Data: anyData,
|
||||||
}
|
}
|
||||||
payload, err := gproto.Marshal(instruction)
|
payload, err := gproto.Marshal(instruction)
|
||||||
|
|||||||
@@ -235,8 +235,7 @@ func (x *BatchCollectCommand) GetTasks() []*CollectTask {
|
|||||||
// 定义了单个采集任务的“意图”。现在直接包含平台生成的原始485指令,并带上总线号。
|
// 定义了单个采集任务的“意图”。现在直接包含平台生成的原始485指令,并带上总线号。
|
||||||
type CollectTask struct {
|
type CollectTask struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
BusNumber int32 `protobuf:"varint,1,opt,name=bus_number,json=busNumber,proto3" json:"bus_number,omitempty"` // 总线号,用于指示单片机将指令发送到哪个总线
|
Command *Raw485Command `protobuf:"bytes,2,opt,name=command,proto3" json:"command,omitempty"` // 平台生成的原始485指令
|
||||||
Command *Raw485Command `protobuf:"bytes,2,opt,name=command,proto3" json:"command,omitempty"` // 平台生成的原始485指令
|
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -271,13 +270,6 @@ func (*CollectTask) Descriptor() ([]byte, []int) {
|
|||||||
return file_device_proto_rawDescGZIP(), []int{3}
|
return file_device_proto_rawDescGZIP(), []int{3}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *CollectTask) GetBusNumber() int32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.BusNumber
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *CollectTask) GetCommand() *Raw485Command {
|
func (x *CollectTask) GetCommand() *Raw485Command {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Command
|
return x.Command
|
||||||
@@ -353,10 +345,8 @@ const file_device_proto_rawDesc = "" +
|
|||||||
"\x04data\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x04data\"g\n" +
|
"\x04data\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x04data\"g\n" +
|
||||||
"\x13BatchCollectCommand\x12%\n" +
|
"\x13BatchCollectCommand\x12%\n" +
|
||||||
"\x0ecorrelation_id\x18\x01 \x01(\tR\rcorrelationId\x12)\n" +
|
"\x0ecorrelation_id\x18\x01 \x01(\tR\rcorrelationId\x12)\n" +
|
||||||
"\x05tasks\x18\x02 \x03(\v2\x13.device.CollectTaskR\x05tasks\"]\n" +
|
"\x05tasks\x18\x02 \x03(\v2\x13.device.CollectTaskR\x05tasks\">\n" +
|
||||||
"\vCollectTask\x12\x1d\n" +
|
"\vCollectTask\x12/\n" +
|
||||||
"\n" +
|
|
||||||
"bus_number\x18\x01 \x01(\x05R\tbusNumber\x12/\n" +
|
|
||||||
"\acommand\x18\x02 \x01(\v2\x15.device.Raw485CommandR\acommand\"N\n" +
|
"\acommand\x18\x02 \x01(\v2\x15.device.Raw485CommandR\acommand\"N\n" +
|
||||||
"\rCollectResult\x12%\n" +
|
"\rCollectResult\x12%\n" +
|
||||||
"\x0ecorrelation_id\x18\x01 \x01(\tR\rcorrelationId\x12\x16\n" +
|
"\x0ecorrelation_id\x18\x01 \x01(\tR\rcorrelationId\x12\x16\n" +
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ message BatchCollectCommand {
|
|||||||
// CollectTask
|
// CollectTask
|
||||||
// 定义了单个采集任务的“意图”。现在直接包含平台生成的原始485指令,并带上总线号。
|
// 定义了单个采集任务的“意图”。现在直接包含平台生成的原始485指令,并带上总线号。
|
||||||
message CollectTask {
|
message CollectTask {
|
||||||
int32 bus_number = 1; // 总线号,用于指示单片机将指令发送到哪个总线
|
|
||||||
Raw485Command command = 2; // 平台生成的原始485指令
|
Raw485Command command = 2; // 平台生成的原始485指令
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func GenerateModbusRTUReadCommand(slaveAddress uint8, functionCode ModbusFunctio
|
|||||||
return command, nil
|
return command, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateModbusRTUWriteCommand 生成Modbus RTU写入单个线圈的指令
|
// GenerateModbusRTUSwitchCommand 生成Modbus RTU写入单个线圈的指令
|
||||||
// 该函数专门用于生成 Modbus RTU 的写入单个线圈 (0x05) 指令,用于控制开关。
|
// 该函数专门用于生成 Modbus RTU 的写入单个线圈 (0x05) 指令,用于控制开关。
|
||||||
//
|
//
|
||||||
// 参数:
|
// 参数:
|
||||||
@@ -113,7 +113,7 @@ func GenerateModbusRTUReadCommand(slaveAddress uint8, functionCode ModbusFunctio
|
|||||||
//
|
//
|
||||||
// []byte: 完整的Modbus RTU指令字节切片。
|
// []byte: 完整的Modbus RTU指令字节切片。
|
||||||
// error: 如果参数无效或生成过程中出现错误,则返回错误信息。
|
// error: 如果参数无效或生成过程中出现错误,则返回错误信息。
|
||||||
func GenerateModbusRTUWriteCommand(slaveAddress uint8, coilAddress uint16, onOffState bool) ([]byte, error) {
|
func GenerateModbusRTUSwitchCommand(slaveAddress uint8, coilAddress uint16, onOffState bool) ([]byte, error) {
|
||||||
// 1. 校验从站地址
|
// 1. 校验从站地址
|
||||||
if slaveAddress == 0 || slaveAddress > 247 {
|
if slaveAddress == 0 || slaveAddress > 247 {
|
||||||
return nil, fmt.Errorf("从站地址无效: %d, 必须在1-247之间", slaveAddress)
|
return nil, fmt.Errorf("从站地址无效: %d, 必须在1-247之间", slaveAddress)
|
||||||
|
|||||||
Reference in New Issue
Block a user