数据类型
Scalable service-Oriented MiddlewarE over IP
SOME/IP数据类型是该协议中的基础概念,了解数据类型可以帮助我们理解SOME/IP的工作原理和实现方式。在做通信设计时,需要根据具体的应用需求和复杂性,选择合适的数据类型。简单的说,数据类型就是SOME/IP报文的Payload中要传输的数据是什么样的结构,以及如何排列。
SOME/IP支持的数据类型可以分为基础数据类型和复杂数据类型两种。基础数据类型涵盖了不同大小和表示方式的整数、布尔值和浮点数;而复杂数据类型则包括String字符串、Array数组、Enumeration枚举、Union联合体(或者叫Variant可变体)和Struct结构体。具体见下图:
String
字符串,一般用于表示文本信息,如报警提示、故障描述、歌词信息等均可定义为字符串,另外有些场景如果需要直接传输字节流,也可以通过字符串的形式传输。SOME/IP协议支持UTF-8、UTF-16BE和UTF-16LE三种字符串编码,无论哪种都需要在前面加一个Byte Order Mark(BOM)字段,用于标识字节顺序,其值为0xEFBBBF(UTF-8)、0xFEFF(UTF-16BE)和0xFFFE(UTF-16LE)。当字符串结束时,均以“\0”作为结束符,因为编码方式不同,所以UTF-8以0x00结束,UTF-16以0x0000结束。
字符串分为固定长度(Fixed length)和动态长度(Dynamic length)两种。 对于固定长度,需要预先定义好其长度,通常是在SOME/IP的静态接口描述文档(通信矩阵)中定义。对于动态长度,因为长度可变,所以无需定义长度,但需要定义其最大长度,另外还需要在BOM字段前添加一个长度字段(Length filed),用于标识其长度,单位为字节,这个Length field本身的长度可以配置为8、16或者32 bits。在一些协议栈中,固定长度的字符串前面也可以添加长度字段,这个可根据实际情况进行配置。
UTF全称为Unicode Transformation Format,意为把Unicode字符转换为某种格式。Unicode 是统一码,也叫万国码、单一码,是容纳世界所有文字符号的国际标准编码,简单地说世界上所有的文字和符号都拥有一个自己的Unicode,无论是中文、英文还是各种数字和符号。
【举个例子】“汽车”通过UTF-8如何编码?
如下图所示,UTF-8会将Unicode按照一定的规则进行编码,在UTF-8中,每个字节前面有若干个固定位,其中黑色的1110、10等,剩下彩色的部分就是Unicode(本文主要介绍SOME/IP,编码方式不做赘述)。
如果使用SOME/IP报文传输UTF-8格式的固定长度的字符串“汽车”,实际报文为0xEF BB BF E6 B1 BD E8 BD A6 00。
如果是配置了长度字段,通常是动态长度的字符串,在传输“汽车”时,需要在BOM前加一个Length field,这里我们使用32 bits长的Length field,该字段的值除了字符串数据长度外,还应包含BOM和结束符的长度。
Array
数组,用于表示多个相同类型的数据元素,这里的元素可以是基础数据类型也可以是复杂数据类型(由多个不同数据类型组成,比如数组本身就是复杂数据类型,所以数组里的元素也可以是数组,即多维数组Multidimensional)。举个例子,如果需要传输所有轮胎的胎压信息,就可以定义一个数组,每个元素是一个轮胎的胎压信息;再比如要传输障碍物信息,也可以定义为数组,车辆通常有能力识别多个障碍物,那数组的每个元素就是一组障碍物的信息,可以包含每个障碍物距离本车的距离、角度以及障碍物类型等。
在SOME/IP中,数组分为静态(Fixed length)和动态(Dynamic length)两种,静态数组长度固定,动态数组长度会根据实际情况变化,和String一样,对于静态和动态数组,需要事先定义好数组的长度或最大长度。在上面的例子中,轮胎信息可以定义为静态数组,因为轮胎数量是固定的,每次都会传输这几个轮胎的信息;而障碍物信息适合定义为动态数组,因为每次识别到的障碍物数量有可能是不同的。
SOME/IP中规定,数组前面可以加一个Length field,长度可以配置为8、16或者32 bits,静态数组的该字段是可选的(Optional),但动态数组是必须添加的,表示数组的总长度,单位为字节,不包括Length field自身的长度,用总长度除以每个元素的长度即可得到该数组一共有多少个元素。下面为几种数组的示意图。
One-dimensional static array
Multidimensional static array
One-dimensional dynamic array
Multidimensional dynamic array
Enumeration
枚举,可以用于表示一组具有明确含义的整数值,每个值都有特定的含义,比较合适在几个选项中进行选择,比如档位信息、障碍物类型、氛围灯颜色等。枚举实际使用时是有排他性的,同一时间只会出现枚举中的一个值。SOME/IP中要求使用无符号整数unsigned integer来定义枚举,一般枚举的数量不会很大,定义为uint8即可(最大256个值)。挡位信息定义为枚举,如果该枚举的值为3,则表示D挡。
Union / Variant
联合体,也叫可变体,允许在相同位置存储不同的数据类型,可以包含多个成员,不过使用时只能选择使用其中的一种,当为其中一个成员赋值时,其他成员的值会被覆盖。通过联合体,程序可以通过调用一个参数,实现使用多种参数类型,可以用于定义类型可变的数据,其优势是可以节省内存空间并提高程序的效率。SOME/IP种定义,联合体由Length field、Type selector和数据三部分组成。
Length field用于表示数据的长度,包含填充的字段,不包含Length field本身和Type selector,单位是字节。Length field的长度可以配置为32、16、8或者0 bits,0表示不添加Length filed(即Length field为可选项),这时联合体里的所有数据类型都拥有相同的长度。
Type selector用于选择联合体中具体使用哪种数据类型,每种类型都需要提前定义好对应的Type selector值,0是预留值,表示NULL,为空的联合体。Type selector的长度同样为可配置的,可以选择32、16或者8 bits,注意Type selector是必须携带的,不是可选项,所以长度不可为0 bit。
【举个例子】联合体的示例
参数包含uint8和float32两个类型,Length Field和Type Filed的长度均为32 bits,Type 1是uint8,Type 2是float32。实际做网络设计时,一般不使用联合体,如果使用需要谨慎,它的内存布局和对齐方式可能会因编译器和操作系统的不同而产生问题。
Struct
结构体,由一系列具有相同类型或不同类型的数据构成的数据集合,这些数据类型可以是基础数据类型、数组、字符或者另一个结构体。当多个数据相互关联的时候,或者需要把多个数据封装在一起的时候,比如Event或者FF类型,只可以有一个参数,如果希望这个参数包含更多的信息,可以将其定义为结构体。在SOME/IP中,结构体前面可以添加Length filed,该字段为可选的,长度可以设置为8、16或者32 bits。下图为一个结构体的示意图,包含一个uint8,一个枚举(Enum)和一个float32。结构体中包含的其他数据类型的格式和单独使用时的格式一致。
在实际项目中,除了需要根据不同的场景选择合适的数据类型,还需规定是否使用不同数据类型中的Length field,以及该字段的长度。
好了,今天就介绍这么多。希望帮大家对SOME/IP的数据类型有个初步的了解,我们下篇再见。
后续还有详细的具体项目实践吗