OTA更新系统(详细设计)
OTA
包的content.json
| 键值 | 类型 | 备注 |
|---|---|---|
| package | 字符串 | 包名称 |
| description | 字符串 | 描述 |
| updateInfo | 字符串 | 更新描述 |
| version | 字符串 | 版本号 |
| branch | 字符串 | 分支 |
| local | 字符串 | 本地包位置 |
| remote | 字符串 | OTA服务器 |
| sha256 | 字符串 | 升级包的SHA256校验码 |
| AfterUpdate | 字符串 | 更新后指令 |
| dependencies | JSON | 依赖 |
一、生产运行侧
重启设备
daemon程序
依赖:flask、requests
JSON配置文件
由于单个设备的包较少,所以维护一个json文件
daemon整体配置文件(device.json):
| 键值 | 类型 | 备注 |
|---|---|---|
| id | INT | 设备唯一id |
| device | 字符串 | 设备名称 |
| registry | 字符串 | 设备注册服务地址 |
| description | 字符串 | 描述 |
| flask | JSON | flask配置项(见下) |
| package | 列表 | 列表中每一项是一个json,为每个包的content.json |
flask配置项:
| 键值 | 类型 | 备注 |
|---|---|---|
| host | 字符串 | 设备启动IP |
| port | 整数 | 端口号 |
| debug | 布尔 | 是否开启debug模式 |
例如:
1 | { |
信息API接口
| 地址 | 参数 | 返回值 |
|---|---|---|
/getInfo |
无 | -“status” -“content”:该设备的 device.json |
操作API接口
| 地址 | 内容类型 | 参数 | 返回值 |
|---|---|---|---|
/startUpdate |
Content-Type: multipart/form-data |
content : content.json(包的content.json) |
status |
二、OTA服务器
依赖:flask、pymysql
配置文件
数据库
ota表:
| 字段 | 类型 | 备注 |
|---|---|---|
| id | INT | |
| name | VARCHAR | 包名 |
| version | VARCHAR | 版本号 |
| branch | VARCHAR | 分支 |
| content | JSON | 包的content.json |
文件管理
文件获取地址:/otafiles/<package>/<branch>/<package>-<branch>-<version>.zip
例如,我需要helloworld包的major分支的0.1.0版本的包
那么访问http://IP:PORT/otafiles/helloworld/major/helloworld-major-0.1.0.zip即可下载文件
API接口
status正常返回200,404代表无法找到,400代表参数错误或服务器错误
| 地址 | 参数 | 返回值 |
|---|---|---|
/latestVersion |
package=包名branch=分支 |
-“status” -“content.json”:该包的 content.json |
/getVersion |
package= 包名(必须)branch=分支version=版本号 |
-“status” -“list”:一个列表,存储所有的查询结果 |
上传接口
用PUT的方式上传status正常返回200
为了以防上传丢包导致问题,服务器会重新进行包校验,如果与content.json不符,服务器不会接受这个文件
如果在上传时发现文件已存在或者数据库中已经有该版本的记录时,不会进行修改,
此时在url的参数里加上’overwrite=1’可以强制进行写入
-
地址 内容类型 参数 返回值 /uploadContent-Type: multipart/form-datacontent : content.json
file : 文件status
三、开发侧
依赖:requests
使用:python pack.py
主要流程:
- 输入服务器的URL,并检查其可用性。
- 输入包名和分支名,获取服务器上的最新版本信息。
- 输入要打包的版本号,检查其是否已存在于服务器上。如果已存在,可以选择覆盖或退出。
- 输入要打包的文件夹路径,如果文件夹存在,则开始打包。
- 打包成功后,生成一个包含最新版本信息的
content.json文件,开发者可以在上传前修改这个content.json文件。 - 最后,将打包的zip文件和
content.json文件上传到服务器
四、设备注册服务
数据库
devices表:
| 字段 | 类型 | 备注 |
|---|---|---|
| id | INT | |
| device | VARCHAR | 设备名 |
| address | VARCHAR | 设备地址 |
| content | JSON | 设备内包情况 |
| status | JSON | 设备状态 |
| lastupdate | TIMESTAMP | 最后更新时间 |
设备侧API接口
接受从生产运行侧的注册和注销请求
每30s接受一次心跳包,超过120s未接收到心跳包即注销设备
| 地址 | 内容类型 | 参数 | 返回值 |
|---|---|---|---|
/register |
Content-Type: multipart/form-data |
content : device.jsonaddress : 设备管理地址 |
status |
/logout |
Content-Type: multipart/form-data |
id : 设备id | status |
/heartbeat |
Content-Type: multipart/form-data |
id : 设备id | status |
接受从设备上的更新状态
| 地址 | 内容类型 | 参数 | 返回值 |
|---|---|---|---|
/updateInfo |
Content-Type: multipart/form-data |
content : update.json |
status |
update.json
若是正常过程升级:
1 | { |
若是出现错误:
1 | { |
信息API接口
返回场景内的所有设备与包信息
| 地址 | 参数 | 返回值 | |
|---|---|---|---|
/getAllInfo |
无 | -“status” -“devices”:所有设备的包的情况例子如下 |
1 | { |
| 地址 | 参数 | 返回值 | |
|---|---|---|---|
/getDeviceInfo |
id=设备id | -“status” -“id”:设备id -“packages”:设备上所有的包 |
1 | { |
| 地址 | 参数 | 返回值 | |
|---|---|---|---|
/getStatus |
无 | -“status” -“update”:升级状态 |
1 | { |
升级状态分为:
- BeforeUpdate:升级前准备
- Downloading:下载包
- Updating:替换中
- AfterUpdate:升级后准备
- Restore:重建现场
| 地址 | 参数 | 返回值 | |
|---|---|---|---|
/getUpdatelist |
无 | -“status” -“list”:注册服务中的更新队列 |
获取更新队列
1 | { |
获取当前升级任务的日志
| 地址 | 参数 | 返回值 | |
|---|---|---|---|
/getUpdatelog |
无 | 一个字符串,自上次All Update Complete之后的所有日志内容 |
操作API接口
| 地址 | 内容类型 | 参数 | 返回值 |
|---|---|---|---|
/update |
Content-Type: multipart/form-data |
content : update.json |
status |
升级场景内的设备与包,content的内容例子如下
1 | { |
若当前存在非空的升级队列status返回400
| 地址 | 内容类型 | 参数 | 返回值 |
|---|---|---|---|
/delFromlist |
Content-Type: multipart/form-data |
content : delete.json |
status |
删除更新队列中的某些更新内容,delete.json的内容例子如下
1 | { |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 四叶草の博客!







