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

View File

@@ -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" +

View File

@@ -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指令
} }

View File

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