
# 1. HttpServer
# 1.1 服务介绍
Http升级服务程序位于uav-ota模块下的子模块uav-ota-http部分。 该服务主要功能是基于雅讯《OTA终端平台交互协议20200610》协议文档,实现除808相关协议之外的协议内容。
# 1.2 任务流程
以下流程顺序仅为模拟器实现过程,真实生产环境有待验证!!!
执行OTA平台构建的升级计划任务,OTA平台通过808网关下发消息ID为8E09任务数据, 同时将任务信息(见1.3.1)写入Redis任务队列;
终端收到8E09任务后,请求HttpServer的鉴权接口login.do;
HttpServer根据login请求参数【vin、sim】,对车辆合法性进行鉴权,并返回鉴权码;
鉴权通过后触发终端调用up_version.do接口,反之结束;
HttpServer将up_version.do请求参数,写入消息队列(Redis or Kafka)供OTA平台使用(见1.3.2);
HttpServer通过读取Redis中Key为http:task:车辆标识的队列值,将任务信息作为up_version.do的响应,返回给终端;
终端调用HttpServer接口task_detail.do并传递任务ID(6返回的信息),获取任务详情信息(子任务ID+子任务优先级)(见1.3.3);
终端调用HttpServer接口task_version_detail.do并传入子任务ID,获取任务版本详情用于校验是否满足升级按本要求(见1.3.4);
终端调用HttpServer接口sub_task_detail.do并传入子任务ID,获取子任务详情(见1.3.5);
终端调用HttpServer接口sub_task_param.do并传入子任务ID,获取子任务升级参数(见1.3.6);
终端调用task_status_report.do接口,传入任务升级过程信息(见1.3.7);
HttpServer将升级过程信息写入消息队列(Redis or Kafka),供平台使用;
# 1.3 缓存结构
# 1.3.1 升级任务信息
OTA平台写入JSON任务结构到Redis,供HttpServer使用。
- 数据类型:List
- 缓存Key:http:task:车辆标识
- 缓存Value:
{
// 任务数
"taskCount": 2,
// 任务ID集合,任务数需和任务集合长度一致
"taskIds": [10001,10002]
}
2
3
4
5
6
# 1.3.2 版本信息上报
终端请求HttpServer接口up_version.do,HttpServer将请求参数中的版本信息写入Redis或Kafka供OTA平台使用。
- 数据类型:List
- 缓存Key:http:version:list
- Kafka主题:http_version
- 缓存Value:
{
// 车辆ID
"vehicleId": 1000115206,
// 版本数量
"versionCount": 1,
"versions": [
{
// 固件类型
"firmwareType": 1,
// 版本号
"version": "1.2.3.9",
// 版本号长度
"versionNoLen": 7,
// 版本类型
"versionType": 1
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
版本信息为集合,可能包含多个零部件版本信息。
# 1.3.3 任务详情
终端请求HttpServer接口task_detail.do,HttpServer根据请求参数中的任务ID,查询Redis获取任务详情。
- 数据类型:String
- 缓存Key:http:task:detail:任务ID
- Kafka主题:http_task_detail
- 缓存Value:
{
// 子任务数
"subTaskNum": 2,
// 子任务集合
"subTasks": [
{
// 任务优先级,0最大
"priority": 0,
// 子任务ID
"subTaskId": 100010001
},
{
"priority": 1,
"subTaskId": 100010002
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
子任务可以有多个,以JSON集合表示。
# 1.3.4 任务版本详情
终端请求HttpServer接口task_version_detail.do,HttpServer根据请求参数中的子任务ID,查询Redis获取子任务版本详情。
- 数据类型:String
- 缓存Key:http:subtask:version:子任务ID
- Kafka主题:http_subtask_version
- 缓存Value:
{
// 发动机程序版本号长度
"engineVersionLen": 7,
// 发动机程序版本号
"engineVersion": "1.0.3.1",
// 支持硬件版本号长度
"hardwareVersionLen": 7,
// 支持硬件程序版本号
"hardwareProgramVersion": "1.0.3.0",
// 支持软件版本号长度
"softwareVersionLen": 7,
// 支持软件程序版本号
"softwareVersion": "1.0.0.1"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
子任务可以有多个,以JSON集合标识。
# 1.3.5 子任务详情
终端请求HttpServer接口sub_task_detail.do,HttpServer根据请求参数中的子任务ID,查询Redis获取任务详情。
- 数据类型:String
- 缓存Key:http:subtask:子任务ID
- Kafka主题:http_subtask
- 缓存Value:
{
// 升级包类型 0:程序包 1:数据包
"packageType": 1,
// 固件类型 1:菱电发动机 2:新风发动机 3:凯龙后处理 4:菱电汽体机 254:终端模块 255:终端单片机 其余待定
"firmwareType": 2,
// 新版本号长度
"newVersionLength": 7,
// 新版本号
"newVersion": "1.0.2",
// 新版本号地址长度
"packageAddrLength": 76,
// 升级包地址
"packageAddr": "http://121.199.35.124:6868/20200915/7ed246b6-849c-4af6-9e66-858151a4324a.bin",
// 升级包md5校验码
"packageMd5": "b58673b14326cf058e915af41972732b",
// 升级包长度
"packageLength": 639856,
// 是否压缩 0否 1是
"compress": 0,
// 升级包原始长度
"packageOriginLength": 639856,
// 升级包原始md5校验码
"packageOriginMd5": "b58673b14326cf058e915af41972732b",
// 是否差分升级
"difference": 0,
// 差分比对版本号长度
"diffCompVersionLength": 7,
// 差分比对版本号
"diffCompVersion": "1.0.1",
// 差分比对版本的md5校验码
"diffCompVersionMd5": "b58673b14326cf058e915af41972732b",
// 差分比对版本的文件长度
"diffCompVersionFileLength": 639856,
// 完整包地址长度
"wholePackageAddrLength": 76,
// 完整包地址
"wholePackageAddr": "http://121.199.35.124:6868/20200915/7ed246b6-849c-4af6-9e66-858151a4324a.bin",
// 完整包md5校验码
"wholePackageMd5": "b58673b14326cf058e915af41972732b",
// 完整包的文件长度
"wholePackageFileLength": 639856,
// 完整包是否压缩 0否 1是
"wholeCompress": 0,
// 完整包原始长度
"wholePackageOriginLength": 639856,
// 完整包原始md5
"wholePackageOriginMd5": "b58673b14326cf058e915af41972732b"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 1.3.6 子任务参数
终端请求HttpServer接口sub_task_param.do,HttpServer根据请求参数中的子任务ID,查询Redis获取子任务参数。
- 数据类型:String
- 缓存Key:http:subtask:param:子任务ID
- Kafka主题:http_subtask_param
- 缓存Value:
{
// 是否静默 0 否 1 强制静默 2 条件静默
"silence": 1,
// 失败是否批量回滚 0 否 1 是
"batchRollback": 0,
// Only WIFI下载 0 否 1 是
"onlyWifi": 0,
// 优先级 从0开始,相同优先级的允许并行,优先级值越小级别越高,0为最优先级。
"priority": 0,
// 条件个数
"conditionNum": 2,
// 条件信息集合,个数对应conditionNum
"conditions": [
{
// 条件类型 1-时间,取值小时部分 2-ACC,0或1 3-速度,单位0.1km/h
// 4-转速, 单位rpm 5-手刹, 0或1 6-电量,%
"conditionType": 2,
// 条件逻辑 0-等于 1-大于 2-小于 3-大于等于 4-小于等于
"conditionLogic": 0,
// 条件值
"conditionValue": 1
},
{
"conditionType": 3,
"conditionLogic": 3,
"conditionValue": 10
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 1.3.7 任务状态
终端请求HttpServer接口task_status_report.do,HttpServer获取请求参数中的状态,写入消息队列供OTA平台使用。
- 数据类型:List
- 缓存Key:http:subtask:status:list
- Kafka主题:http_subtask_status
- 缓存Value:
{
// 进度/错误码 当状态为2和4时表示错误码, 说明见错误码部分
"code": 100,
// 状态 1:下载成功 2:下载失败 3:刷写成功 4:刷写失败 5:下载中 6:刷写中
"status": 1,
// 子任务ID
"subTaskId": 10001,
// 版本信息
"version": "1.0.2.2",
// 版本长度
"versionNumLength": 7,
// 车辆ID
"vehicleId": 1000115206
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.4 Kafka消息消费
待完善。
# 1.5 状态错误码
- 110 下载应答文件长度错误
- 111-下载校验不对
- 112- 存储空间不够
- 148-通知升级请求错误失败
- 149-通知升级请求无应答失败
- 150-擦除程序区错误失败
- 151-擦除程序区无应答失败
- 152-固件数据传输错误失败
- 153-固件数据传输无应答失败
- 154-刷写段校验错误失败
- 155-刷写无段校验通知
- 156-flash driver 传输错误失败
- 157-flash driver 传输无应答失败
- 158-读取版本号失败
- 199-刷写完成,版本号不匹配
- 200-仪表不支持提醒,未完成升级
# 2 模拟测试
# 2.1 808网关信息
- 网关地址:47.111.111.9
- 网关端口:50200
# 2.2 HttpServer信息
- 服务地址 http://47.111.111.9:2000/ota/terminal/
# 2.3 模拟器使用
# 3 任务模拟接口
模拟将任务数据写入Redis缓存,可通过后台UT测试程序或EOLinker接口实现。
# 3.1 8E09
# 3.1.1 API Path
/ota/terminal/simulation/8E09
# 3.1.2 请求协议
HTTP
# 2.4.1.3 请求方法
POST
# 3.1.4 请求参数
参数名 | 说明 | 必填 | 类型 | 示例 |
---|---|---|---|---|
dataNo | 车辆数据号(ID) | 是 | [long] | 1000000001 |
message | 任务消息体 | 是 | [string] | 见下方JSON |
{
"taskId": "0",
"createTime": "1590485541",
"messageId": "36361",
"expiration": "1590486141",
"priority": "1",
"messageBody": ""
}
2
3
4
5
6
7
8
# 3.2 版本上报
# 3.2.1 API Path
/ota/terminal/simulation/task
# 3.2.2 请求协议
HTTP
# 3.2.3 请求方法
POST
# 3.2.4 请求参数
参数名 | 说明 | 必填 | 类型 | 示例 |
---|---|---|---|---|
dataNo | 车辆ID | 是 | [long] | 1000000001 |
message | 消息体 | 是 | [string] | 见下方JSON |
{
"taskCount": 2,
"taskIds": [10001,10002]
}
2
3
4
# 3.3 任务详情
# 3.3.1 API Path
/ota/terminal/simulation/task/detail
# 3.3.2 请求协议
HTTP
# 3.3.3 请求方法
POST
# 3.3.4 请求参数
参数名 | 说明 | 必填 | 类型 | 示例 |
---|---|---|---|---|
taskId | 任务ID | 是 | [long] | 10001 |
message | 消息体 | 是 | [string] | 见下方JSON |
{
"subTaskNum": 2,
"subTasks": [
{
"priority": 0,
"subTaskId": 100010001
},
{
"priority": 1,
"subTaskId": 100010002
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
# 3.4 子任务详情
# 3.4.1 API Path
/ota/terminal/simulation/task/sub/detail
# 3.4.2 请求协议
HTTP
# 3.4.3 请求方法
POST
# 3.4.4 请求参数
参数名 | 说明 | 必填 | 类型 | 示例 |
---|---|---|---|---|
subTaskId | 子任务ID | 是 | [long] | 100010001 |
message | 消息体 | 是 | [string] | 见下方JSON |
{
"packageType": 1,
"firmwareType": 2,
"newVersionLength": 7,
"newVersion": "1.0.2",
"packageAddrLength": 127,
"packageAddr": "http://121.199.35.124:6868/%E6%96%B0%E9%A3%8E%E5%8F%91%E5%8A%A8%E6%9C%BA/Q28-0198-0925/a4f82ff2-4817-481b-b4df-49f1158f16cd.s19",
"packageMd5": "fc1709d0a95a6be30bc5926fdb7f22f4",
"packageLength": 3965262,
"compress": 0,
"packageOriginLength": 3965262,
"packageOriginMd5": "fc1709d0a95a6be30bc5926fdb7f22f4",
"difference": 0,
"diffCompVersionLength": 7,
"diffCompVersion": "1.0.1",
"diffCompVersionMd5": "fc1709d0a95a6be30bc5926fdb7f22f4",
"diffCompVersionFileLength": 3965262,
"wholePackageAddrLength": 127,
"wholePackageAddr": "http://121.199.35.124:6868/%E6%96%B0%E9%A3%8E%E5%8F%91%E5%8A%A8%E6%9C%BA/Q28-0198-0925/a4f82ff2-4817-481b-b4df-49f1158f16cd.s19",
"wholePackageMd5": "fc1709d0a95a6be30bc5926fdb7f22f4",
"wholePackageFileLength": 3965262,
"wholeCompress": 0,
"wholePackageOriginLength": 3965262,
"wholePackageOriginMd5": "fc1709d0a95a6be30bc5926fdb7f22f4"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 3.5 子任务参数
# 3.5.1 API Path
/ota/terminal/simulation/task/sub/param
# 3.5.2 请求协议
HTTP
# 3.5.3 请求方法
POST
# 3.5.4 请求参数
参数名 | 说明 | 必填 | 类型 | 示例 |
---|---|---|---|---|
subTaskId | 子任务ID | 是 | [long] | 100010001 |
message | 消息体 | 是 | [string] | 见下方JSON |
{
"silence": 1,
"batchRollback": 0,
"onlyWifi": 0,
"priority": 0,
"conditionNum": 2,
"conditions": [
{
"conditionType": 2,
"conditionLogic": 0,
"conditionValue": 1
},
{
"conditionType": 3,
"conditionLogic": 3,
"conditionValue": 10
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 3.6 任务版本详情
# 3.6.1 API Path
/ota/terminal/simulation/task/sub/version
# 3.6.2 请求协议
HTTP
# 3.6.3 请求方法
POST
# 3.6.4 请求参数
参数名 | 说明 | 必填 | 类型 | 示例 |
---|---|---|---|---|
subTaskId | 子任务ID | 是 | [long] | 100010001 |
message | 消息体 | 是 | [string] | 见下方JSON |
{
"engineVersionLen": 7,
"engineVersion": "1.0.3.1",
"hardwareVersionLen": 7,
"hardwareProgramVersion": "1.0.3.0",
"softwareVersionLen": 7,
"softwareVersion": "1.0.0.1"
}
2
3
4
5
6
7
8
# 4. S19文件
# 4.1 基本介绍
S-record每行最大是78个字节,156个字符。 S格式文件中的每一行称为一个S记录,每个S记录由记录类型、记录长度、存储地址、 代码 / 数据 、校验和5个部分组成。
示例数据:S11300A038000000900100003800FFFF90010004B8
记录类型 | 记录长度 | 存储地址 | 代码/数据 | 校验和 |
---|---|---|---|---|
S1 | 13 | 00A0 | 38000000900100003800FFFF90010004 | B8 |
# 4.2 组成部分
# 4.2.1 记录类型
2个字符 (即1个字节),用来描述记录的类型。记录供定义了8种类型:
S0 S格式文件的第一个记录, 表示文件名(含路径) ,存储地址部分没有使用,以0000置位 。 此记录表示记录的开始,无需下载到 MCU 。
S1 地址长度为2字节 (4个字符)的记录。记录类型是“ S1” (0x5331) 。地址场由2个字节地址来说明 。数据场由可载入的数据组成。
S2 地址长度为3字节的记录。记录类型是“ S2” (0x5332) 。地址场由3个字节地址来说明。数据场由可载入的数据组成。
S3 地址长度为4字节的记录。记录类型是“ S3” (0x5333) 。地址场由4个字节地址来说明。数据场由可载入的数据组成。
S5 标记本文件的S1、S2、S3记录的个数 (此记录不是一个 S文件所必须的)。记录类型是 “S5 ” (0x5335) 。 地址场由2字节的值说明 ,包含了先前传输的S1、S2、S3记录的计数。没有数据场。
S7 地址长度为4字节, 表示程序的开始执行地址 ,代码/数据部分没有被使用, 此行表示程序的结束 ,无需下载到MCU。记录类型是“S7”(0x5337) 。 地址场由4字节的地址说明, 包含了开始执行地址。没有数据场 。此行表示程序的结束,不需烧入memory。
S8 地址长度为3字节,表示程序的开始执行地址,代码/数据部分没有被使用,此行表示程序的结束 ,无需下载到MCU。记录类型是“S8”(0x5338) 。 地址场由3字节的地址说明,包含了开始执行地址。没有数据场。此行表示程序的结束,不需烧入memory。
S9 地址长度为2字节,表示程序的开始执行地址,代码/数据部分没有被使用,此行表示程序的结束,无需下载到MCU。记录类型是“S9”(0x5339)。 地址场由2字节的地址说明,包含了开始执行地址。没有数据场。此行表示程序的结束,不需烧入memory。
只有 S1、S2、S3、S5 需要写入到 FLASH 中。
# 4.2.2 记录长度
2个字符(即1个字节),显示在记录中剩余的字节数。即
记录长度 = 存储地址字节数 + 代码/数据字节数 + 校验和字节数
示例数据:S11300A038000000900100003800FFFF90010004B8
- 记录长度 = 0x13 = 19
- 存储地址字节数 = 2 (类型为S1)
- 代码/数据字节数 = 32(字符数) / 2 = 16
- 校验和字节数 = 1
以上可得:19 = 2 + 16 + 1
# 4.2.3 存储地址
2或3或4个字节(由记录类型决定),用来表示代码/数据应该装载的起始地址。这部分的长度取决于载入地址
的字节数。
2个字节的地址占用4个字符,3个字节的地址占用6个字符, 4个字节的地址占用8个字符。
- S1 2字节
- S2 3字节
- S3 4字节
- S7 4字节
- S8 3字节
- S9 2字节
# 4.2.4 代码/数据
0-64字符(即 0-32 字节), 表示需要下载到MCU中的数据。
# 4.2.5 校验和
2个字符(即1字节),校验数据,计算方法:
校验和 = 0Xff – (记录长度 + 存储地址 + 代码 / 数据 )
注意,校验和不是字符的校验和,而是实际二进制数的校验和。这些字符当被配对并换算成16进制数据的时候形成了一个最低有效字符节,
该字符节用来表达作为补充数据,地址和数据库的字符对所代表的(字节的)补码的byte总和。即计数值、地址场和数据场的若干字符以两个字符为一对,
将它们相加求和,和的溢出部分不计,只保留最低两位字符NN,checksum = 0xFF - 0xNN
。
# 4.3 示例数据
记录类型 / 记录长度 / 存储地址 / (代码/数据) / 校验和
S3250013C7801CC6646A7F66DAAE7FFC073473B0E0027070E0023BBD76123863761005CD05C3AB
# 5. Intel Hex文件
# 5.1 基本介绍
Intel HEX文件是记录文本行的ASCII文本文件,在Intel HEX文件中,每一行是一个HEX记录,由十六进制数组成的机器码或者数据常量。
Intel HEX文件经常被用于将程序或数据传输存储到ROM、EPROM,大多数编程器和模拟器使用Intel HEX文件。
每一行由冒号开始,由回车换行符结束。
# 5.2 组成部分
:LLTTDDCC
- :(冒号) 是每一条Intel HEX记录的开始
- LL 是这条记录的长度域,他表示数据(dd)的字节数目
- TT 这个域表示这条HEX记录的类型,他有可能是下面这几种类型
- 00 ----数据记录,表示该记录所包含的数据为实际要烧写到存储器中的数据
- 01 ----文件结束记录,表示该记录为本文件的最后一个记录
- 02 ----扩展段地址记录,表示该记录所包含的数据为段地址
- 04 ----扩展线性地址记录,表示该记录所包含的数据为线性地址
- DD是数据域,表示一个字节的数据,一个记录可能有多个数据字节,字节数目可以 查看LL域的说明
- CC 是效验和域,它表示这个记录的校验和。校验和的计算是通过将记录当中所有十六进制编码数字对的值相加,以256为模进行以下补足。 校验和需要保证整行中所有十六进制字节相加的和为0
# 5.3 示例数据
记录标志 / 记录长度 / 装载偏移 / 记录类型 / 数据校验和
:20002000DEADBEEF0000000000000000000000000000000000000000000000000000000088
# 6 转换规则
S19或Hex文件按一下结构组装。
段个数N+[段执行地址+段文件偏移+段长度]N+段数据N+校验和。
字段 | 数据类型 | 描述及要求 |
---|---|---|
段个数 | DWORD | |
段执行地址 | DWORD | 烧写地址 |
段文件偏移 | DWORD | 段数据在本文件的偏移地址 |
段长度L | DWORD | 段数据长度 |
段数据内容 | BYTE[L] | |
校验和 | DWORD | 累加和校验 |
- 段个数 固定值
- 段执行地址 指的是该段要写到ECU的地址,就是S19每行的那个地址
- 段文件偏移 数据段在文件里的起始位置