【仓颉】二、一个仓颉的Modbus库-5. 接口实现:ILinkLayer

在前面的ModbusBase里我们还留了个坑:ILinkLayer连接层,以及其实现ModbusRTUFrameLinkLayer与ModbusTCPFrameLinkLayer。

首先我们先考虑,ILinkLayer是最外层包裹数据的,其数据部分无论RTU的CRC校验,还是TCP的6各byte头,都与其内容无关(最多看一下长度)。因此,总数组最适合在这里定义。

prop BaseData : Array<UInt8>

基础数据的属性。也就是ModbusBase里使用的BaseData。它从完整数据里切片。完整数据由不同链路层决定。

func initLen(len:UInt8):Unit

用于ModbusBase初始化时,将自己的长度传给链路层,让链路层知道要初始化一个多大的buffer空间。

func setBuffer(buffer:Array<Byte>):Unit

用于ModbusBase将一整个数组丢过来~~~不同的链路层自行去掉链路层的数据后,剥离出BaseData给ModbusBase。以及考虑是否需要进行响应的校验

func getFrameData():Array<UInt8>

获取包含链路层的全部数组。

可以看到,我们链路层作为最外的一层壳,功能与Modbus分离的很清晰,但又依赖于Modbus组件好之后作为外壳发送出去。因此我将ILinkLayer作为Modbus的一个属性对ModbusBase进行包装,帮助ModbusBase功能完整。

ModbusRTUFrameLinkLayer的功能是对ModbusBase增加CRCL与CRCH

class ModbusRTUFrameLinkLayer <: ILinkLayer{
    var buffer = Array<Byte>()
    public func setBuffer(buffer:Array<Byte>){
        if(buffer.size < 4){
            throw IndexOutOfBoundsException("数组长度不足")
        }
        this.buffer = buffer
    }
    public func initLen(len:UInt8):Unit{
        if(len < 2){
            throw IndexOutOfBoundsException("数组长度不足")
        }
        this.buffer = Array<Byte>(Int64(len+2),item:0)
    }
    public prop BaseData : Array<UInt8>{
        get(){
            if(buffer.size > 2){
                return buffer[..(buffer.size - 2)]
            }
            else{
                throw NoneValueException("数组未初始化")
            }
        }
    }
    public func getFrameData():Array<UInt8>{
        var crc = crcCal(buffer,(buffer.size - 2))
        buffer[buffer.size - 1] = UInt8((crc & 0xFF00) >> 8)
        buffer[buffer.size - 2] = UInt8(crc & 0x00FF)
        this.buffer
    }
    public func checkCRC():Bool{
        var crc = crcCal(buffer, (buffer.size - 2))
        crc == ((UInt16(buffer[buffer.size - 1]) << 8) | UInt16(buffer[buffer.size - 2]))
    }
    static func crcCal(buffer:Array<UInt8>,len:Int64) : UInt16{
        var crc = 0xFFFFu16
        for (i in 0..len){
            crc = crc ^ UInt16(buffer[i])
            for (_ in 0..8){
                if((crc & 0x0001u16)!= 0){
                    crc = (crc >> 1) ^ 0xA001
                }
                else{
                    crc = crc >> 1
                }
            }
        }
        crc
    }
}

ModbusTCPFrameLinkLayer则是对ModbusBase增加六个字节头:

内容

解释

事务处理标识

可以理解为报文的序列号,一般每次通信之后就要加1以区别不同的通信数据报文。

协议标识符

00 00表示ModbusTCP协议。

长度

表示接下来的数据长度,单位为字节。

代码如下:

class ModbusTCPFrameLinkLayer <: ILinkLayer{
    var buffer = Array<Byte>()
    var transaction_id_bak = 0u16
    init(transactionID!:UInt16 = 0){
        transaction_id_bak = transactionID
    }
    public func setBuffer(buffer:Array<Byte>){
        if(buffer.size < 8){
            throw IndexOutOfBoundsException("数组长度不足")
        }
        this.buffer = buffer
    }
    public func initLen(len:UInt8):Unit{
        if(len < 2){
            throw IndexOutOfBoundsException("数组长度不足")
        }
        buffer = Array<Byte>(Int64(len+6),item:0)
        TransactionId = transaction_id_bak
    }
    public prop BaseData :  Array<UInt8>{
        get(){
            if(buffer.size > 6 && buffer.size < 262){
                return buffer[6..]
            }
            else{
                throw NoneValueException("数组未初始化")
            }
        }
    }
    //ModbusTCP报文帧的事务序号
    public mut prop TransactionId: UInt16 {
        get() {
            if(buffer.size > 6){
                return buffer.getUInt16AB(0)
            } else{
                return UInt16(transaction_id_bak & 0x0000ffff)
            }
        }
        set(value) {
            if(buffer.size > 6){
                buffer.setUInt16AB(0,value)
                transaction_id_bak = 0
            } else{
                transaction_id_bak = value
            }
        }
    }
    var protocolId : UInt16 = 0
    //Modbus报文帧的协议字
    public prop ProtocolId: UInt16 {
        get() {
            protocolId
        }
    }
    //ProtocolID一般不用修改。为兼容特殊应用,单独保留设置选项
    public func setProtocolId(protocolID:UInt16){
        this.protocolId = protocolID
    }
    public prop TotLen : UInt16 {
        get() {
            UInt16(BaseData.size)
        }
    }
    public mut prop UnitId : UInt8 {
        get() {
            buffer.getUInt8(6)
        }
        set(value) {
            buffer.setUInt8(6,value)
        }
    }
    public func getFrameData():Array<UInt8>{
        //设置协议标识
        buffer.setUInt16AB(2,ProtocolId)
        //设置长度
        buffer.setUInt16AB(4,TotLen)
        buffer
    }
}

再补上接口的代码:

package modbus

interface ILinkLayer{
    func setBuffer(buffer:Array<Byte>):Unit
    func initLen(len:UInt8):Unit
    prop BaseData : Array<UInt8>
    func getFrameData():Array<UInt8>
}

我这些放在了一个文件里:src/modbus/modbus_linklayer.cj

  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值