From 0eb7c6f3711a961c2cf004d5cf462257377b08ea Mon Sep 17 00:00:00 2001 From: huang <1724659546@qq.com> Date: Sat, 29 Nov 2025 17:06:09 +0800 Subject: [PATCH] proto --- .../ota-upgrade-and-log-monitoring/index.md | 13 + internal/infra/transport/proto/device.pb.go | 530 ++++++++++++++++-- internal/infra/transport/proto/device.proto | 69 ++- 3 files changed, 553 insertions(+), 59 deletions(-) create mode 100644 design/ota-upgrade-and-log-monitoring/index.md diff --git a/design/ota-upgrade-and-log-monitoring/index.md b/design/ota-upgrade-and-log-monitoring/index.md new file mode 100644 index 0000000..abfecd2 --- /dev/null +++ b/design/ota-upgrade-and-log-monitoring/index.md @@ -0,0 +1,13 @@ +# 需求 + +支持主控设备ota升级和远程查看日志 + +## issue + +http://git.huangwc.com/pig/pig-farm-controller/issues/71 + +# 开发计划 + +## OTA 升级 + +- [ ] 增加一个proto包, 用于封装ota升级包 diff --git a/internal/infra/transport/proto/device.pb.go b/internal/infra/transport/proto/device.pb.go index 6a80a9a..46b8ca3 100644 --- a/internal/infra/transport/proto/device.pb.go +++ b/internal/infra/transport/proto/device.pb.go @@ -21,6 +21,123 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// LogLevel 定义了日志的严重级别。 +type LogLevel int32 + +const ( + LogLevel_LOG_LEVEL_UNSPECIFIED LogLevel = 0 // 未指定 + LogLevel_DEBUG LogLevel = 1 // 调试信息 + LogLevel_INFO LogLevel = 2 // 普通信息 + LogLevel_WARN LogLevel = 3 // 警告 + LogLevel_ERROR LogLevel = 4 // 错误 +) + +// Enum value maps for LogLevel. +var ( + LogLevel_name = map[int32]string{ + 0: "LOG_LEVEL_UNSPECIFIED", + 1: "DEBUG", + 2: "INFO", + 3: "WARN", + 4: "ERROR", + } + LogLevel_value = map[string]int32{ + "LOG_LEVEL_UNSPECIFIED": 0, + "DEBUG": 1, + "INFO": 2, + "WARN": 3, + "ERROR": 4, + } +) + +func (x LogLevel) Enum() *LogLevel { + p := new(LogLevel) + *p = x + return p +} + +func (x LogLevel) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (LogLevel) Descriptor() protoreflect.EnumDescriptor { + return file_device_proto_enumTypes[0].Descriptor() +} + +func (LogLevel) Type() protoreflect.EnumType { + return &file_device_proto_enumTypes[0] +} + +func (x LogLevel) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use LogLevel.Descriptor instead. +func (LogLevel) EnumDescriptor() ([]byte, []int) { + return file_device_proto_rawDescGZIP(), []int{0} +} + +// LogEntry 代表一条由设备生成的日志记录。 +type LogEntry struct { + state protoimpl.MessageState `protogen:"open.v1"` + TimestampUnix int64 `protobuf:"varint,1,opt,name=timestamp_unix,json=timestampUnix,proto3" json:"timestamp_unix,omitempty"` // 日志生成的Unix时间戳 (秒) + Level LogLevel `protobuf:"varint,2,opt,name=level,proto3,enum=device.LogLevel" json:"level,omitempty"` // 日志级别 + Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` // 日志内容 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LogEntry) Reset() { + *x = LogEntry{} + mi := &file_device_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LogEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogEntry) ProtoMessage() {} + +func (x *LogEntry) ProtoReflect() protoreflect.Message { + mi := &file_device_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogEntry.ProtoReflect.Descriptor instead. +func (*LogEntry) Descriptor() ([]byte, []int) { + return file_device_proto_rawDescGZIP(), []int{0} +} + +func (x *LogEntry) GetTimestampUnix() int64 { + if x != nil { + return x.TimestampUnix + } + return 0 +} + +func (x *LogEntry) GetLevel() LogLevel { + if x != nil { + return x.Level + } + return LogLevel_LOG_LEVEL_UNSPECIFIED +} + +func (x *LogEntry) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + // 平台生成的原始485指令,单片机直接发送到总线 type Raw485Command struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -32,7 +149,7 @@ type Raw485Command struct { func (x *Raw485Command) Reset() { *x = Raw485Command{} - mi := &file_device_proto_msgTypes[0] + mi := &file_device_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -44,7 +161,7 @@ func (x *Raw485Command) String() string { func (*Raw485Command) ProtoMessage() {} func (x *Raw485Command) ProtoReflect() protoreflect.Message { - mi := &file_device_proto_msgTypes[0] + mi := &file_device_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -57,7 +174,7 @@ func (x *Raw485Command) ProtoReflect() protoreflect.Message { // Deprecated: Use Raw485Command.ProtoReflect.Descriptor instead. func (*Raw485Command) Descriptor() ([]byte, []int) { - return file_device_proto_rawDescGZIP(), []int{0} + return file_device_proto_rawDescGZIP(), []int{1} } func (x *Raw485Command) GetBusNumber() int32 { @@ -74,7 +191,6 @@ func (x *Raw485Command) GetCommandBytes() []byte { return nil } -// BatchCollectCommand // 一个完整的、包含所有元数据的批量采集任务。 type BatchCollectCommand struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -86,7 +202,7 @@ type BatchCollectCommand struct { func (x *BatchCollectCommand) Reset() { *x = BatchCollectCommand{} - mi := &file_device_proto_msgTypes[1] + mi := &file_device_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -98,7 +214,7 @@ func (x *BatchCollectCommand) String() string { func (*BatchCollectCommand) ProtoMessage() {} func (x *BatchCollectCommand) ProtoReflect() protoreflect.Message { - mi := &file_device_proto_msgTypes[1] + mi := &file_device_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -111,7 +227,7 @@ func (x *BatchCollectCommand) ProtoReflect() protoreflect.Message { // Deprecated: Use BatchCollectCommand.ProtoReflect.Descriptor instead. func (*BatchCollectCommand) Descriptor() ([]byte, []int) { - return file_device_proto_rawDescGZIP(), []int{1} + return file_device_proto_rawDescGZIP(), []int{2} } func (x *BatchCollectCommand) GetCorrelationId() string { @@ -128,7 +244,6 @@ func (x *BatchCollectCommand) GetTasks() []*CollectTask { return nil } -// CollectTask // 定义了单个采集任务的“意图”。 type CollectTask struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -139,7 +254,7 @@ type CollectTask struct { func (x *CollectTask) Reset() { *x = CollectTask{} - mi := &file_device_proto_msgTypes[2] + mi := &file_device_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -151,7 +266,7 @@ func (x *CollectTask) String() string { func (*CollectTask) ProtoMessage() {} func (x *CollectTask) ProtoReflect() protoreflect.Message { - mi := &file_device_proto_msgTypes[2] + mi := &file_device_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -164,7 +279,7 @@ func (x *CollectTask) ProtoReflect() protoreflect.Message { // Deprecated: Use CollectTask.ProtoReflect.Descriptor instead. func (*CollectTask) Descriptor() ([]byte, []int) { - return file_device_proto_rawDescGZIP(), []int{2} + return file_device_proto_rawDescGZIP(), []int{3} } func (x *CollectTask) GetCommand() *Raw485Command { @@ -174,7 +289,6 @@ func (x *CollectTask) GetCommand() *Raw485Command { return nil } -// CollectResult // 这是设备响应的、极致精简的数据包。 type CollectResult struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -186,7 +300,7 @@ type CollectResult struct { func (x *CollectResult) Reset() { *x = CollectResult{} - mi := &file_device_proto_msgTypes[3] + mi := &file_device_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -198,7 +312,7 @@ func (x *CollectResult) String() string { func (*CollectResult) ProtoMessage() {} func (x *CollectResult) ProtoReflect() protoreflect.Message { - mi := &file_device_proto_msgTypes[3] + mi := &file_device_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -211,7 +325,7 @@ func (x *CollectResult) ProtoReflect() protoreflect.Message { // Deprecated: Use CollectResult.ProtoReflect.Descriptor instead. func (*CollectResult) Descriptor() ([]byte, []int) { - return file_device_proto_rawDescGZIP(), []int{3} + return file_device_proto_rawDescGZIP(), []int{4} } func (x *CollectResult) GetCorrelationId() string { @@ -228,16 +342,233 @@ func (x *CollectResult) GetValues() []float32 { return nil } -// 指令 (所有从平台下发到设备的数据都应该被包装在这里面) -// 使用 oneof 来替代 google.protobuf.Any,这是嵌入式环境下的标准做法。 -// 它高效、类型安全,且只解码一次。 +// OTA(空中下载)升级指令,包含完整的固件包。 +type OtaUpgradeCommand struct { + state protoimpl.MessageState `protogen:"open.v1"` + FirmwareVersion string `protobuf:"bytes,1,opt,name=firmware_version,json=firmwareVersion,proto3" json:"firmware_version,omitempty"` // 目标固件版本, e.g., "v1.2.3" + FirmwareHash string `protobuf:"bytes,2,opt,name=firmware_hash,json=firmwareHash,proto3" json:"firmware_hash,omitempty"` // 固件包的SHA-256哈希值,用于完整性校验 + FirmwarePackage []byte `protobuf:"bytes,3,opt,name=firmware_package,json=firmwarePackage,proto3" json:"firmware_package,omitempty"` // 完整的固件二进制文件 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *OtaUpgradeCommand) Reset() { + *x = OtaUpgradeCommand{} + mi := &file_device_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *OtaUpgradeCommand) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OtaUpgradeCommand) ProtoMessage() {} + +func (x *OtaUpgradeCommand) ProtoReflect() protoreflect.Message { + mi := &file_device_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OtaUpgradeCommand.ProtoReflect.Descriptor instead. +func (*OtaUpgradeCommand) Descriptor() ([]byte, []int) { + return file_device_proto_rawDescGZIP(), []int{5} +} + +func (x *OtaUpgradeCommand) GetFirmwareVersion() string { + if x != nil { + return x.FirmwareVersion + } + return "" +} + +func (x *OtaUpgradeCommand) GetFirmwareHash() string { + if x != nil { + return x.FirmwareHash + } + return "" +} + +func (x *OtaUpgradeCommand) GetFirmwarePackage() []byte { + if x != nil { + return x.FirmwarePackage + } + return nil +} + +// 设备端执行OTA升级后的状态报告 (上行)。 +type OtaUpgradeStatus struct { + state protoimpl.MessageState `protogen:"open.v1"` + // 状态码: 0=成功, 1=哈希校验失败, 2=烧录失败, 3=空间不足, 99=其他未知错误 + StatusCode int32 `protobuf:"varint,1,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"` + // 设备当前运行的固件版本 (升级后或升级失败时) + CurrentFirmwareVersion string `protobuf:"bytes,2,opt,name=current_firmware_version,json=currentFirmwareVersion,proto3" json:"current_firmware_version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *OtaUpgradeStatus) Reset() { + *x = OtaUpgradeStatus{} + mi := &file_device_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *OtaUpgradeStatus) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OtaUpgradeStatus) ProtoMessage() {} + +func (x *OtaUpgradeStatus) ProtoReflect() protoreflect.Message { + mi := &file_device_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OtaUpgradeStatus.ProtoReflect.Descriptor instead. +func (*OtaUpgradeStatus) Descriptor() ([]byte, []int) { + return file_device_proto_rawDescGZIP(), []int{6} +} + +func (x *OtaUpgradeStatus) GetStatusCode() int32 { + if x != nil { + return x.StatusCode + } + return 0 +} + +func (x *OtaUpgradeStatus) GetCurrentFirmwareVersion() string { + if x != nil { + return x.CurrentFirmwareVersion + } + return "" +} + +// 控制设备日志上传的指令 (下行)。 +type ControlLogUploadCommand struct { + state protoimpl.MessageState `protogen:"open.v1"` + Enable bool `protobuf:"varint,1,opt,name=enable,proto3" json:"enable,omitempty"` // true = 开始上传, false = 停止上传 + DurationSeconds uint32 `protobuf:"varint,2,opt,name=duration_seconds,json=durationSeconds,proto3" json:"duration_seconds,omitempty"` // 指定上传持续时间(秒)。 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ControlLogUploadCommand) Reset() { + *x = ControlLogUploadCommand{} + mi := &file_device_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ControlLogUploadCommand) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ControlLogUploadCommand) ProtoMessage() {} + +func (x *ControlLogUploadCommand) ProtoReflect() protoreflect.Message { + mi := &file_device_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ControlLogUploadCommand.ProtoReflect.Descriptor instead. +func (*ControlLogUploadCommand) Descriptor() ([]byte, []int) { + return file_device_proto_rawDescGZIP(), []int{7} +} + +func (x *ControlLogUploadCommand) GetEnable() bool { + if x != nil { + return x.Enable + } + return false +} + +func (x *ControlLogUploadCommand) GetDurationSeconds() uint32 { + if x != nil { + return x.DurationSeconds + } + return 0 +} + +// 设备用于向平台批量上传日志的请求 (上行)。 +type LogUploadRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Entries []*LogEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` // 一批日志条目 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LogUploadRequest) Reset() { + *x = LogUploadRequest{} + mi := &file_device_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LogUploadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogUploadRequest) ProtoMessage() {} + +func (x *LogUploadRequest) ProtoReflect() protoreflect.Message { + mi := &file_device_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogUploadRequest.ProtoReflect.Descriptor instead. +func (*LogUploadRequest) Descriptor() ([]byte, []int) { + return file_device_proto_rawDescGZIP(), []int{8} +} + +func (x *LogUploadRequest) GetEntries() []*LogEntry { + if x != nil { + return x.Entries + } + return nil +} + +// Instruction 封装了所有与设备间的通信。 +// 使用 oneof 来确保每个消息只有一个负载类型,这在嵌入式系统中是高效且类型安全的。 type Instruction struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Payload: // // *Instruction_Raw_485Command // *Instruction_BatchCollectCommand + // *Instruction_OtaUpgradeCommand + // *Instruction_ControlLogUploadCommand // *Instruction_CollectResult + // *Instruction_OtaUpgradeStatus + // *Instruction_LogUploadRequest Payload isInstruction_Payload `protobuf_oneof:"payload"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -245,7 +576,7 @@ type Instruction struct { func (x *Instruction) Reset() { *x = Instruction{} - mi := &file_device_proto_msgTypes[4] + mi := &file_device_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -257,7 +588,7 @@ func (x *Instruction) String() string { func (*Instruction) ProtoMessage() {} func (x *Instruction) ProtoReflect() protoreflect.Message { - mi := &file_device_proto_msgTypes[4] + mi := &file_device_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -270,7 +601,7 @@ func (x *Instruction) ProtoReflect() protoreflect.Message { // Deprecated: Use Instruction.ProtoReflect.Descriptor instead. func (*Instruction) Descriptor() ([]byte, []int) { - return file_device_proto_rawDescGZIP(), []int{4} + return file_device_proto_rawDescGZIP(), []int{9} } func (x *Instruction) GetPayload() isInstruction_Payload { @@ -298,6 +629,24 @@ func (x *Instruction) GetBatchCollectCommand() *BatchCollectCommand { return nil } +func (x *Instruction) GetOtaUpgradeCommand() *OtaUpgradeCommand { + if x != nil { + if x, ok := x.Payload.(*Instruction_OtaUpgradeCommand); ok { + return x.OtaUpgradeCommand + } + } + return nil +} + +func (x *Instruction) GetControlLogUploadCommand() *ControlLogUploadCommand { + if x != nil { + if x, ok := x.Payload.(*Instruction_ControlLogUploadCommand); ok { + return x.ControlLogUploadCommand + } + } + return nil +} + func (x *Instruction) GetCollectResult() *CollectResult { if x != nil { if x, ok := x.Payload.(*Instruction_CollectResult); ok { @@ -307,11 +656,30 @@ func (x *Instruction) GetCollectResult() *CollectResult { return nil } +func (x *Instruction) GetOtaUpgradeStatus() *OtaUpgradeStatus { + if x != nil { + if x, ok := x.Payload.(*Instruction_OtaUpgradeStatus); ok { + return x.OtaUpgradeStatus + } + } + return nil +} + +func (x *Instruction) GetLogUploadRequest() *LogUploadRequest { + if x != nil { + if x, ok := x.Payload.(*Instruction_LogUploadRequest); ok { + return x.LogUploadRequest + } + } + return nil +} + type isInstruction_Payload interface { isInstruction_Payload() } type Instruction_Raw_485Command struct { + // --- 下行指令 (平台 -> 设备) --- Raw_485Command *Raw485Command `protobuf:"bytes,1,opt,name=raw_485_command,json=raw485Command,proto3,oneof"` } @@ -319,21 +687,50 @@ type Instruction_BatchCollectCommand struct { BatchCollectCommand *BatchCollectCommand `protobuf:"bytes,2,opt,name=batch_collect_command,json=batchCollectCommand,proto3,oneof"` } +type Instruction_OtaUpgradeCommand struct { + OtaUpgradeCommand *OtaUpgradeCommand `protobuf:"bytes,3,opt,name=ota_upgrade_command,json=otaUpgradeCommand,proto3,oneof"` +} + +type Instruction_ControlLogUploadCommand struct { + ControlLogUploadCommand *ControlLogUploadCommand `protobuf:"bytes,4,opt,name=control_log_upload_command,json=controlLogUploadCommand,proto3,oneof"` +} + type Instruction_CollectResult struct { - CollectResult *CollectResult `protobuf:"bytes,3,opt,name=collect_result,json=collectResult,proto3,oneof"` // ADDED:用于上行数据 + // --- 上行数据 (设备 -> 平台) --- + CollectResult *CollectResult `protobuf:"bytes,101,opt,name=collect_result,json=collectResult,proto3,oneof"` +} + +type Instruction_OtaUpgradeStatus struct { + OtaUpgradeStatus *OtaUpgradeStatus `protobuf:"bytes,102,opt,name=ota_upgrade_status,json=otaUpgradeStatus,proto3,oneof"` +} + +type Instruction_LogUploadRequest struct { + LogUploadRequest *LogUploadRequest `protobuf:"bytes,103,opt,name=log_upload_request,json=logUploadRequest,proto3,oneof"` } func (*Instruction_Raw_485Command) isInstruction_Payload() {} func (*Instruction_BatchCollectCommand) isInstruction_Payload() {} +func (*Instruction_OtaUpgradeCommand) isInstruction_Payload() {} + +func (*Instruction_ControlLogUploadCommand) isInstruction_Payload() {} + func (*Instruction_CollectResult) isInstruction_Payload() {} +func (*Instruction_OtaUpgradeStatus) isInstruction_Payload() {} + +func (*Instruction_LogUploadRequest) isInstruction_Payload() {} + var File_device_proto protoreflect.FileDescriptor const file_device_proto_rawDesc = "" + "\n" + - "\fdevice.proto\x12\x06device\"S\n" + + "\fdevice.proto\x12\x06device\"s\n" + + "\bLogEntry\x12%\n" + + "\x0etimestamp_unix\x18\x01 \x01(\x03R\rtimestampUnix\x12&\n" + + "\x05level\x18\x02 \x01(\x0e2\x10.device.LogLevelR\x05level\x12\x18\n" + + "\amessage\x18\x03 \x01(\tR\amessage\"S\n" + "\rRaw485Command\x12\x1d\n" + "\n" + "bus_number\x18\x01 \x01(\x05R\tbusNumber\x12#\n" + @@ -345,12 +742,35 @@ const file_device_proto_rawDesc = "" + "\acommand\x18\x01 \x01(\v2\x15.device.Raw485CommandR\acommand\"N\n" + "\rCollectResult\x12%\n" + "\x0ecorrelation_id\x18\x01 \x01(\tR\rcorrelationId\x12\x16\n" + - "\x06values\x18\x02 \x03(\x02R\x06values\"\xec\x01\n" + + "\x06values\x18\x02 \x03(\x02R\x06values\"\x8e\x01\n" + + "\x11OtaUpgradeCommand\x12)\n" + + "\x10firmware_version\x18\x01 \x01(\tR\x0ffirmwareVersion\x12#\n" + + "\rfirmware_hash\x18\x02 \x01(\tR\ffirmwareHash\x12)\n" + + "\x10firmware_package\x18\x03 \x01(\fR\x0ffirmwarePackage\"m\n" + + "\x10OtaUpgradeStatus\x12\x1f\n" + + "\vstatus_code\x18\x01 \x01(\x05R\n" + + "statusCode\x128\n" + + "\x18current_firmware_version\x18\x02 \x01(\tR\x16currentFirmwareVersion\"\\\n" + + "\x17ControlLogUploadCommand\x12\x16\n" + + "\x06enable\x18\x01 \x01(\bR\x06enable\x12)\n" + + "\x10duration_seconds\x18\x02 \x01(\rR\x0fdurationSeconds\">\n" + + "\x10LogUploadRequest\x12*\n" + + "\aentries\x18\x01 \x03(\v2\x10.device.LogEntryR\aentries\"\xad\x04\n" + "\vInstruction\x12?\n" + "\x0fraw_485_command\x18\x01 \x01(\v2\x15.device.Raw485CommandH\x00R\rraw485Command\x12Q\n" + - "\x15batch_collect_command\x18\x02 \x01(\v2\x1b.device.BatchCollectCommandH\x00R\x13batchCollectCommand\x12>\n" + - "\x0ecollect_result\x18\x03 \x01(\v2\x15.device.CollectResultH\x00R\rcollectResultB\t\n" + - "\apayloadB\x1eZ\x1cinternal/domain/device/protob\x06proto3" + "\x15batch_collect_command\x18\x02 \x01(\v2\x1b.device.BatchCollectCommandH\x00R\x13batchCollectCommand\x12K\n" + + "\x13ota_upgrade_command\x18\x03 \x01(\v2\x19.device.OtaUpgradeCommandH\x00R\x11otaUpgradeCommand\x12^\n" + + "\x1acontrol_log_upload_command\x18\x04 \x01(\v2\x1f.device.ControlLogUploadCommandH\x00R\x17controlLogUploadCommand\x12>\n" + + "\x0ecollect_result\x18e \x01(\v2\x15.device.CollectResultH\x00R\rcollectResult\x12H\n" + + "\x12ota_upgrade_status\x18f \x01(\v2\x18.device.OtaUpgradeStatusH\x00R\x10otaUpgradeStatus\x12H\n" + + "\x12log_upload_request\x18g \x01(\v2\x18.device.LogUploadRequestH\x00R\x10logUploadRequestB\t\n" + + "\apayload*O\n" + + "\bLogLevel\x12\x19\n" + + "\x15LOG_LEVEL_UNSPECIFIED\x10\x00\x12\t\n" + + "\x05DEBUG\x10\x01\x12\b\n" + + "\x04INFO\x10\x02\x12\b\n" + + "\x04WARN\x10\x03\x12\t\n" + + "\x05ERROR\x10\x04B\x1eZ\x1cinternal/domain/device/protob\x06proto3" var ( file_device_proto_rawDescOnce sync.Once @@ -364,25 +784,38 @@ func file_device_proto_rawDescGZIP() []byte { return file_device_proto_rawDescData } -var file_device_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_device_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_device_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_device_proto_goTypes = []any{ - (*Raw485Command)(nil), // 0: device.Raw485Command - (*BatchCollectCommand)(nil), // 1: device.BatchCollectCommand - (*CollectTask)(nil), // 2: device.CollectTask - (*CollectResult)(nil), // 3: device.CollectResult - (*Instruction)(nil), // 4: device.Instruction + (LogLevel)(0), // 0: device.LogLevel + (*LogEntry)(nil), // 1: device.LogEntry + (*Raw485Command)(nil), // 2: device.Raw485Command + (*BatchCollectCommand)(nil), // 3: device.BatchCollectCommand + (*CollectTask)(nil), // 4: device.CollectTask + (*CollectResult)(nil), // 5: device.CollectResult + (*OtaUpgradeCommand)(nil), // 6: device.OtaUpgradeCommand + (*OtaUpgradeStatus)(nil), // 7: device.OtaUpgradeStatus + (*ControlLogUploadCommand)(nil), // 8: device.ControlLogUploadCommand + (*LogUploadRequest)(nil), // 9: device.LogUploadRequest + (*Instruction)(nil), // 10: device.Instruction } var file_device_proto_depIdxs = []int32{ - 2, // 0: device.BatchCollectCommand.tasks:type_name -> device.CollectTask - 0, // 1: device.CollectTask.command:type_name -> device.Raw485Command - 0, // 2: device.Instruction.raw_485_command:type_name -> device.Raw485Command - 1, // 3: device.Instruction.batch_collect_command:type_name -> device.BatchCollectCommand - 3, // 4: device.Instruction.collect_result:type_name -> device.CollectResult - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 0, // 0: device.LogEntry.level:type_name -> device.LogLevel + 4, // 1: device.BatchCollectCommand.tasks:type_name -> device.CollectTask + 2, // 2: device.CollectTask.command:type_name -> device.Raw485Command + 1, // 3: device.LogUploadRequest.entries:type_name -> device.LogEntry + 2, // 4: device.Instruction.raw_485_command:type_name -> device.Raw485Command + 3, // 5: device.Instruction.batch_collect_command:type_name -> device.BatchCollectCommand + 6, // 6: device.Instruction.ota_upgrade_command:type_name -> device.OtaUpgradeCommand + 8, // 7: device.Instruction.control_log_upload_command:type_name -> device.ControlLogUploadCommand + 5, // 8: device.Instruction.collect_result:type_name -> device.CollectResult + 7, // 9: device.Instruction.ota_upgrade_status:type_name -> device.OtaUpgradeStatus + 9, // 10: device.Instruction.log_upload_request:type_name -> device.LogUploadRequest + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_device_proto_init() } @@ -390,23 +823,28 @@ func file_device_proto_init() { if File_device_proto != nil { return } - file_device_proto_msgTypes[4].OneofWrappers = []any{ + file_device_proto_msgTypes[9].OneofWrappers = []any{ (*Instruction_Raw_485Command)(nil), (*Instruction_BatchCollectCommand)(nil), + (*Instruction_OtaUpgradeCommand)(nil), + (*Instruction_ControlLogUploadCommand)(nil), (*Instruction_CollectResult)(nil), + (*Instruction_OtaUpgradeStatus)(nil), + (*Instruction_LogUploadRequest)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_device_proto_rawDesc), len(file_device_proto_rawDesc)), - NumEnums: 0, - NumMessages: 5, + NumEnums: 1, + NumMessages: 10, NumExtensions: 0, NumServices: 0, }, GoTypes: file_device_proto_goTypes, DependencyIndexes: file_device_proto_depIdxs, + EnumInfos: file_device_proto_enumTypes, MessageInfos: file_device_proto_msgTypes, }.Build() File_device_proto = out.File diff --git a/internal/infra/transport/proto/device.proto b/internal/infra/transport/proto/device.proto index f987c43..c403379 100644 --- a/internal/infra/transport/proto/device.proto +++ b/internal/infra/transport/proto/device.proto @@ -2,11 +2,27 @@ syntax = "proto3"; package device; -// import "google/protobuf/any.proto"; // REMOVED: Not suitable for embedded systems. - option go_package = "internal/domain/device/proto"; -// --- Concrete Command & Data Structures --- +// --- 日志相关 --- + +// LogLevel 定义了日志的严重级别。 +enum LogLevel { + LOG_LEVEL_UNSPECIFIED = 0; // 未指定 + DEBUG = 1; // 调试信息 + INFO = 2; // 普通信息 + WARN = 3; // 警告 + ERROR = 4; // 错误 +} + +// LogEntry 代表一条由设备生成的日志记录。 +message LogEntry { + int64 timestamp_unix = 1; // 日志生成的Unix时间戳 (秒) + LogLevel level = 2; // 日志级别 + string message = 3; // 日志内容 +} + +// --- 核心指令与数据结构 --- // 平台生成的原始485指令,单片机直接发送到总线 message Raw485Command { @@ -14,38 +30,65 @@ message Raw485Command { bytes command_bytes = 2; // 原始485指令的字节数组 } -// BatchCollectCommand // 一个完整的、包含所有元数据的批量采集任务。 message BatchCollectCommand { string correlation_id = 1; // 用于关联请求和响应的唯一ID repeated CollectTask tasks = 2; // 采集任务列表 } -// CollectTask // 定义了单个采集任务的“意图”。 message CollectTask { Raw485Command command = 1; // 平台生成的原始485指令 } -// CollectResult // 这是设备响应的、极致精简的数据包。 message CollectResult { string correlation_id = 1; // 从下行指令中原样返回的关联ID repeated float values = 2; // 按预定顺序排列的采集值 } +// OTA(空中下载)升级指令,包含完整的固件包。 +message OtaUpgradeCommand { + string firmware_version = 1; // 目标固件版本, e.g., "v1.2.3" + string firmware_hash = 2; // 固件包的SHA-256哈希值,用于完整性校验 + bytes firmware_package = 3; // 完整的固件二进制文件 +} -// --- Main Downlink Instruction Wrapper --- +// 设备端执行OTA升级后的状态报告 (上行)。 +message OtaUpgradeStatus { + // 状态码: 0=成功, 1=哈希校验失败, 2=烧录失败, 3=空间不足, 99=其他未知错误 + int32 status_code = 1; + // 设备当前运行的固件版本 (升级后或升级失败时) + string current_firmware_version = 2; +} -// 指令 (所有从平台下发到设备的数据都应该被包装在这里面) -// 使用 oneof 来替代 google.protobuf.Any,这是嵌入式环境下的标准做法。 -// 它高效、类型安全,且只解码一次。 +// 控制设备日志上传的指令 (下行)。 +message ControlLogUploadCommand { + bool enable = 1; // true = 开始上传, false = 停止上传 + uint32 duration_seconds = 2; // 指定上传持续时间(秒)。 +} + +// 设备用于向平台批量上传日志的请求 (上行)。 +message LogUploadRequest { + repeated LogEntry entries = 1; // 一批日志条目 +} + + +// --- 顶层指令包装器 --- + +// Instruction 封装了所有与设备间的通信。 +// 使用 oneof 来确保每个消息只有一个负载类型,这在嵌入式系统中是高效且类型安全的。 message Instruction { oneof payload { + // --- 下行指令 (平台 -> 设备) --- Raw485Command raw_485_command = 1; BatchCollectCommand batch_collect_command = 2; - CollectResult collect_result = 3; // ADDED:用于上行数据 - // 如果未来有其他指令类型,比如开关控制,可以直接在这里添加 - // SwitchCommand switch_command = 3; + OtaUpgradeCommand ota_upgrade_command = 3; + ControlLogUploadCommand control_log_upload_command = 4; + + // --- 上行数据 (设备 -> 平台) --- + CollectResult collect_result = 101; + OtaUpgradeStatus ota_upgrade_status = 102; + LogUploadRequest log_upload_request = 103; } }