分析bacnet协议栈源码 AtomicReadFile 服务

          一个客户端的BACnet用户使用基本读文件服务对某个文件进行一个“打开--读出--关闭”的操作。访问的文件可以是字节流,也可以是编号记录。以下是基本读文件的服务原语:

          

          这是在基本读文件中定义的数据结构:

typedef struct BACnet_Atomic_Read_File_Data {
    BACNET_OBJECT_TYPE object_type;
    uint32_t object_instance;
    BACNET_FILE_ACCESS_METHOD access;
    union {
        struct {
            int32_t fileStartPosition;
            uint32_t requestedOctetCount;
        } stream;
        struct {
            int32_t fileStartRecord;
            /* requested or returned record count */
            uint32_t RecordCount;
        } record;
    } type;
    BACNET_OCTET_STRING fileData;
    bool endOfFile;
} BACNET_ATOMIC_READ_FILE_DATA;
          在这个数据结构的定义中,包括了对象类型,对象实例,访问方法(access),一个联合体type,在联合体中包括了以下两个结构:stream 和 record。在VTS中,这两个结构代表了两种access:

还有表示文件数据的fileData,以及判断是否到达文件末尾的endOfFile。

回到arf_encode_apdu(...)函数:

int arf_encode_apdu(
    uint8_t * apdu,
    uint8_t invoke_id,
    BACNET_ATOMIC_READ_FILE_DATA * data)
{
    int apdu_len = 0;   /* total length of the apdu, return value */

    if (apdu) {
        apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
        apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
        apdu[2] = invoke_id;
        apdu[3] = SERVICE_CONFIRMED_ATOMIC_READ_FILE;   /* service choice */
        apdu_len = 4;
        apdu_len +=
            encode_application_object_id(&apdu[apdu_len], data->object_type,
            data->object_instance);
        switch (data->access) {
            case FILE_STREAM_ACCESS:
                apdu_len += encode_opening_tag(&apdu[apdu_len], 0);
                apdu_len += encode_application_signed(&apdu[apdu_len],
                    data->type.stream.fileStartPosition);
                apdu_len +=
                    encode_application_unsigned(&apdu[apdu_len],
                    data->type.stream.requestedOctetCount);
                apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
                break;
            case FILE_RECORD_ACCESS:
                apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
                apdu_len +=
                    encode_application_signed(&apdu[apdu_len],
                    data->type.record.fileStartRecord);
                apdu_len +=
                    encode_application_unsigned(&apdu[apdu_len],
                    data->type.record.RecordCount);
                apdu_len += encode_closing_tag(&apdu[apdu_len], 1);
                break;
            default:
                break;
        }
    }

    return apdu_len;
}

          apdu[0]~apdu[3]存储了关于apdu报文的基本信息,具体分析看前面的文章。然后调用了encode_application_object_id(...),根据传入的对象类型 (object_type) 和对象实例 (object_instance) 进行编码,根据15.3.2中的服务过程,响应的BACnet用户检验了请求的合法性之后就会创建一个具有“ 对象说明符 ”参数所规定的类型的新对象的操作。具体编码过程可参照协议书的 20.2.14 小节。

        data access 包括 FILE_STREAM_ACCESS 以及 FILE_RECORD_ACCESS 两个分类。当选择的是FILE_STREAM_ACCESS

的时候,表示文件流访问,包含了 ‘ 文件开始位置 ’  参数和 ‘ 请求的字节数目 ’ 参数。

          当选择的是FILE_STREAM_ACCESS 或者 FILE_RECORD_ACCESS 的时候,需要将它们的信息包含在opening_tag和closing_tag之间。以

FILE_STREAM_ACCESS 为例,在VTS中,它的格式如下:


          可以看到,首先对opening_tag进行编码,然后开始对FILE_STREAM_ACCESS的内容进行编码,包括‘ 文件开始位置 ’ 和 ‘ 请求的字节数目 ‘,最后编码结束的时候需要进行closing_tag的编码。由于fileStartPosition 是一个整型类型,因此需要调用encode_application_signed(...)进行编码。由于encode_application_signed(....)之前的文章中没有提到过,这里就对这个函数做一个分析。

       

int encode_application_signed(
    uint8_t * apdu,
    int32_t value)
{
    int len = 0;        /* return value */

    /* assumes that the tag only consumes 1 octet */
    len = encode_bacnet_signed(&apdu[1], value);
    len +=
        encode_tag(&apdu[0], BACNET_APPLICATION_TAG_SIGNED_INT, false,
        (uint32_t) len);

    return len;
}

              这个函数需要调用两个函数,encode_bacnet_signed(.....) 和encode_tag(.....),encode_tag(...)是对 fileStartPosition 中的application tag 进行编码,重点来看看encode_bacnet_signed(....)这个函数:

int encode_bacnet_signed(
    uint8_t * apdu,
    int32_t value)
{
    int len = 0;        /* return value */
    if ((value >= -128) && (value < 128)) {
        len = encode_signed8(&apdu[0], (int8_t) value);
    } else if ((value >= -32768) && (value < 32768)) {
        len = encode_signed16(&apdu[0], (int16_t) value);
    } else if ((value > -8388608) && (value < 8388608)) {
        len = encode_signed24(&apdu[0], value);
    } else {
        len = encode_signed32(&apdu[0], value);
    }

    return len;
}
         
          根据20.2.5小节中的编码规则,有符号整型数值在内容字节中被编码成一个取值范围为-2^(8*L) 到 (2^(8*L) - 1)的,使用2的补码标记的二进制数。其中L 是用于对这个值编码的字节数。如果value的取值在[-128 , 128),L取值为1;当value取值在[-32768 , 32768),L取值为2;当value取值在[-8388608, 8388608),L取值为3;否则,L取值为4。

         FILE_RECORD_ACCESS的编码与FILE_STREAM_ACCESS几乎相同,但是FILE_RECORD_ACCESS是一个无符号整型类型,因此在编码的时候需要调用encode_bacnet_unsigned(...)这个函数。

         最后返回的是apdu的长度。


  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值