定义otatask模型
This commit is contained in:
@@ -85,27 +85,27 @@
|
|||||||
// PrepareUpdateReq: 平台发送给设备,通知设备准备开始 OTA 升级
|
// PrepareUpdateReq: 平台发送给设备,通知设备准备开始 OTA 升级
|
||||||
message PrepareUpdateReq {
|
message PrepareUpdateReq {
|
||||||
string version = 1; // 新固件版本号
|
string version = 1; // 新固件版本号
|
||||||
string task_id = 2; // 升级任务唯一ID
|
uint32 task_id = 2; // 升级任务唯一ID
|
||||||
string manifest_md5 = 3; // 清单文件的 MD5 校验和,用于设备初步校验清单文件完整性
|
string manifest_md5 = 3; // 清单文件的 MD5 校验和,用于设备初步校验清单文件完整性
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestFile: 设备向平台请求特定文件 (包括清单文件和固件文件)
|
// RequestFile: 设备向平台请求特定文件 (包括清单文件和固件文件)
|
||||||
message RequestFile {
|
message RequestFile {
|
||||||
string task_id = 1; // 升级任务ID
|
uint32 task_id = 1; // 升级任务ID
|
||||||
string filepath = 2; // 请求的文件路径 (例如 "/manifest.json" 或 "/main.py")
|
string filepath = 2; // 请求的文件路径 (例如 "/manifest.json" 或 "/main.py")
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileResponse: 平台响应设备请求,发送单个文件的完整内容
|
// FileResponse: 平台响应设备请求,发送单个文件的完整内容
|
||||||
// LoRa 传输层会自动处理分片和重组,因此应用层可以直接发送完整的单个文件内容
|
// LoRa 传输层会自动处理分片和重组,因此应用层可以直接发送完整的单个文件内容
|
||||||
message FileResponse {
|
message FileResponse {
|
||||||
string task_id = 1; // 升级任务ID
|
uint32 task_id = 1; // 升级任务ID
|
||||||
string filepath = 2; // 设备上的目标路径 (例如 "/manifest.json" 或 "/main.py")
|
string filepath = 2; // 设备上的目标路径 (例如 "/manifest.json" 或 "/main.py")
|
||||||
bytes content = 3; // 文件的完整内容
|
bytes content = 3; // 文件的完整内容
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateStatusReport: 设备向平台报告升级状态
|
// UpdateStatusReport: 设备向平台报告升级状态
|
||||||
message UpdateStatusReport {
|
message UpdateStatusReport {
|
||||||
string task_id = 1; // 升级任务ID
|
uint32 task_id = 1; // 升级任务ID
|
||||||
string current_version = 2; // 操作完成后的当前版本
|
string current_version = 2; // 操作完成后的当前版本
|
||||||
enum Status {
|
enum Status {
|
||||||
STATUS_UNKNOWN = 0;
|
STATUS_UNKNOWN = 0;
|
||||||
|
|||||||
@@ -153,6 +153,44 @@ func (PendingCollection) TableName() string {
|
|||||||
return "pending_collections"
|
return "pending_collections"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- OTA 升级任务 ---
|
||||||
|
|
||||||
|
// OTATaskStatus 定义 OTA 升级任务的状态
|
||||||
|
type OTATaskStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
OTATaskStatusPending OTATaskStatus = "待开始" // 任务已创建,等待下发
|
||||||
|
OTATaskStatusInProgress OTATaskStatus = "进行中" // 任务已下发,设备正在处理
|
||||||
|
OTATaskStatusSuccess OTATaskStatus = "成功" // 设备报告升级成功,新固件已运行
|
||||||
|
OTATaskStatusAlreadyUpToDate OTATaskStatus = "版本已是最新" // 设备报告版本已是最新,未执行升级
|
||||||
|
OTATaskStatusFailedPreCheck OTATaskStatus = "预检失败" // 设备报告升级前检查失败 (如拒绝降级、准备分区失败)
|
||||||
|
OTATaskStatusFailedDownload OTATaskStatus = "下载或校验失败" // 设备报告文件下载或校验失败 (包括清单文件和固件文件)
|
||||||
|
OTATaskStatusFailedRollback OTATaskStatus = "固件回滚" // 新固件启动失败,设备自动回滚
|
||||||
|
OTATaskStatusTimedOut OTATaskStatus = "超时" // 平台在超时后仍未收到最终报告
|
||||||
|
OTATaskStatusPlatformError OTATaskStatus = "平台内部错误" // 平台处理过程中发生的非设备报告错误
|
||||||
|
)
|
||||||
|
|
||||||
|
// OTATask 记录一次 OTA 升级任务的详细信息
|
||||||
|
type OTATask struct {
|
||||||
|
// ID 是数据库自增主键,将作为 task_id 在平台与设备间通信
|
||||||
|
ID uint32 `gorm:"primaryKey"`
|
||||||
|
// CreatedAt 是任务创建和开始的时间,作为联合主键方便只查询热点数据
|
||||||
|
CreatedAt time.Time `gorm:"primaryKey"`
|
||||||
|
|
||||||
|
AreaControllerID uint32 `gorm:"not null;index;comment:目标区域主控的ID"`
|
||||||
|
TargetVersion string `gorm:"type:varchar(32);not null;comment:目标固件版本号"`
|
||||||
|
Status OTATaskStatus `gorm:"type:varchar(32);not null;index;comment:任务状态"`
|
||||||
|
ErrorMessage string `gorm:"type:text;comment:错误信息,如果任务失败"`
|
||||||
|
FailedFilePath string `gorm:"type:text;comment:失败时关联的文件路径"`
|
||||||
|
CompletedAt *time.Time `gorm:"comment:任务完成(成功或失败)的时间"`
|
||||||
|
FinalReportedVersion string `gorm:"type:varchar(32);comment:任务结束后,设备上报的最终固件版本"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableName 自定义 GORM 使用的数据库表名
|
||||||
|
func (OTATask) TableName() string {
|
||||||
|
return "ota_tasks"
|
||||||
|
}
|
||||||
|
|
||||||
// --- 用户审计日志 ---
|
// --- 用户审计日志 ---
|
||||||
|
|
||||||
// --- 审计日志状态常量 ---
|
// --- 审计日志状态常量 ---
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ func (x *Pong) GetFirmwareVersion() string {
|
|||||||
type PrepareUpdateReq struct {
|
type PrepareUpdateReq struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` // 新固件版本号
|
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` // 新固件版本号
|
||||||
TaskId string `protobuf:"bytes,2,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` // 升级任务唯一ID
|
TaskId uint32 `protobuf:"varint,2,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` // 升级任务唯一ID
|
||||||
ManifestMd5 string `protobuf:"bytes,3,opt,name=manifest_md5,json=manifestMd5,proto3" json:"manifest_md5,omitempty"` // 清单文件的 MD5 校验和,用于设备初步校验清单文件完整性
|
ManifestMd5 string `protobuf:"bytes,3,opt,name=manifest_md5,json=manifestMd5,proto3" json:"manifest_md5,omitempty"` // 清单文件的 MD5 校验和,用于设备初步校验清单文件完整性
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -419,11 +419,11 @@ func (x *PrepareUpdateReq) GetVersion() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PrepareUpdateReq) GetTaskId() string {
|
func (x *PrepareUpdateReq) GetTaskId() uint32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.TaskId
|
return x.TaskId
|
||||||
}
|
}
|
||||||
return ""
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PrepareUpdateReq) GetManifestMd5() string {
|
func (x *PrepareUpdateReq) GetManifestMd5() string {
|
||||||
@@ -436,8 +436,8 @@ func (x *PrepareUpdateReq) GetManifestMd5() string {
|
|||||||
// RequestFile: 设备向平台请求特定文件 (包括清单文件和固件文件) (上行)
|
// RequestFile: 设备向平台请求特定文件 (包括清单文件和固件文件) (上行)
|
||||||
type RequestFile struct {
|
type RequestFile struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` // 升级任务ID
|
TaskId uint32 `protobuf:"varint,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` // 升级任务ID
|
||||||
Filepath string `protobuf:"bytes,2,opt,name=filepath,proto3" json:"filepath,omitempty"` // 请求的文件路径 (例如 "/manifest.json" 或 "/main.py")
|
Filepath string `protobuf:"bytes,2,opt,name=filepath,proto3" json:"filepath,omitempty"` // 请求的文件路径 (例如 "/manifest.json" 或 "/main.py")
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -472,11 +472,11 @@ func (*RequestFile) Descriptor() ([]byte, []int) {
|
|||||||
return file_device_proto_rawDescGZIP(), []int{7}
|
return file_device_proto_rawDescGZIP(), []int{7}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RequestFile) GetTaskId() string {
|
func (x *RequestFile) GetTaskId() uint32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.TaskId
|
return x.TaskId
|
||||||
}
|
}
|
||||||
return ""
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RequestFile) GetFilepath() string {
|
func (x *RequestFile) GetFilepath() string {
|
||||||
@@ -490,9 +490,9 @@ func (x *RequestFile) GetFilepath() string {
|
|||||||
// LoRa 传输层会自动处理分片和重组,因此应用层可以直接发送完整的单个文件内容
|
// LoRa 传输层会自动处理分片和重组,因此应用层可以直接发送完整的单个文件内容
|
||||||
type FileResponse struct {
|
type FileResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` // 升级任务ID
|
TaskId uint32 `protobuf:"varint,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` // 升级任务ID
|
||||||
Filepath string `protobuf:"bytes,2,opt,name=filepath,proto3" json:"filepath,omitempty"` // 设备上的目标路径 (例如 "/manifest.json" 或 "/main.py")
|
Filepath string `protobuf:"bytes,2,opt,name=filepath,proto3" json:"filepath,omitempty"` // 设备上的目标路径 (例如 "/manifest.json" 或 "/main.py")
|
||||||
Content []byte `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` // 文件的完整内容
|
Content []byte `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` // 文件的完整内容
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -527,11 +527,11 @@ func (*FileResponse) Descriptor() ([]byte, []int) {
|
|||||||
return file_device_proto_rawDescGZIP(), []int{8}
|
return file_device_proto_rawDescGZIP(), []int{8}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *FileResponse) GetTaskId() string {
|
func (x *FileResponse) GetTaskId() uint32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.TaskId
|
return x.TaskId
|
||||||
}
|
}
|
||||||
return ""
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *FileResponse) GetFilepath() string {
|
func (x *FileResponse) GetFilepath() string {
|
||||||
@@ -551,7 +551,7 @@ func (x *FileResponse) GetContent() []byte {
|
|||||||
// UpdateStatusReport: 设备向平台报告升级状态 (上行)
|
// UpdateStatusReport: 设备向平台报告升级状态 (上行)
|
||||||
type UpdateStatusReport struct {
|
type UpdateStatusReport struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
TaskId string `protobuf:"bytes,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` // 升级任务ID
|
TaskId uint32 `protobuf:"varint,1,opt,name=task_id,json=taskId,proto3" json:"task_id,omitempty"` // 升级任务ID
|
||||||
CurrentVersion string `protobuf:"bytes,2,opt,name=current_version,json=currentVersion,proto3" json:"current_version,omitempty"` // 操作完成后的当前版本
|
CurrentVersion string `protobuf:"bytes,2,opt,name=current_version,json=currentVersion,proto3" json:"current_version,omitempty"` // 操作完成后的当前版本
|
||||||
Status UpdateStatusReport_Status `protobuf:"varint,3,opt,name=status,proto3,enum=device.UpdateStatusReport_Status" json:"status,omitempty"` // 升级的最终状态
|
Status UpdateStatusReport_Status `protobuf:"varint,3,opt,name=status,proto3,enum=device.UpdateStatusReport_Status" json:"status,omitempty"` // 升级的最终状态
|
||||||
ErrorMessage string `protobuf:"bytes,4,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` // 人类可读的详细错误信息
|
ErrorMessage string `protobuf:"bytes,4,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` // 人类可读的详细错误信息
|
||||||
@@ -590,11 +590,11 @@ func (*UpdateStatusReport) Descriptor() ([]byte, []int) {
|
|||||||
return file_device_proto_rawDescGZIP(), []int{9}
|
return file_device_proto_rawDescGZIP(), []int{9}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *UpdateStatusReport) GetTaskId() string {
|
func (x *UpdateStatusReport) GetTaskId() uint32 {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.TaskId
|
return x.TaskId
|
||||||
}
|
}
|
||||||
return ""
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *UpdateStatusReport) GetCurrentVersion() string {
|
func (x *UpdateStatusReport) GetCurrentVersion() string {
|
||||||
@@ -845,17 +845,17 @@ const file_device_proto_rawDesc = "" +
|
|||||||
"\x10firmware_version\x18\x01 \x01(\tR\x0ffirmwareVersion\"h\n" +
|
"\x10firmware_version\x18\x01 \x01(\tR\x0ffirmwareVersion\"h\n" +
|
||||||
"\x10PrepareUpdateReq\x12\x18\n" +
|
"\x10PrepareUpdateReq\x12\x18\n" +
|
||||||
"\aversion\x18\x01 \x01(\tR\aversion\x12\x17\n" +
|
"\aversion\x18\x01 \x01(\tR\aversion\x12\x17\n" +
|
||||||
"\atask_id\x18\x02 \x01(\tR\x06taskId\x12!\n" +
|
"\atask_id\x18\x02 \x01(\rR\x06taskId\x12!\n" +
|
||||||
"\fmanifest_md5\x18\x03 \x01(\tR\vmanifestMd5\"B\n" +
|
"\fmanifest_md5\x18\x03 \x01(\tR\vmanifestMd5\"B\n" +
|
||||||
"\vRequestFile\x12\x17\n" +
|
"\vRequestFile\x12\x17\n" +
|
||||||
"\atask_id\x18\x01 \x01(\tR\x06taskId\x12\x1a\n" +
|
"\atask_id\x18\x01 \x01(\rR\x06taskId\x12\x1a\n" +
|
||||||
"\bfilepath\x18\x02 \x01(\tR\bfilepath\"]\n" +
|
"\bfilepath\x18\x02 \x01(\tR\bfilepath\"]\n" +
|
||||||
"\fFileResponse\x12\x17\n" +
|
"\fFileResponse\x12\x17\n" +
|
||||||
"\atask_id\x18\x01 \x01(\tR\x06taskId\x12\x1a\n" +
|
"\atask_id\x18\x01 \x01(\rR\x06taskId\x12\x1a\n" +
|
||||||
"\bfilepath\x18\x02 \x01(\tR\bfilepath\x12\x18\n" +
|
"\bfilepath\x18\x02 \x01(\tR\bfilepath\x12\x18\n" +
|
||||||
"\acontent\x18\x03 \x01(\fR\acontent\"\x9a\x03\n" +
|
"\acontent\x18\x03 \x01(\fR\acontent\"\x9a\x03\n" +
|
||||||
"\x12UpdateStatusReport\x12\x17\n" +
|
"\x12UpdateStatusReport\x12\x17\n" +
|
||||||
"\atask_id\x18\x01 \x01(\tR\x06taskId\x12'\n" +
|
"\atask_id\x18\x01 \x01(\rR\x06taskId\x12'\n" +
|
||||||
"\x0fcurrent_version\x18\x02 \x01(\tR\x0ecurrentVersion\x129\n" +
|
"\x0fcurrent_version\x18\x02 \x01(\tR\x0ecurrentVersion\x129\n" +
|
||||||
"\x06status\x18\x03 \x01(\x0e2!.device.UpdateStatusReport.StatusR\x06status\x12#\n" +
|
"\x06status\x18\x03 \x01(\x0e2!.device.UpdateStatusReport.StatusR\x06status\x12#\n" +
|
||||||
"\rerror_message\x18\x04 \x01(\tR\ferrorMessage\x12\x1f\n" +
|
"\rerror_message\x18\x04 \x01(\tR\ferrorMessage\x12\x1f\n" +
|
||||||
|
|||||||
@@ -46,27 +46,27 @@ message Pong {
|
|||||||
// PrepareUpdateReq: 平台发送给设备,通知设备准备开始 OTA 升级 (下行)
|
// PrepareUpdateReq: 平台发送给设备,通知设备准备开始 OTA 升级 (下行)
|
||||||
message PrepareUpdateReq {
|
message PrepareUpdateReq {
|
||||||
string version = 1; // 新固件版本号
|
string version = 1; // 新固件版本号
|
||||||
string task_id = 2; // 升级任务唯一ID
|
uint32 task_id = 2; // 升级任务唯一ID
|
||||||
string manifest_md5 = 3; // 清单文件的 MD5 校验和,用于设备初步校验清单文件完整性
|
string manifest_md5 = 3; // 清单文件的 MD5 校验和,用于设备初步校验清单文件完整性
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestFile: 设备向平台请求特定文件 (包括清单文件和固件文件) (上行)
|
// RequestFile: 设备向平台请求特定文件 (包括清单文件和固件文件) (上行)
|
||||||
message RequestFile {
|
message RequestFile {
|
||||||
string task_id = 1; // 升级任务ID
|
uint32 task_id = 1; // 升级任务ID
|
||||||
string filepath = 2; // 请求的文件路径 (例如 "/manifest.json" 或 "/main.py")
|
string filepath = 2; // 请求的文件路径 (例如 "/manifest.json" 或 "/main.py")
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileResponse: 平台响应设备请求,发送单个文件的完整内容 (下行)
|
// FileResponse: 平台响应设备请求,发送单个文件的完整内容 (下行)
|
||||||
// LoRa 传输层会自动处理分片和重组,因此应用层可以直接发送完整的单个文件内容
|
// LoRa 传输层会自动处理分片和重组,因此应用层可以直接发送完整的单个文件内容
|
||||||
message FileResponse {
|
message FileResponse {
|
||||||
string task_id = 1; // 升级任务ID
|
uint32 task_id = 1; // 升级任务ID
|
||||||
string filepath = 2; // 设备上的目标路径 (例如 "/manifest.json" 或 "/main.py")
|
string filepath = 2; // 设备上的目标路径 (例如 "/manifest.json" 或 "/main.py")
|
||||||
bytes content = 3; // 文件的完整内容
|
bytes content = 3; // 文件的完整内容
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateStatusReport: 设备向平台报告升级状态 (上行)
|
// UpdateStatusReport: 设备向平台报告升级状态 (上行)
|
||||||
message UpdateStatusReport {
|
message UpdateStatusReport {
|
||||||
string task_id = 1; // 升级任务ID
|
uint32 task_id = 1; // 升级任务ID
|
||||||
string current_version = 2; // 操作完成后的当前版本
|
string current_version = 2; // 操作完成后的当前版本
|
||||||
enum Status {
|
enum Status {
|
||||||
STATUS_UNSPECIFIED = 0; // 未指定,protobuf3 要求枚举从0开始
|
STATUS_UNSPECIFIED = 0; // 未指定,protobuf3 要求枚举从0开始
|
||||||
|
|||||||
Reference in New Issue
Block a user