issue_25 #26

Merged
huang merged 17 commits from issue_25 into main 2025-09-30 00:33:34 +08:00
4 changed files with 58 additions and 38 deletions
Showing only changes of commit 56dbb680a7 - Show all commits

View File

@@ -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)

View File

@@ -235,7 +235,6 @@ 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指令
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" +

View File

@@ -40,7 +40,6 @@ message BatchCollectCommand {
// CollectTask
// 定义了单个采集任务的“意图”。现在直接包含平台生成的原始485指令并带上总线号。
message CollectTask {
int32 bus_number = 1; // 总线号,用于指示单片机将指令发送到哪个总线
Raw485Command command = 2; // 平台生成的原始485指令
}

View File

@@ -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)