阿里云物联网平台-数据解析脚本详解
var COMMAND_REPORT = 0x00; //属性上报。
var COMMAND_SET = 0x01; //属性设置。
var COMMAND_REPORT_REPLY = 0x02; //上报数据返回结果。
var COMMAND_SET_REPLY = 0x03; //属性设置设备返回结果。
var COMMAD_UNKOWN = 0xff; //未知的命令。
定义五个参数,分别在后面的解析函数中用到。
var ALINK_PROP_REPORT_METHOD = 'thing.event.property.post'; //物联网平台Topic,设备上传属性数据到云端。
var ALINK_PROP_SET_METHOD = 'thing.service.property.set'; //物联网平台Topic,云端下发属性控制指令到设备端。
var ALINK_PROP_SET_REPLY_METHOD = 'thing.service.property.set'; //物联网平台Topic,设备上报属性设置的结果到云端。
var SELF_DEFINE_TOPIC_UPDATE_FLAG = '/user/update' //自定义Topic:/user/update。
var SELF_DEFINE_TOPIC_ERROR_FLAG = '/user/update/error' //自定义Topic:/user/update/error。
定义请求方法method参数的值,详见阿里云 <设备属性、事件、服务> 文档
设备属性、事件、服务
<函数一>
/** * 将设备的自定义格式数据转换为Alink协议的数据,设备上报数据到物联网平台时调用
* 入参:rawData byte[]数组 不能为空
* 出参:jsonObj Alink JSON对象 不能为空 */
function rawDataToProtocol(bytes) {
var uint8Array = new Uint8Array(bytes.length);
for (var i = 0; i < bytes.length; i++) {
uint8Array[i] = bytes[i] & 0xff;
}
/*将设备上报的原始数据转换为数组。其中bytes对象中存储着设备上报原始数据。*/
var dataView = new DataView(uint8Array.buffer, 0);
var jsonMap = new Object();
var fHead = uint8Array[0]; // 第0个字节00,
if (fHead == COMMAND_REPORT) {//判断第0个字节是否等于属性上报标识符,也就是说属性上报第0个字节必须为00(自己前面定义的十六进制).
jsonMap['method'] = ALINK_PROP_REPORT_METHOD; //ALink JSON格式,属性上报topic。method='thing.event.property.post
jsonMap['version'] = '1.0'; //ALink JSON格式,协议版本号固定字段。
jsonMap['id'] = '' + dataView.getInt32(1); //ALink JSON格式,标示该次请求id值。
var params = {}; // 定义属性存放对象。
var jsonMap = {};
params['prop_int16'] = dataView.getInt16(5); //对应产品属性中prop_int16。
params['prop_bool'] = uint8Array[7]; //对应产品属性中prop_bool。
params['prop_float'] = dataView.getFloat32(8); //对应产品属性中prop_float。
jsonMap['params'] = params; //ALink JSON格式,params标准字段。将参数打包到数据帧中。
}
else if(fHead == COMMAND_SET_REPLY) {//判断第0个字节是否等于属性设置设备返回结果
jsonMap['version'] = '1.0'; //ALink JSON格式,协议版本号固定字段。
jsonMap['id'] = '' + dataView.getInt32(1); //ALink JSON格式,标示该次请求id值。
jsonMap['code'] = ''+ dataView.getUint8(5);
jsonMap['data'] = {};
}
return jsonMap;
}
这段代码里面最重要的就是dataView.getInt16()与uint8Array[];
dataView.getInt16()是dataView中的内置函数,用于在指定位置(即,距dataView起始字节偏移)获取16位整型。uint8Array[]则是直接取字节。
例如:
- dataView.getInt32(1),从第一个字节开始取32位整型转化为十进制,如上图所示,四个字节为32位,也就是00000001,十六进制转为十进制,id=1。
- params[‘prop_int16’] = dataView.getInt16(5); //对应产品属性中prop_int16。
从第五个字节开始,取16位整型。也就是第5和第6个字节。0032->50(十进制)。 params[‘prop_bool’] =50。 - params[‘prop_bool’] = uint8Array[7];
//对应产品属性中prop_bool。直接取字节,第七个字节,注意字节要从第0个开始计数。第七个字节为01。 - params[‘prop_float’] = dataView.getFloat32(8);
//对应产品属性中prop_float。从第8个字节开始,取32位浮点型。也就是第8、第9、第10、第11个字节。00000000->0(十进制)。
<函数二>
/*云端下发指令解析函数*/
function protocolToRawData(json) {
var method = json['method'];
var id = json['id'];
var version = json['version'];
var payloadArray = [];
if (method == ALINK_PROP_SET_METHOD) //属性设置。云端下发属性控制指令到设备端的这个物模型topic。接收来自物联网平台的“设置设备属性”的命令。
{
var params = json['params'];
var prop_float = params['prop_float'];
var prop_int16 = params['prop_int16'];
var prop_bool = params['prop_bool'];//将设置的具体值抽取出来。
//按照自定义协议格式拼接 rawData。
payloadArray = payloadArray.concat(buffer_uint8(COMMAND_SET)); //command字段。属性设置。01
payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); //ALink JSON格式 'id'。配置32位的int整型id。12345->00 00 30 39
payloadArray = payloadArray.concat(buffer_int16(prop_int16)); //属性'prop_int16'的值。配置16位的int整型。333->01 4d
payloadArray = payloadArray.concat(buffer_uint8(prop_bool)); //属性'prop_bool'的值。配置8位的无符号int整型。1->01
payloadArray = payloadArray.concat(buffer_float32(prop_float)); //属性'prop_float'的值。配置32位的浮点型。754标准
}
else if (method == ALINK_PROP_REPORT_METHOD) { //设备上报数据返回结果。
var code = json['code'];
payloadArray = payloadArray.concat(buffer_uint8(COMMAND_REPORT_REPLY)); //command字段。上报数据返回结果。
payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); //ALink JSON格式'id'。
payloadArray = payloadArray.concat(buffer_uint8(code));
}
else { //未知命令,对于这些命令不做处理。
var code = json['code'];
payloadArray = payloadArray.concat(buffer_uint8(COMMAD_UNKOWN)); //command字段。
payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); //ALink JSON格式'id'。
payloadArray = payloadArray.concat(buffer_uint8(code));
}
return payloadArray;
}
payloadArray.concat();这个函数就是取相应的位数进行拼接。
例如 :
- payloadArray = payloadArray.concat(buffer_int32(parseInt(id)));
//配置32位的int整型id。12345->00 00 30 39,十进制转十六进制即可。 - payloadArray = payloadArray.concat(buffer_int16(prop_int16));
//属性’prop_int16’的值。配置16位的int整型。333->01 4d - payloadArray = payloadArray.concat(buffer_uint8(prop_bool));
//属性’prop_bool’的值。配置8位的无符号int整型。1->01 - payloadArray = payloadArray.concat(buffer_float32(prop_float));
//属性’prop_float’的值。配置32位的浮点型。
唯一比较难算的就是拼接浮点型了,按照754标准拼接即可。
<函数三>
/**
* 将设备自定义topic数据转换为json格式数据, 设备上报数据到物联网平台时调用
* 入参:topic 字符串,设备上报消息的topic
* 入参:rawData byte[]数组 不能为空
* 出参:jsonObj JSON对象 不能为空
*/
function transformPayload(topic, bytes) {
var uint8Array = new Uint8Array(bytes.length);
for (var i = 0; i < bytes.length; i++) {
uint8Array[i] = bytes[i] & 0xff;
}
var dataView = new DataView(uint8Array.buffer, 0);
var jsonMap = {};
if(topic.includes(SELF_DEFINE_TOPIC_ERROR_FLAG)) {
jsonMap['topic'] = topic;
jsonMap['errorCode'] = dataView.getInt8(0)
}
else if (topic.includes(SELF_DEFINE_TOPIC_UPDATE_FLAG)) {
jsonMap['topic'] = topic;
jsonMap['prop_int16'] = dataView.getInt16(5);
jsonMap['prop_bool'] = uint8Array[7];
jsonMap['prop_float'] = dataView.getFloat32(8);
}
return jsonMap;
}
和之前一样,唯一的就是topic是自定义topic时使用这个函数。
自定义topic数据解析:
- 仅华东2(上海)、华北2(北京)、华南1(深圳)地域支持自定义Topic数据解析。
- 仅通过MQTT协议接入的设备支持自定义Topic数据解析。 仅解析设备上报云端的数据,不解析云端下行数据。
- 解析上报数据的Payload,并返回解析后的Payload。
- 解析前后,数据所在Topic不变。例如,设备发送到/{deviceName}/user/update的数据,解析后仍在该Topic中。
- 配置设备端时,需在发布消息的自定义Topic后添加数据解析标记?_sn=default。物联网平台仅解析设备通过携带标记的Topic发布的数据。在物联网平台创建自定义Topic时按正常Topic定义,不添加该解析标记。
- 如果产品的数据格式为透传/自定义,还需编写物模型数据解析脚本。
自定义解析的时候数据脚本中transformPayload函数必须定义,而且内部需要解析指令。
物模型数据解析
- 设备端也应该选择物模型的topic
- 属性为默认模块属性,若使用物模型自定义模块,标识符(identifier)的格式为模块标识符:属性标识符。例如,
model1:prop_int16 。
- 数据格式选择为透传/自定义。
- 物模型数据解析的时候数据脚本中transformPayload函数也需要定义,但是可以只定义一个空函数即可,不必往里面写东西。