本期讲一讲IOL使用的数据类型,方便后面对IODD等内容进行讲解。IOL的数据类型总的分为两大类,简单数据类型和复杂数据类型。简单数据类型很好理解,即大家最熟悉的布尔、整形、浮点等数据类型;复杂数据类型则是将简单数据类型进行组合得到的数据类型。也因此,复杂数据类型理解和使用起来会比简单数据类型要难上一些。然而,IOL在使用过程中,复杂数据类型是使用最多的。
布尔数据类型,只表示两种逻辑状态,即“True”和“False”,不用做过多解释,这种数据类型大家都用过。这里就讲一下使用上的注意事项。
IOL文档中定义BooleanT数据类型的数据长度可以使用1bit或者8bit(即一个字节),其中1bit中的1和8bit中的0xFF表示逻辑意义上的True,1bit的0和8bit的0x00表示逻辑意义上的False。虽然定义了可以使用8bit,然而在实际使用过程中,更多是跟若干其他数据组合成RecordT数据类型,为了尽可能最大化使用有限的数据空间,通常情况下定义1bit来使用。
无符号整数,最大支持64bit,虽然协议的IODD文件可以定义任意长度,但考虑到寄存器存储的规则为8bit、16bit、32bit、64bit,建议在C代码编写过程中使用的字节数量为1、2、4、8,右对齐,高位若未使用到用0占位。
有符号整数,最高一位表示正负号,为0时表示正数,为1时表示负数,不做过多解释。其注意事项与UIntegerT一致。
浮点数,最高位表示正负,0表示正数,1表示负数,整数位最大值0x7F。
字符串类型,具有最大232字节的可变字节长度,每一个字节对应一个ASCLL码数值,在解析时转换成对应的字符。
字符串的长度可以通过‘fixedLengthRestriction’属性来定义,数值对应字节个数。接收方会根据IODD中设置的这个属性获取交互的字节数量,因为ASCLL码中0x00对应为NULL,因此也可以不定义长度,接收方通过监听第一个0x00字节来判断发送方传输结束。
The receiver can deduce the original length from the length of the ISDU or by searching the first NULL (0x00) character.
固定长度字符串类型,其使用方法与StringT相同,但是值得注意的是,该类型为固定232个字节。
An OctetStringT is representing an ordered sequence of octets with a fixed length (maximum of 232 octets).
“时间”数据类型,由8个字节组成,每四字节分为一个部分,均为无符号整数。前四个字节的32bit提供了自1900-01-01 0.00,00(UTC)或自2036-02-07 6.28,16(UTC)的时间值小于0x9DFF4400的网络时间,它表示1984-01-01 0:00,00(UTC)。后四个字节以2的32次方分之一秒为单位表示秒信息。这个时间信息需要由设备供应商来维护。
然而在实际的观察中,几乎没有设备供应商用到这个数据类型携带信息,个人也没有做过多研究,或许主站会用到这个数据类型,完成时间一致性?没了解过,不懂。
表示与网络时间之间的时间差,以2的32次方分之一秒为单位表示秒信息,是一个64bit整数类型的二进制补码。
与上一个数据类型一样,市面上几乎没有设备供应商用到这个数据类型,我也没用过,不懂。
上面讲完了简单数据类型,下面来讲一下复杂数据类型。在官方文档中优先介绍了ArrayT即数组数据类型,我这里就相反,先介绍RecordT,我称之为“记录集”。为什么我要优先介绍“记录集”呢?因为“数组”可以看作是“记录集”的一个子数据类型,两者用法很相似,可以说“数组”是一个特殊的“记录集”。
记录集内可以使用任意简单数据类型,并且每一个数据的长度都可以在定义的范围内使用任意长度。
先放出官方的规则。
1、IODD内的子索引应按从1到n的升序列出,以描述八位字节序列。允许在子索引列表中有空白。
2、位偏移应该总是在这个八位序列中显示(在IODD中可能没有严格的顺序)
3、位偏移量从序列中的最后一个八位开始:这个八位从最低有效位开始则偏移量为0,最高有效位开始则偏移量为7。
4、以下数据类型必须始终在字节边界上对齐:Float32T、StringT、OctetStringT、TimeT和TimeSpanT
5、长度为大于58位的UIntegerT和IntegerT应始终在八位边界的一侧对齐。
6、强烈建议长度为大于8位的ulintegert和IntegerT始终对齐在八进制边界的一侧。
7、强烈建议长度小于8位的ulintegert和IntegerT不要跨越八位边界。
8、一个位成员不得同时用于多个记录项。
看不懂,直接上例子,简单粗暴。
例1,四个bool类型的参数。
这个是最简单的情况,四个bool数据右对齐排列,这里subindex索引是从右到左依次递增即4-3-2-1,也可以定义从左到右递增即1-2-3-4。这里引入了一个bitoffset位偏移,表达的含义就是,该数据相较于最右侧最低位间隔了多少位,用来确定解析的位置。第一个数据的位偏移若右对齐,其位偏移必须是0,其后按照上一个数据的位长度进行位偏移的计算。
例2,一个16位无符号整数,一个8位无符号整数
这里可以看到subindex = 1成员位偏移变成了8,就是因为subindex = 2的成员位长度是8。当然也可以换一下,定义U16的成员为subindex2,U8成员为subindex1,这样U16成员的位偏移就变成了0,U8成员的位偏移就变成了16。
例3,一个14位无符号整数,两个bool
这里出现了一个特殊的成员,其位长度不满足两个完整字节,这是所允许的,比如我的某一个数值最大到4095,12位,余下四位装填几个bool类型的数据,完全没有问题。但是需要注意的是,先下方这种,虽然不是填充郑子杰,但是跨越了多字节,仍然是不建议的。更优解是将红色数据多1位,将绿色数据拿到左侧第一个字节低2位,让红色数据与黄色数据组成完整字节。
例4,两个16位无符号整数,存在完整一字节空缺
这个还是比较好理解的,其实空出来的位置,假设有一个U8的数据,定义为subindex2,就跟上面的例子完全一样。这里不仅说明,记录集内是可以存在空缺的,更是说了一个小技巧,比如某设备有三个运行模式,模式一需要上报运行状态、测量结果、设备温度、速度、加速度五个信息,模式二需要上报运行状态、测量结果、速度三个信息,模式三需要上报运行状态、速度、加速度三个信息。就会发现,三个模式需要的信息是有重合的,这些数值被不同需要。那么就可以装五个数据写入到一个记录集中,不同模式抽取不同的subindex作为自己的显示,就形成了本利中的空缺。看到这里有很多读者就会想到一个——结构体。其实记录集就是一个结构体,不同的数据汇总,又有若干数据被拿走输出,而多个结构体实例只需要定义一个结构体就可以,不需要多次重复的定义。
例5,两个16位无符号整数,一个4位无符号整数,一个bool,存在少于1字节空缺
本例与例4有相同点,都是有空缺,需要注意的是,本例中的空缺,红色的数据不建议补齐,因为红色数据将变成即不满足整字节的数据又是跨字节数据。这几个位的空缺就让他空着就行,或者有更多的bool类型数据,填补空缺即可。
总结规律,其实在编写记录集数据库类型的时候,只需要记住两点:一,子索引按顺序写,不要出现重复定义,无论是bitoffset位偏移从低到高写还是从高到低写都行;二,尽可能满足整字节,不要跨字节,可以空缺被0占位,也可以将一位一位的数据填充进空位。
跟记录集用法相类似,定义索引和子索引。
区别:定义了数据类型和位数量之后,所有成员的数据类型和位数量均一致。如,定义了数据类型为U3,包含了5个成员,那么这五个成员以右对齐的方式从右到左排列,每一个成员的数据类型都是U3。也因此,数组只需要定义三个属性:数据类型(例中UInteger)、数据位长度(例中3bit)、数据数量(例中5个成员)。
以上是本节的全部内容,如果各位读者仍然存在疑惑可以私信发送问题,笔者会一一解答并积累一期文章的内容更新出来。
笔者内容也许会存在错误,欢迎志同道合的开发者一起讨论。我们共同进步!喜欢我的文章请关注我的微信公众号,我会不定期更新更多干货知识!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。