316 lines
11 KiB
JavaScript
316 lines
11 KiB
JavaScript
|
||
// 接收数据
|
||
// 压差站号:01,温湿度站号:02,门控制站号:03
|
||
// 压差 01 03 02 12 34 B5 33
|
||
// 温湿度 01 03 04 02 98 00 BC 7B D5
|
||
// IC卡 20 01 00 08 04 00 00 00 0e 26 fe ab 8f 03
|
||
// ID卡 20 01 00 05 49 00 b5 6a b5 d8 03
|
||
// 卡离开 20 01 00 01 02 fd 03
|
||
// 门控制指令(中盛4路数字IO模块,功能码06):
|
||
// 左门开门: 03 06 00 00 00 01 49 E8
|
||
// 左门关门: 03 06 00 00 00 00 88 28
|
||
// 右门开门: 03 06 00 01 00 01 18 28
|
||
// 右门关门: 03 06 00 01 00 00 D9 E8
|
||
|
||
// 卡片放入和离开 起始位20, 所有卡片通过同一通道读取,通过数据库比对判断类型
|
||
// let RS232 = getApp().globalData.RS232
|
||
import store from '@/store/index.js'
|
||
|
||
// ========== RS485 优先级消息队列 START ==========
|
||
const PRIORITY = {
|
||
HIGH: 0, // 用户操作(开门/关门/控制指令)
|
||
LOW: 1, // 定时轮询(getTemp/getPressure)
|
||
}
|
||
|
||
const rs485Queue = []
|
||
let isRS485Sending = false
|
||
const RS485_SEND_DELAY = 200 // 每条指令间隔200ms,确保总线空闲
|
||
|
||
/**
|
||
* 将任务按优先级插入队列
|
||
* 高优先级(0)插入到队列前面低优先级任务之前
|
||
* 低优先级(1)追加到队列末尾
|
||
*/
|
||
const enqueueTask = (task) => {
|
||
if (task.priority === PRIORITY.HIGH) {
|
||
let insertIndex = rs485Queue.findIndex(t => t.priority === PRIORITY.LOW)
|
||
if (insertIndex === -1) {
|
||
rs485Queue.push(task)
|
||
} else {
|
||
rs485Queue.splice(insertIndex, 0, task)
|
||
}
|
||
} else {
|
||
rs485Queue.push(task)
|
||
}
|
||
}
|
||
|
||
const processRS485Queue = async () => {
|
||
if (isRS485Sending || rs485Queue.length === 0) {
|
||
return
|
||
}
|
||
isRS485Sending = true
|
||
const task = rs485Queue.shift()
|
||
try {
|
||
let RS485 = getApp().globalData.RS485
|
||
if (!RS485) {
|
||
throw new Error('RS485 not initialized')
|
||
}
|
||
const result = RS485.sendDataString(task.cmd)
|
||
if (result && typeof result.then === 'function') {
|
||
await result
|
||
}
|
||
// 发送后延迟,确保总线空闲再发下一条
|
||
await new Promise(r => setTimeout(r, RS485_SEND_DELAY))
|
||
task.resolve()
|
||
} catch (error) {
|
||
console.error('RS485 send error:', error)
|
||
task.reject(error)
|
||
} finally {
|
||
isRS485Sending = false
|
||
processRS485Queue()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送RS485指令(带优先级)
|
||
* @param {string} cmd - 十六进制指令字符串
|
||
* @param {number} priority - 优先级,默认HIGH
|
||
* @returns {Promise}
|
||
*/
|
||
const enqueueRS485Send = (cmd, priority = PRIORITY.HIGH) => {
|
||
return new Promise((resolve, reject) => {
|
||
enqueueTask({ cmd, priority, resolve, reject })
|
||
processRS485Queue()
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 清空队列(重连时调用,拒绝所有待发送请求)
|
||
*/
|
||
const clearRS485Queue = () => {
|
||
while (rs485Queue.length > 0) {
|
||
const task = rs485Queue.shift()
|
||
task.reject(new Error('RS485队列已清空(重连中)'))
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前队列长度(调试用)
|
||
*/
|
||
const getRS485QueueSize = () => {
|
||
return rs485Queue.length
|
||
}
|
||
// ========== RS485 优先级消息队列 END ==========
|
||
|
||
export default {
|
||
// 开左门指令 (通过RS485站号03) - 高优先级
|
||
// 中盛4路数字IO模块,功能码06(写单个保持寄存器)
|
||
LeftDoor: async (status) => {
|
||
// 左门开门: 03 06 00 00 00 01 49 E8
|
||
// 左门关门: 03 06 00 00 00 00 88 28
|
||
let cmd = status ? '03060000000149E8' : '0306000000008828'
|
||
store.state.relay.leftDoor = status
|
||
// door状态 = leftDoor OR rightDoor (任一门打开则door为true)
|
||
store.state.relay.door = store.state.relay.leftDoor || store.state.relay.rightDoor
|
||
await enqueueRS485Send(cmd, PRIORITY.HIGH)
|
||
},
|
||
|
||
// 开右门指令 (通过RS485站号03) - 高优先级
|
||
// 中盛4路数字IO模块,功能码06(写单个保持寄存器)
|
||
RightDoor: async (status) => {
|
||
// 右门开门: 03 06 00 01 00 01 18 28
|
||
// 右门关门: 03 06 00 01 00 00 D9 E8
|
||
let cmd = status ? '0306000100011828' : '030600010000D9E8'
|
||
store.state.relay.rightDoor = status
|
||
// door状态 = leftDoor OR rightDoor (任一门打开则door为true)
|
||
store.state.relay.door = store.state.relay.leftDoor || store.state.relay.rightDoor
|
||
await enqueueRS485Send(cmd, PRIORITY.HIGH)
|
||
},
|
||
|
||
// 开门关门控制 01
|
||
// Door: async (status) => {
|
||
// let op = status ? '01' : '00'
|
||
// let cmd = '5AA5EE01' + op
|
||
// store.state.relay.door = status
|
||
// let RS232 = getApp().globalData.RS232
|
||
// await RS232.sendDataString(cmd);
|
||
// },
|
||
// 开风机指令 02
|
||
Wind: async (status) => {
|
||
let op = status ? '01' : '00'
|
||
let cmd = '5AA5EE02' + op
|
||
store.state.relay.wind = status
|
||
let RS232 = getApp().globalData.RS232
|
||
await RS232.sendDataString(cmd);
|
||
},
|
||
// 开灯指令 03
|
||
Light: async (status) => {
|
||
let op = status ? '01' : '00'
|
||
let cmd = '5AA5EE03' + op
|
||
store.state.relay.light = status
|
||
let RS232 = getApp().globalData.RS232
|
||
await RS232.sendDataString(cmd);
|
||
},
|
||
// 开真空指令 04
|
||
Vacuum: async (status) => {
|
||
let op = status ? '01' : '00'
|
||
let cmd = '5AA5EE04' + op
|
||
store.state.relay.vacuum = status
|
||
let RS232 = getApp().globalData.RS232
|
||
await RS232.sendDataString(cmd);
|
||
},
|
||
// 开关消毒指令 05
|
||
Disinfect: async (status) => {
|
||
let op = status ? '01' : '00'
|
||
let cmd = '5AA5EE05' + op
|
||
store.state.relay.disinfect = status
|
||
let RS232 = getApp().globalData.RS232
|
||
await RS232.sendDataString(cmd);
|
||
},
|
||
// 获取温湿度(通过RS485站号02)- 低优先级
|
||
getTemp: () => {
|
||
let cmd = '020300000002C438';
|
||
return enqueueRS485Send(cmd, PRIORITY.LOW)
|
||
},
|
||
|
||
// 获取压差指令(通过RS485站号01)- 低优先级
|
||
getPressure: () => {
|
||
let cmd = '01030001000295CB'
|
||
return enqueueRS485Send(cmd, PRIORITY.LOW)
|
||
},
|
||
|
||
// 导出队列管理方法
|
||
clearRS485Queue,
|
||
getRS485QueueSize,
|
||
|
||
// 解析门卡数据,返回卡号
|
||
parse232dData: (hexString) => {
|
||
// IC卡 20 01 00 08 04 00 00 00 0e 26 fe ab 8f 03
|
||
// ID卡 20 01 00 05 49 00 b5 6a b5 d8 03
|
||
// 卡离开 20 01 00 01 02 fd 03
|
||
// 01 地址, 04 00 卡类型,0e 26 fe ab 卡号,8f 校验 03固定值
|
||
// 人员卡地址号0,卡号4个字节,倒数4个字节,倒数一位03固定值,倒数二位校验
|
||
// 内镜卡地址号1-16,卡号4个字节,倒数4个字节,倒数一位03固定值,倒数二位校验
|
||
// 卡离开8位,卡进入11位和14位
|
||
|
||
// 移除空格和换行符
|
||
const hexStr = hexString.replace(/\s+/g, '');
|
||
|
||
// 验证输入
|
||
if (hexStr.length === 0) {
|
||
throw new Error('请输入Modbus十六进制数据');
|
||
}
|
||
|
||
if (hexStr.length % 2 !== 0) {
|
||
throw new Error('请输入有效的Modbus十六进制数据');
|
||
}
|
||
|
||
// 解析后的数据对象
|
||
let data = {};
|
||
// 刷卡数据 起始字节20,第二字节站号,结束字节03
|
||
if (hexStr.substr(0, 2) == '20' && hexStr.substr(hexStr.length - 2, 2) == '03') {
|
||
// 卡数据
|
||
let hexNum = hexStr.substr(hexStr.length - 12, 8)
|
||
// 第二位是地址
|
||
let addHex = hexStr.substr(2, 2)
|
||
// 转换成10进制地址号
|
||
let address = parseInt(addHex, 16)
|
||
|
||
// 解析后的数据 { address: 0, number: 12345678, cardType: 'ic', action: 'enter', type: 'person' }
|
||
data.address = address
|
||
if (hexStr.length == 28) {
|
||
// 14字节是IC卡号
|
||
let icNo = parseInt(hexNum, 16)
|
||
data.number = icNo
|
||
data.cardType = 'ic'
|
||
data.action = 'enter'
|
||
}
|
||
if (hexStr.length == 22) {
|
||
// 11字节是ID卡号
|
||
let idNo = parseInt(hexNum, 16)
|
||
data.number = idNo
|
||
data.cardType = 'id'
|
||
data.action = 'enter'
|
||
}
|
||
if (hexStr.length == 14) {
|
||
// 7字节是卡离开
|
||
data.action = 'leave'
|
||
}
|
||
if (address == 0) {
|
||
// 人员卡
|
||
data.type = 'person'
|
||
}
|
||
if (address >= 1 && address <= 16) {
|
||
// 内镜卡
|
||
data.type = 'internal'
|
||
}
|
||
}
|
||
// 关门上报指令
|
||
// if (hexStr.toUpperCase() == '5AA50000000000FF') {
|
||
// // 开门数据 8个字节
|
||
// data.action = 'door'
|
||
// }
|
||
return data
|
||
},
|
||
parse485Data: (hexString) => {
|
||
// 移除空格和换行符
|
||
const hexStr = hexString.replace(/\s+/g, '');
|
||
// 压差站号:01,温湿度站号:02
|
||
let addHex = hexStr.substr(0, 2)
|
||
let data = {}
|
||
if (hexStr.length == 14 || hexStr.length == 18) {
|
||
if (addHex == '01') {
|
||
let pressure = parseInt(hexStr.substr(6, 4), 16);
|
||
data.pressure = pressure / 10
|
||
}
|
||
if (addHex == '02') {
|
||
let humi = parseInt(hexStr.substr(6, 4), 16);
|
||
let temp = parseInt(hexStr.substr(10, 4), 16);
|
||
// 温湿度上报数据异常处理,取正常温湿度范围内数据
|
||
temp = (temp / 10).toFixed(1)
|
||
if (temp >= 0 && temp < 100) {
|
||
data.temp = temp
|
||
}
|
||
humi = (humi / 10).toFixed(1)
|
||
if (humi >= 0 && humi < 100) {
|
||
data.humi = humi
|
||
}
|
||
}
|
||
|
||
if (addHex == '03') {
|
||
// 中盛4路数字IO控制器, 站号:03
|
||
// 左门开门: 03 06 00 01 00 01 18 28
|
||
// 左门关门: 03 06 00 01 00 00 D9 E8
|
||
// 03040200010130
|
||
// 右门开门: 03 06 00 00 00 01 49 E8
|
||
// 右门关门: 03 06 00 00 00 00 88 28
|
||
// 03040200024131
|
||
|
||
// 关门状态上报(功能码04,读输入寄存器):
|
||
|
||
// DI状态变化上报,解析左右门触点触发
|
||
|
||
let funcHex = hexStr.substr(8, 2);
|
||
if (funcHex == '01') {
|
||
// 左门关
|
||
data.action = 'LeftDoor'
|
||
data.status = 'closed'
|
||
}
|
||
if (funcHex == '02') {
|
||
// 右门关
|
||
data.action = 'RightDoor'
|
||
data.status = 'closed'
|
||
}
|
||
if (funcHex == '03') {
|
||
// 2个门关
|
||
data.action = 'door'
|
||
data.status = 'closed'
|
||
}
|
||
|
||
// console.log('parse485Data:',hexStr, data)
|
||
}
|
||
}
|
||
|
||
return data
|
||
},
|
||
} |