From 56dbb680a74a557a2df4f50788e21b97c2e89859 Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Tue, 30 Sep 2025 00:07:16 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=20GeneralDeviceService?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/device/general_device_service.go | 75 +++++++++++++------ .../app/service/device/proto/device.pb.go | 16 +--- .../app/service/device/proto/device.proto | 1 - .../utils/command_generater/modbus_rtu.go | 4 +- 4 files changed, 58 insertions(+), 38 deletions(-) diff --git a/internal/app/service/device/general_device_service.go b/internal/app/service/device/general_device_service.go index 2233c4a..3ea8069 100644 --- a/internal/app/service/device/general_device_service.go +++ b/internal/app/service/device/general_device_service.go @@ -10,6 +10,8 @@ import ( "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/utils/command_generater" + "github.com/google/uuid" gproto "google.golang.org/protobuf/proto" "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) } - var command models.SwitchCommands - // 前面的 device.DeviceTemplate.SelfCheck()保障了解析一定成功 - _ = device.DeviceTemplate.ParseCommands(&command) - deviceAction := command.On - if action == DeviceActionStop { - deviceAction = command.Off + // 4. 解析设备模板中的开关指令参数 + var switchCmd models.SwitchCommands + if err := device.DeviceTemplate.ParseCommands(&switchCmd); err != nil { + return fmt.Errorf("解析设备 %v(id=%v) 的开关指令失败: %w", device.Name, device.ID, err) } - // 4. 构建 Protobuf 指令 - data, err := anypb.New(&proto.Switch{ - DeviceAction: deviceAction, - BusNumber: int32(deviceProps.BusNumber), - BusAddress: int32(deviceProps.BusAddress), - RelayChannel: int32(deviceProps.RelayChannel), - }) + // 5. 根据 action 生成 Modbus RTU 写入指令 + onOffState := true // 默认为开启 + if action == DeviceActionStop { // 如果是停止动作,则设置为关闭 + onOffState = false + } + + modbusCommandBytes, err := command_generater.GenerateModbusRTUSwitchCommand( + deviceProps.BusAddress, + switchCmd.ModbusStartAddress, + onOffState, + ) 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{ - Method: proto.MethodType_SWITCH, + Method: proto.MethodType_INSTRUCTION, // 使用通用指令类型 Data: data, } @@ -90,14 +105,14 @@ func (g *GeneralDeviceService) Switch(device *models.Device, action DeviceAction return fmt.Errorf("序列化指令失败: %w", err) } - // 5. 发送指令 + // 7. 发送指令 networkID := areaController.NetworkID sendResult, err := g.comm.Send(networkID, message) if err != nil { return fmt.Errorf("发送指令到 %s 失败: %w", networkID, err) } - // 6. 创建并保存命令日志 + // 8. 创建并保存命令日志 logRecord := &models.DeviceCommandLog{ MessageID: sendResult.MessageID, DeviceID: areaController.ID, @@ -144,7 +159,7 @@ func (g *GeneralDeviceService) Collect(regionalControllerID uint, devicesToColle continue } - // 使用模板的 ParseCommands 方法获取指令 + // 使用模板的 ParseCommands 方法获取传感器指令参数 var sensorCmd models.SensorCommands if err := dev.DeviceTemplate.ParseCommands(&sensorCmd); err != nil { g.logger.Warnf("跳过设备 %d,因其模板指令无法解析为 SensorCommands: %v", dev.ID, err) @@ -158,10 +173,26 @@ func (g *GeneralDeviceService) Collect(regionalControllerID uint, devicesToColle 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{ - DeviceAction: sensorCmd.Read, // 使用从模板中获取的指令 - BusNumber: int32(deviceProps.BusNumber), - BusAddress: int32(deviceProps.BusAddress), + Command: raw485Cmd, }) childDeviceIDs = append(childDeviceIDs, dev.ID) } @@ -199,7 +230,7 @@ func (g *GeneralDeviceService) Collect(regionalControllerID uint, devicesToColle return err } instruction := &proto.Instruction{ - Method: proto.MethodType_COLLECT, + Method: proto.MethodType_COLLECT, // 使用 COLLECT 指令类型 Data: anyData, } payload, err := gproto.Marshal(instruction) diff --git a/internal/app/service/device/proto/device.pb.go b/internal/app/service/device/proto/device.pb.go index 68d3cd6..573d4ff 100644 --- a/internal/app/service/device/proto/device.pb.go +++ b/internal/app/service/device/proto/device.pb.go @@ -235,8 +235,7 @@ func (x *BatchCollectCommand) GetTasks() []*CollectTask { // 定义了单个采集任务的“意图”。现在直接包含平台生成的原始485指令,并带上总线号。 type CollectTask struct { 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 sizeCache protoimpl.SizeCache } @@ -271,13 +270,6 @@ func (*CollectTask) Descriptor() ([]byte, []int) { 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 { if x != nil { return x.Command @@ -353,10 +345,8 @@ const file_device_proto_rawDesc = "" + "\x04data\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x04data\"g\n" + "\x13BatchCollectCommand\x12%\n" + "\x0ecorrelation_id\x18\x01 \x01(\tR\rcorrelationId\x12)\n" + - "\x05tasks\x18\x02 \x03(\v2\x13.device.CollectTaskR\x05tasks\"]\n" + - "\vCollectTask\x12\x1d\n" + - "\n" + - "bus_number\x18\x01 \x01(\x05R\tbusNumber\x12/\n" + + "\x05tasks\x18\x02 \x03(\v2\x13.device.CollectTaskR\x05tasks\">\n" + + "\vCollectTask\x12/\n" + "\acommand\x18\x02 \x01(\v2\x15.device.Raw485CommandR\acommand\"N\n" + "\rCollectResult\x12%\n" + "\x0ecorrelation_id\x18\x01 \x01(\tR\rcorrelationId\x12\x16\n" + diff --git a/internal/app/service/device/proto/device.proto b/internal/app/service/device/proto/device.proto index 539f9ae..af23a51 100644 --- a/internal/app/service/device/proto/device.proto +++ b/internal/app/service/device/proto/device.proto @@ -40,7 +40,6 @@ message BatchCollectCommand { // CollectTask // 定义了单个采集任务的“意图”。现在直接包含平台生成的原始485指令,并带上总线号。 message CollectTask { - int32 bus_number = 1; // 总线号,用于指示单片机将指令发送到哪个总线 Raw485Command command = 2; // 平台生成的原始485指令 } diff --git a/internal/infra/utils/command_generater/modbus_rtu.go b/internal/infra/utils/command_generater/modbus_rtu.go index 2b5f950..f9afbe1 100644 --- a/internal/infra/utils/command_generater/modbus_rtu.go +++ b/internal/infra/utils/command_generater/modbus_rtu.go @@ -100,7 +100,7 @@ func GenerateModbusRTUReadCommand(slaveAddress uint8, functionCode ModbusFunctio return command, nil } -// GenerateModbusRTUWriteCommand 生成Modbus RTU写入单个线圈的指令 +// GenerateModbusRTUSwitchCommand 生成Modbus RTU写入单个线圈的指令 // 该函数专门用于生成 Modbus RTU 的写入单个线圈 (0x05) 指令,用于控制开关。 // // 参数: @@ -113,7 +113,7 @@ func GenerateModbusRTUReadCommand(slaveAddress uint8, functionCode ModbusFunctio // // []byte: 完整的Modbus RTU指令字节切片。 // error: 如果参数无效或生成过程中出现错误,则返回错误信息。 -func GenerateModbusRTUWriteCommand(slaveAddress uint8, coilAddress uint16, onOffState bool) ([]byte, error) { +func GenerateModbusRTUSwitchCommand(slaveAddress uint8, coilAddress uint16, onOffState bool) ([]byte, error) { // 1. 校验从站地址 if slaveAddress == 0 || slaveAddress > 247 { return nil, fmt.Errorf("从站地址无效: %d, 必须在1-247之间", slaveAddress)