首先定义基础Modbus类的数据为BaseData。var baseData= Array<Byte>()。baseData暂时不在意链路层CRC与ModbusTCP的头包,只考虑Modbus应用部分。
出现第一个考虑:链路层需要最终给数组装配外壳。baseData需要由链路层决定最终的数组长度与内容。因此,baseData设计为链路管理。并采用ILinkLayer接口类型的私有变量linkLayer作为链路层,后续链路层中细节设计。
var linkLayer : ILinkLayer
Modbus由基本信息:Command和ID,其中ID占baseData的第0与第1个byte,特别适合用属性实现:
//Modbus报文帧的地址ID
public mut prop Id: UInt8 {
get() {
linkLayer.BaseData.getUInt8(0)
}
set(value) {
linkLayer.BaseData.setUInt8(0,value)
}
}
//Modbus报文帧的命令字
public mut prop Command: UInt8 {
get() {
linkLayer.BaseData.getUInt8(1)
}
set(value) {
linkLayer.BaseData.setUInt8(1,value)
}
}
此处看到仓颉的get、set方法与C#有很大的相似,但写的方式相比于C#更严格(死板)一些。缺失:get和set无法分别设置方位级别。
需要set时,属性需要定义mut。这里的关键字有些rust的影子。
GetUInt8与SetUInt8为自定义的数组扩展方法,作用为:1:判断数组是否满足长度要求。2:存取一个指定类型数据到数组的指定位置。
继续设计当前类。
后续的子类不再管数组的前两个字节。因此截取好后续的内容给各子类使用。我们发现,数组的切片也是引用方式,对切片的修改会影响原数组的元素。
//取出内容部分。仅在继承子类中可用
protected prop Content : Array<UInt8>{
get(){
@LenCheckThrow(linkLayer.BaseData.size <= 3)
linkLayer.BaseData[2..]
}
}
@LenCheckThrow(linkLayer.BaseData.size <= 3)是我们自定义的一个宏,用于检测数组长度并判断是否需要抛出异常(也是测试宏的使用)
这里数组使用了类似切片的方法。仓颉目前的数组切片采用[起始索引:终点索引]的方式。其中索引的截取方案参考文档的区间类型。格式是 start..end : step,它表示一个从 start 开始,以 step 为步长,到 end(不包含 end)为止的区间;“左闭右闭”区间的格式是 start..=end : step,它表示一个从 start 开始,以 step 为步长,到 end(包含 end)为止的区间。
例:
let n = 10
let r1 = 0..10 : 1 // r1 contains 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
let r2 = 0..=n : 1 // r2 contains 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
let r3 = n..0 : -2 // r3 contains 10, 8, 6, 4, 2
let r4 = 10..=0 : -2 // r4 contains 10, 8, 6, 4, 2, 0
本类需要一个ToArray函数,用于输出最终数组。但最终数组将由链路层提供:
public func toArray() : Array<UInt8>{
linkLayer.getFrameData()
}
最后定义两个构造函数,分别从命令创建,以及从数组创建:
init(id:UInt8,command:UInt8,initLen:UInt8,linkLayer:ILinkLayer) {
this.linkLayer = linkLayer
this.linkLayer.initLen(initLen)
linkLayer.BaseData.setUInt8(0,id)
linkLayer.BaseData.setUInt8(1,command)
}
init(buffer : Array<Byte>,linkLayer:ILinkLayer) {
this.linkLayer = linkLayer
this.linkLayer.setBuffer(buffer)
}
构造函数使用init方式,看起来由python的影子,感觉上比go强制没有构造函数的方式,在设计类的时候舒服多了。
好了,本届遗留两个问题:1:对数组扩展方法。2:宏的使用。
使用了以后会设计制作的一个重要的类:ILinkLayer
下一节我们介绍数组的扩展方法。