WebSocket 客户端驱动
该驱动会为每个模型建立一个连接, 驱动启动后主动建立连接并接收服务端发送的数据, 同时也可以向客户端发送数据.
脚本说明
脚本语言: JavasScript ECMAScript 5.1
驱动使用时要求提供 连接处理脚本
, 解析处理脚本
, 指令处理脚本
和 定时器脚本
4 个脚本函数来处理接收和发送数据过程中的协议和数据格式问题.
在脚本的上下文中内置了 Buffer
包, 可用于处理接收或发送二进制数据.
除此之外, 还内置了 lodash
, crypto-js
, moment
, xml-js
和 formulajs(Excel函数)
包.
注: 所有的脚本的函数名必须为
handler
.
客户端对象
在每次脚本中的参数中提供了 client
对象, 该对象为当前 WebSocket
连接客户端对象,
可以通过该对象实现向客户端发送数据的功能.
例如: 向客户端发送 ack
信息等. 该对象提供了以下函数:
send(params)
用于向客户端发送数据. 例如: 在接收到数据时向客户端发送 ack
信息.
该函数的参数为 {messageType: number; data: Buffer}
对象, 说明如下:
messageType
: 消息类型, 数值. 1: 文本消息, 2: 二进制消息.data
: 发送的数据,Buffer
对象.
示例
function handler(client, buffer) {
// 发送文本消息
client.send({messageType: 1, data: Buffer.from("这是一段文本消息")});
// 发送二进制消息
const binData = Buffer.capacity(128);
binData[0] = '@';
binData.writeInt16LE(15, 1);
binData.writeInt16LE(34, 3);
binData[5] = '@';
// send 函数的返回值为 string 或 undefined
// 如果参数不正确(字节数组为空或空数组)或发送失败则返回 string 内容为错误说明, 如果发送成功则返回 undefined
const result = client.send({messageType: 2, data: binData});
if(result) {
console.log("发送失败,", result);
}
}
连接处理脚本
当连接状态发生变化时会调用该函数. 例如: 当连接建立时向服务端发送数据.
说明:
- 当连接断开时, 会调用该函数, 此时
state
的值为false
, 此时无法通过client
发送数据.- 当驱动启动或者重连建立连接时, 会调用该函数, 此时
state
的值为true
.- 当重启驱动时, 该函数会调用两次, 第一次
state
的值为false
, 第二次为true
.
函数定义
/**
* 连接处理脚本, 当与服务端连接建立或断开时执行的操作
*
* @param client 客户端对象
* @param {function(Buffer)} client.send({messageType: 1, data: Buffer.from("hello world")}) 向服务端发送该数据(只有 state 为 true 时可用). messageType: 1: 文本消息, 2: 二进制消息
* @param state 连接状态, true: 已连接, false: 已断开
*/
function handler(client, state) {
if(state) {
// 当连接已建立时执行操作. 例如: 可以发送登录请求
} else {
// 当连接断开时执行操作
}
}
示例
/**
* 连接处理脚本, 当与服务端连接建立或断开时执行的操作
*
* @param client 客户端对象
* @param {function(Buffer)} client.send({messageType: 1, data: Buffer.from("hello world")}) 向服务端发送该数据(只有 state 为 true 时可用). messageType: 1: 文本消息, 2: 二进制消息
* @param state 连接状态, true: 已连接, false: 已断开
*/
function handler(client, state) {
if(state) {
// 当连接已建立时执行操作. 例如: 可以发送登录请求
const loginParams = {"type": "login", "username": "user1", "password": "123456"};
client.send({messageType: 1, data: Buffer.from(JSON.stringify(loginParams))});
} else {
// 当连接断开时执行操作
}
}
数据处理脚本
该脚本用于处理从服务端接收到的数据, 然后将数据解析为平台规定的格式并返回.
函数定义
/**
* 数据处理脚本, 解析从 websocket 接收到的数据并转换为平台规定的数据格式
*
* @param {Object} client 客户端对象
* @param {function(Buffer)} client.send({messageType: 1, data: Buffer.from("hello world")}) 向服务端发送该数据. messageType: 1: 文本消息, 2: 二进制消息
* @param {Buffer} buffer 接收到的数据
*/
function handler(client, buffer) {
// 返回值说明:
// id: 必填, 设备编号或自定义标识.
// table: 可选, 设备所属工作表标识. 当多个工作表中存在相同的设备编号或自定义标识时, 需要指定该设备所属的工作表, 否则会丢弃访数据
// time: 可选, 采集数据的时间, 毫秒. 如果为 0 则取服务器的时间.
// values: 必填, 数据点的值. key: 数据点标识, value: 数据点的值
return [{"id": "deviceId", "table": "tableId", "time": new Date().getTime(), "values": {"tagId1": 123, "tagId2": "abc"}}];
}
示例
function handler(client, data) {
// 以 json 格式为例, 例如: {"id":"d01","time":"2022-10-17 17:57:32","values":[{"name":"temperature","data":17.5},{"name":"humidity","data":35.7}]}
// 将 data 解析为 json 对象
const jsonData = JSON.parse(data.toString());
// 从对象中提取时间信息
const time = moment(jsonData.time, "YYYY-MM-DD HH:mm:ss");
// 将 values 字段解析为平台规定的格式 {"key1": value1, "key2": "value2", ...}
const values = {};
for (let i = 0; i < jsonData.values.length; i++) {
const value = jsonData.values[i];
values[value.name] = value.data;
}
// 返回解析后的结果数据
return [
{id: jsonData.id, values: values, time: time.valueOf()}
]
}
指令处理脚本
该脚本用于将发送的指令内容转换为字节数组, 当向设备发送指令时, 驱动会将要发送的内容先经过 handler
函数处理,
返回结果作为实际发送的内容.
函数定义
/**
* 指令处理脚本, 当发送指令时将指令信息转换为字节数组
*
* @param {Object} client 客户端对象
* @param {string} serialNo 平台指令序号
* @param {string} table 表标识
* @param {string} deviceId 设备标识
* @param {object} command 指令信息, 详细格式说明见驱动配置文档
* @return {Buffer} 最终发送数据
*/
function handler(client, serialNo, table, deviceId, command) {
// messageType:
// 1: 文本消息
// 2: 二进制消息
//
// data: 最终发送的数据, 必须为 Buffer 对象
return {"messageType": 1, "data": Buffer.from(command.ops[0].value)};
}
示例
// 发送文本消息
function handler(client, data) {
// 从指令信息中取出要发送的内容, 格式为 base64
const value = data.ops[0].value;
// 将要发送的内容做 base64 解码
return {"messageType": 1, "data": Buffer.from(value, 'hex')};
}
定时器处理脚本
定时器脚本用于定义驱动周期性的向服务端发送数据, 例如: 心跳数据或数据请求等
当开启定时器后, 驱动定时向执行脚本中的 handler
函数, 可以在 handler
脚本中通过调用 client.send
函数向服务端发送数据.
函数定义
/**
* 定时器脚本, 定时执行一些动作. 例如: 定时发送数据
*
* @param {Object} client 客户端对象
* @param {String[]} deviceIds 共享同一连接的设备标识列表, 即服务端地址相同的设备
*/
function handler(client, deviceIds) {
client.send({"messageType": 1, "data": Buffer.from("ping")});
}
示例
function handler(client, deviceIds) {
for(let i=0; i<deviceIds.length; i++) {
client.send({messageType: 1, data: Buffer.from(`{"type": "request", "deviceId": ${deviceIds[i]}}`)});
}
}