OTA 空中协议

# 1. HttpServer

# 1.1 服务介绍

Http升级服务程序位于uav-ota模块下的子模块uav-ota-http部分。 该服务主要功能是基于雅讯《OTA终端平台交互协议20200610》协议文档,实现除808相关协议之外的协议内容。

# 1.2 任务流程

以下流程顺序仅为模拟器实现过程,真实生产环境有待验证!!!

  1. 执行OTA平台构建的升级计划任务,OTA平台通过808网关下发消息ID为8E09任务数据, 同时将任务信息(见1.3.1)写入Redis任务队列;

  2. 终端收到8E09任务后,请求HttpServer的鉴权接口login.do

  3. HttpServer根据login请求参数【vin、sim】,对车辆合法性进行鉴权,并返回鉴权码;

  4. 鉴权通过后触发终端调用up_version.do接口,反之结束;

  5. HttpServer将up_version.do请求参数,写入消息队列(Redis or Kafka)供OTA平台使用(见1.3.2);

  6. HttpServer通过读取Redis中Key为http:task:车辆标识的队列值,将任务信息作为up_version.do的响应,返回给终端;

  7. 终端调用HttpServer接口task_detail.do并传递任务ID(6返回的信息),获取任务详情信息(子任务ID+子任务优先级)(见1.3.3);

  8. 终端调用HttpServer接口task_version_detail.do并传入子任务ID,获取任务版本详情用于校验是否满足升级按本要求(见1.3.4);

  9. 终端调用HttpServer接口sub_task_detail.do并传入子任务ID,获取子任务详情(见1.3.5);

  10. 终端调用HttpServer接口sub_task_param.do并传入子任务ID,获取子任务升级参数(见1.3.6);

  11. 终端调用task_status_report.do接口,传入任务升级过程信息(见1.3.7);

  12. HttpServer将升级过程信息写入消息队列(Redis or Kafka),供平台使用;

# 1.3 缓存结构

# 1.3.1 升级任务信息

OTA平台写入JSON任务结构到Redis,供HttpServer使用。

  • 数据类型:List
  • 缓存Key:http:task:车辆标识
  • 缓存Value:
{
  // 任务数
  "taskCount": 2,
  // 任务ID集合,任务数需和任务集合长度一致
  "taskIds": [10001,10002]
}
1
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
    }
  ]
}
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
    }
  ]
}
1
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"
}
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"
}
1
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
    }
  ]
}
1
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
}
1
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": ""
}
1
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]
}
1
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
    }
  ]
}
1
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"
}
1
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
    }
  ]
}
1
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"
}
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每行的那个地址
  • 段文件偏移 数据段在文件里的起始位置

# 7. 其他业务