Device Communication Control服务,是一个客户端的BACnet用户使用设备通信控制服务向一个远程设备发送指令,指示这个设备在一个规定的时间内停止其起始和响应除了设备通信控制或者重新初始化设备之外的所有APDU。DCC主要由操作者用来进行设备诊断。
在dcc.c文件中,先是定义了BACNET_COMMUNICATION_ENABLE_DISABLE这个枚举值,用于表示当前设备的会话状态:
typedef enum {
COMMUNICATION_ENABLE = 0,
COMMUNICATION_DISABLE = 1,
COMMUNICATION_DISABLE_INITIATION = 2,
MAX_BACNET_COMMUNICATION_ENABLE_DISABLE = 3
} BACNET_COMMUNICATION_ENABLE_DISABLE;</span></strong>
由于设备在使用DCC服务时可能存在相应的时间期限,因此需要对DCC服务的时间期限进行设置:
bool dcc_set_status_duration(BACNET_COMMUNICATION_ENABLE_DISABLE status, uint16_t minutes)
{
bool valid = false;
/* valid? */
if (status < MAX_BACNET_COMMUNICATION_ENABLE_DISABLE) { // COMMUNICATION_ENABLE = 0, COMMUNICATION_DISABLE = 1, COMMUNICATION_DISABLE_INITIATION = 2
DCC_Enable_Disable = status;
if (status == COMMUNICATION_ENABLE) {
DCC_Time_Duration_Seconds = 0;
} else {
DCC_Time_Duration_Seconds = minutes * 60;
}
valid = true;
}
return valid;
}
dcc_set_status_duration判断是否设置了时间期限(Time Duration),如果设置了,则需要将这个时间以秒的形式表示。
同时,可以看到对DCC的状态有三种设置方法:
bool dcc_communication_enabled(void);
bool dcc_communication_disabled(void);
bool dcc_communication_initiation_disabled(void);
在VTS中,也同样只有三种状态:
接下来看看dcc_encode_apdu(.......)这个函数:
int dcc_encode_apdu(
uint8_t * apdu,
uint8_t invoke_id,
uint16_t timeDuration, /* 0=optional */
BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable,
BACNET_CHARACTER_STRING * password)
{ /* NULL=optional */
int len = 0; /* length of each encoding */
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_DEVICE_COMMUNICATION_CONTROL;
apdu_len = 4;
/* optional timeDuration */
if (timeDuration) {
len = encode_context_unsigned(&apdu[apdu_len], 0, timeDuration);
apdu_len += len;
}
/* enable disable */
len = encode_context_enumerated(&apdu[apdu_len], 1, enable_disable);
apdu_len += len;
/* optional password */
if (password) {
/* FIXME: must be at least 1 character, limited to 20 characters */
len =
encode_context_character_string(&apdu[apdu_len], 2, password);
apdu_len += len;
}
}
return apdu_len;
}
首先,先来看看设备通信控制服务的原语结构:
其中,Time Duration 和 Password 是用户可选,Enable 和 Disable 是必选参数。
apdu[0]~adpu[3] 是常规的定义,具体可参考前面的文章。接下来是判断 Time Duration 是否有设定,如果Time Duration有设定,参照VTS中关于Time Duration的信息可知:
对 time duration 编码的过程中需要进行标记编码,由于 time duration 是一个无符号整型类型,因此也需要对其进行无符号整型编码。在 encode_context_unsigned(...) 中包括了 encode_tag(...) 和 encode_bacnet_unsigned(...)。
接下来是 使能 / 禁止参数,这是一个必选参数。需要进行标记编码和枚举值编码,由于枚举值属于无符号整型类型的一种,因此也需要进行无符号标记编码。
最后是对password的编码。协议中规定password属性是一个不超过20个字符的字符串。在这里调用了一个字符串编码函数:
int encode_context_character_string(
uint8_t * apdu,
uint8_t tag_number,
BACNET_CHARACTER_STRING * char_string)
{
int len = 0;
int string_len = 0;
string_len =
(int) characterstring_length(char_string) + 1 /* for encoding */ ;
len += encode_tag(&apdu[0], tag_number, true, (uint32_t) string_len);
if ((len + string_len) < MAX_APDU) {
len += encode_bacnet_character_string(&apdu[len], char_string);
} else {
len = 0;
}
return len;
}
在这个函数中,其参数有这样一个类型BACNET_CHARACTER_STRING,它的定义如下:
<span style="font-family:Microsoft YaHei;">typedef struct BACnet_Character_String {
size_t length;
uint8_t encoding;
/* limit - 6 octets is the most our tag and type could be */
char value[MAX_CHARACTER_STRING_BYTES];
} BACNET_CHARACTER_STRING;</span>
characterstring_length(...)这个函数获取的是
BACNET_CHARACTER_STRING结构体中的 length 参数加上1。然后调用标记编码函数为这个password字符串进行标记。对这个字符串长度进行判断,看其是否小于APDU的最大长度1476,如果符合要求,则对这个password字符串的内容进行编码。encode_bacnet_character_string(...)包含三个编码过程,参照VTS中password字符串的信息,包括了length, value 和 ASCII Character Encoding。由此可知,在BACnet协议中,任何关于字符串的编码都包括了这三方面的内容。
最后我们就得到了在这个编码过程中得到的长度。