// 异或和校验 function xor_verify(data){ if (data.length < 2){ return false; } var xor_sum = xor(data, 1, data.length - 2); if (xor_sum == data[data.length - 1]){ return true; } return false; } // 获取异或和 function xor(data, start, end){ var xor_sum = data[start]; for (var i = start + 1; i < end + 1; i++){ xor_sum ^= data[i]; } return xor_sum; } // crc校验 function crc(bytes) { // CRC寄存器全为1 var CRC = 0x0000ffff; // 多项式校验值 var POLYNOMIAL = 0x0000a001; for (var i = 0; i < bytes.length; i++) { CRC ^= bytes[i] & 0x000000ff; for (var j = 0; j < 8; j++) { if (CRC & 0x00000001 != 0) { CRC >>= 1; CRC ^= POLYNOMIAL; } else { CRC >>= 1; } } } return CRC; } function crc_verify(data){ var verifyBytes = ConvertToUint8Array(data, 1, data.length - 2); var crcBytes = ConvertToUint8Array(data, data.length - 2, data.length); var thisCrc = crc(verifyBytes); var thatCrc = new DataView(crcBytes.buffer).getUint16(0, true); if (thisCrc == thatCrc){ return true; } return false; } // 字节数组 function ArrayToHexString(data,start,end){ start = start?start:0; end = end?end:data.length; var Hex = ''; for (var i =start; i < end; i++) { var s = data[i].toString(16); if (s.length == 1) { s = "0"+s; } Hex += s; } return Hex.toUpperCase(); } function ConvertToUint8Array(rawData, start, end) { start = start?start:0; end = end?end:rawData.length; var uint8Array = new Uint8Array(end - start); for (var i = start; i < end; i++) { uint8Array[i-start] = rawData[i] & 0xff; } return uint8Array; } // 合并字节数组 function concat(array1, array2) { var uint8Array = new Uint8Array(array1.length + array2.length); for (var i = 0; i < array1.length; i++) { uint8Array[i] = array1[i] & 0xff; } for (var j = 0; j < array2.length; j++) { uint8Array[j + array1.length] = array2[j] & 0xff; } return uint8Array; } // 解析报警数据 function analysis_old_alarm(dataArray, obj) { obj.dataType = 5; // 报警数据 var dataView = new DataView(dataArray.buffer, 0); // 类型【1】 var type = dataView.getUint8(1); if (type == 0x15){ obj.pumpType = 1; }else if (type == 0x25){ obj.pumpType = 2; }else if (type == 0x35){ obj.pumpType = 3; }else { obj.pumpType = 0; obj.msg = '泵类型不存在'; return; } // 泵号【2-9】 obj.pumpCode = ArrayToHexString(dataArray, 2, 10); // 住院号【10-13】 if (obj.pumpType == 1 && dataArray.length == 36){ obj.patientCodeArray = concat(ConvertToUint8Array(dataArray, 10, 14), ConvertToUint8Array(dataArray, 32, 34)); } else if (obj.pumpType == 2 && dataArray.length == 39){ obj.patientCodeArray = concat(ConvertToUint8Array(dataArray, 10, 14), ConvertToUint8Array(dataArray, 35, 37)); } else { obj.patientCodeArray = ConvertToUint8Array(dataArray, 10, 14); } var patientCodeDataView = new DataView(obj.patientCodeArray.buffer); var patientCode = patientCodeDataView.getUint32(0, true); if (patientCodeDataView.byteLength == 6){ patientCode += patientCodeDataView.getUint16(4, true) * 1000000000; } obj.patientCode = patientCode + ''; // 病区【14-15】 obj.ward = dataView.getUint16(14, true); // 床号【16-17】 obj.bedNo = dataView.getUint16(16, true); // 持续量【18-19】 obj.continueDose = dataView.getUint16(18, true) * 0.1; // 锁定时间【20-21】 obj.lockTime = dataView.getUint16(20, true); // 极限量【22-23】 obj.ultimateDose = dataView.getUint16(22, true); // 首次量【24】 obj.firstDose = dataView.getUint8(24); // 追加量【25-26】 obj.appendDose = dataView.getUint16(25, true) * 0.1; // 报警【29】 obj.alarm = []; obj.forcast = []; var alarmByte = dataView.getUint8(29); // 未装药盒 9 if ((alarmByte & 0x03) == 0x03){ obj.alarm.push(9); } else if ((alarmByte & 0x01) == 0x01){ // 气泡或无液 1 obj.alarm.push(1); } else if ((alarmByte & 0x02) == 0x02){ // 堵塞 2 obj.alarm.push(2); } // 总量 3 if ((alarmByte & 0x04) == 0x04){ obj.alarm.push(3); } // 极限量 4 if ((alarmByte & 0x08) == 0x08){ obj.alarm.push(4); } // 输液将结束 1 if ((alarmByte & 0x10) == 0x10){ obj.forcast.push(1); } // 输液结束 6 if ((alarmByte & 0x20) == 0x20){ obj.alarm.push(6); } // 电量偏低预警 5 if ((alarmByte & 0x40) == 0x40){ obj.alarm.push(5); } // 电量偏低预报 3 if ((alarmByte & 0x80) == 0x80){ obj.forcast.push(3); } var alarmByte30 = dataView.getUint8(30); // 点击失控 7 if ((alarmByte30 & 0x0f) == 0x01){ obj.alarm.push(7); } else if ((alarmByte30 & 0x0f) == 0x02){ // 机器故障 8 obj.alarm.push(8); } // 无报警 if (obj.alarm.length == 0){ obj.alarm.push(0); } // 无预报 if (obj.forcast.length == 0){ obj.forcast.push(0); } // 运行状态【30】 obj.runStatus = (dataArray[30] & 0xf0) >> 4; // 电池电量【31】 obj.electricity = dataView.getUint8(31); // 脉冲泵的属性 if (obj.pumpType == 2){ // 首次量锁时【32】 obj.firstLockTime = dataView.getUint8(32); // 脉冲量【33】 obj.pulseDose = dataView.getUint8(33); // 脉冲量锁时【34】 obj.pulseLockTime = dataView.getUint8(34); } else if (obj.pumpType == 3){ // 智能泵的属性 // 加档时间【32】 obj.filingCycle = dataView.getUint8(32) * 0.1; // 减档时间【33】 obj.reductionPeriod = dataView.getUint8(33) * 0.1; // 加档PCA有效计数【34】 obj.addTrueFrequency = dataView.getUint8(34); // 自调比例【35】 obj.customScate = dataView.getUint8(35); // 加档上限【36】 var data36 = dataView.getUint8(36); if (data36 > 100){ obj.upperLimit = (data36 - 128) * 1.0; }else { obj.upperLimit = data36 * 0.1; } // 减档下限【37】 obj.lowerLimit = dataView.getUint8(37) * 0.1; } } // 解析运行数据 function analysis_old_run(dataArray, obj) { obj.dataType = 3; // 报警数据 var dataView = new DataView(dataArray.buffer, 0); // 类型【1】 var type = dataView.getUint8(1); if (type == 0x13){ obj.pumpType = 1; }else if (type == 0x23){ obj.pumpType = 2; }else if (type == 0x33){ obj.pumpType = 3; }else { obj.pumpType = 0; obj.msg = '泵类型不存在'; return; } // 住院号【2-5】 if (obj.pumpType == 1 && dataArray.length == 18){ obj.patientCodeArray = concat(ConvertToUint8Array(dataArray, 2, 6), ConvertToUint8Array(dataArray, 14, 16)); } else if (obj.pumpType == 2 && dataArray.length == 18){ obj.patientCodeArray = concat(ConvertToUint8Array(dataArray, 2, 6), ConvertToUint8Array(dataArray, 14, 16)); } else { obj.patientCodeArray = ConvertToUint8Array(dataArray, 2, 6); } var patientCodeDataView = new DataView(obj.patientCodeArray.buffer); var patientCode = patientCodeDataView.getUint32(0, true); if (patientCodeDataView.byteLength == 6){ patientCode += patientCodeDataView.getUint16(4, true) * 1000000000; } obj.patientCode = patientCode + ''; // 总量。【6-7】 obj.totalDose = dataView.getUint16(6, true); // 已输入量【8-9】 obj.finishDose = dataView.getUint16(8, true) * 0.1; // 电池状态【10】 obj.electricity = dataView.getUint8(10); // 有效次数【11】 obj.validTimes = dataView.getUint8(11); // 无效次数【12】 obj.invalidTimes = dataView.getUint8(12); // 预报【13】和报警 obj.forcast = []; obj.alarm = []; var warnByte = dataView.getUint8(13); // 电量偏低预报 3 if ((warnByte & 0x04) == 0x04){ obj.forcast.push(3); } // 无预报 if (obj.forcast.length == 0){ obj.forcast.push(0); } // 无报警 if (obj.alarm.length == 0){ obj.alarm.push(0); } // 运行状态 obj.runStatus = 2; } // 解析老协议 function analysis_old(dataArray, obj) { // crc校验 if (!crc_verify(dataArray)){ obj.msg = '老协议数据CRC校验失败'; return ; } // 泵数据 var type = dataArray[1] & 0x0f; if (type == 5){ analysis_old_alarm(dataArray, obj); }else if (type == 3){ analysis_old_run(dataArray, obj); }else { obj.msg = '数据格式错误'; } } // 解析新协议 function analysis_new(dataArray, obj) { // crc校验 if (!crc_verify(dataArray)){ obj.msg = '新协议数据CRC校验失败'; return ; } // 泵数据 var dataView = new DataView(dataArray.buffer, 0); // 泵类型【2】 高4位 obj.pumpType = (dataArray[2] & 0xf0) >> 4; // 版本【2】 低4位 obj.version = dataArray[2] & 0x0f; // 泵号【3-10】 obj.pumpCode = ArrayToHexString(dataArray, 3, 11); // 输注标识【11】 obj.infusionId = dataView.getUint8(11); // 发送条数【12】 obj.dataNumber = dataView.getUint8(12); // 医院编号【13-14】 obj.userId = dataView.getUint16(13, true); // 住院号【15-20】 var patientCodeArray = ConvertToUint8Array(dataArray, 15, 21); var patientCodeDataView = new DataView(patientCodeArray.buffer); var patientCode = patientCodeDataView.getUint32(0, true) + patientCodeDataView.getUint16(4, true) * 4294967295; obj.patientCode = patientCode + ''; obj.patientCodeArray = patientCodeArray; // 持续量【21-22】 obj.continueDose = dataView.getUint16(21, true) * 0.1; // 锁定时间【23】 obj.lockTime = dataView.getUint8(23); // 极限量【24】 obj.ultimateDose = dataView.getUint8(24); // 首次量【25】 obj.firstDose = dataView.getUint8(25); // 追加量【26】 obj.appendDose = dataView.getUint8(26) * 0.1; // 总量【27-28】 obj.totalDose = dataView.getUint16(27, true); // 已输入量【29-30】 obj.finishDose = dataView.getUint16(29, true) * 0.1; // 有效次数【31】 obj.validTimes = dataView.getUint8(31); // 无效次数【32】 obj.invalidTimes = dataView.getUint8(32); // 报警【33】 obj.alarm = []; obj.forcast = []; var alarmByte = dataView.getUint8(33); // 气泡或无液 1 if ((alarmByte & 0x01) == 0x01){ obj.alarm.push(1); } // 堵塞 2 if ((alarmByte & 0x02) == 0x02){ obj.alarm.push(2); } // 未装药盒 9 if ((alarmByte & 0x04) == 0x04){ obj.alarm.push(9); } // 极限量 4 if ((alarmByte & 0x08) == 0x08){ obj.alarm.push(4); } // 总量 3 if ((alarmByte & 0x10) == 0x10){ obj.alarm.push(3); } // 输液将结束 1 if ((alarmByte & 0x20) == 0x20){ obj.forcast.push(1); } // 输液结束 6 if ((alarmByte & 0x40) == 0x40){ obj.alarm.push(6); } // 机器故障 8 if ((alarmByte & 0x80) == 0x80){ obj.alarm.push(8); } // 预报 [34] var warnByte = dataView.getUint8(34); // 电量偏低预警 5 if ((warnByte & 0x01) == 0x01){ obj.alarm.push(5); } // 电量偏低预报 3 if ((warnByte & 0x02) == 0x02){ obj.forcast.push(3); } // 遗忘报警 4 if ((warnByte & 0x04) == 0x04){ obj.forcast.push(4); } // 无报警 if (obj.alarm.length == 0){ obj.alarm.push(0); } // 无预报 if (obj.forcast.length == 0){ obj.forcast.push(0); } // 运行状态【35】 obj.runStatus = (dataArray[35] & 0xf0) >> 4; // 电池电量【36】 obj.electricity = dataView.getUint8(36); // CRC【37-38】不处理 } // 获取返回的字节 function responseData(dest, patientCodeArray){ var length = dest.length + patientCodeArray.length + 7; var responseBytes = new Uint8Array(length); // 帧头 responseBytes[0] = 0xFE; // 长度域 responseBytes[1] = (dest.length + patientCodeArray.length + 2) & 0xff; // 命令域 responseBytes[2] = 0x24; responseBytes[3] = 0x5f; // 目的地址 responseBytes[4] = dest[0]; responseBytes[5] = dest[1]; // 固定位。0xAD 0xFC responseBytes[6] = 0xad; responseBytes[7] = 0xfc; // 数据。住院号 for (var i = 0; i < patientCodeArray.length; i++){ responseBytes[8 + i] = patientCodeArray[i]; } // 异或和 var xor_value = xor(responseBytes, 1, length - 1); responseBytes[length - 1] = xor_value & 0xff; return responseBytes; } // 四信网关 function fourfaithHandle(rawData, obj) { var rawDataArray = ConvertToUint8Array(rawData); // obj.rawDataArray = ArrayToHexString(rawDataArray); // obj.rawDataArrayLength = rawDataArray.length if (rawDataArray.length == 1 && rawDataArray[0] == 0xFE){ obj.msg = '网关心跳'; return ; } if (rawDataArray[0] == 0x78 && rawDataArray[1] == 0x56){ obj.msg = '网关连接'; return ; } // 校验 if (!xor_verify(rawDataArray)){ throw 'API数据异或和校验失败'; } // 获取api协议中的数据 var dataArray = ConvertToUint8Array(rawData, 6, rawData.length - 1); // obj.dataArray = ArrayToHexString(dataArray); // obj.dataArrayLength = dataArray.length; // 判断新老协议 if (dataArray[0] == 0xEF){ analysis_new(dataArray, obj); }else { analysis_old(dataArray, obj); } // 获取api协议中的目的地址 var addressArray = ConvertToUint8Array(rawData, 4, 6); if (obj.patientCodeArray){ obj.responseHex = ArrayToHexString(responseData(addressArray, obj.patientCodeArray)); delete obj.patientCodeArray } } // 判断数组中是否包含某个元素 function includes(array, item) { for (var i = 0; i < array.length; i++){ if (array[i] == item){ return true; } } return false; } function updateItems(device, analysisResult) { var oldPatientCode = device.patientCode; var newPatientCode = analysisResult.patientCode; var date = new Date(); // 设置输注标识 if (analysisResult.hasOwnProperty('infusionId')){ } else if (!device.items.hasOwnProperty("infusionId")){ device.items.infusionId = 1; device.items.dataNumber = 1; } else if (oldPatientCode != newPatientCode){ var infusionId = device.items.infusionId; device.items.infusionId = infusionId + 1; } else { var dataNumber = device.items.dataNumber; device.items.dataNumber = dataNumber + 1; } // 更新items for (var key in analysisResult) { if (analysisResult.hasOwnProperty(key)) { device.items[key] = analysisResult[key]; } } // 更新models for (var key in device.items) { if (device.items.hasOwnProperty(key)) { device.models[key] = { time: date.getTime(), value: device.items[key], }; } } device.patientCode = newPatientCode; device.updateTime = date; } /* * 粘包处理 */ function stick(rawData){ rawData = ConvertToUint8Array(rawData); var obj = { valid: '', surplus: '' }; var start = -1; for(var i = 0; i < rawData.length; i++){ if(rawData[i] == 0xFE && rawData.length > 1){ start = i; break; } } if(start == -1){ obj.valid = ArrayToHexString(ConvertToUint8Array(rawData)); }else if (start > 0) { obj.valid = ArrayToHexString(ConvertToUint8Array(rawData, 0, start)); obj.surplus = ArrayToHexString(ConvertToUint8Array(rawData, start, rawData.length)); }else if(rawData[1] + 5 == rawData.length){ obj.valid = ArrayToHexString(ConvertToUint8Array(rawData, 0, rawData[1] + 5)); }else if(rawData[1] + 5 < rawData.length){ obj.valid = ArrayToHexString(ConvertToUint8Array(rawData, 0, rawData[1] + 5)); obj.surplus = ArrayToHexString(ConvertToUint8Array(rawData, rawData[1] + 5, rawData.length)); }else if(rawData[1] + 5 > rawData.length){ obj.surplus = ArrayToHexString(ConvertToUint8Array(rawData, 0, rawData.length)); } var json = JSON.stringify(obj); return json; } /* * 从tcp发来的数据 * 第一步:解析。 * 解析tcp数据 */ function fromTcp(rawData){ var obj = {}; fourfaithHandle(rawData, obj) obj.productId = '1dbfd476b7nm2'; var json = JSON.stringify(obj); return json; } /* * 从数据下发系统发来的数据 * 第一步:解析。 * 解析mqtt数据 */ function fromMqtt(rawData) { var items = JSON.parse(rawData); var obj = items.items; obj.productId = '1dbfd476b7nm2'; obj.pumpCode = items.mac; var json = JSON.stringify(obj); return json; } /* * 整合数据 * 第二步:整合。 * 根据解析获取到设备列表。选择一个设备作为返回,并整合设备的参数。 */ function zhenghe(deviceList, analysisResult) { deviceList = JSON.parse(deviceList); analysisResult = JSON.parse(analysisResult); // 设备 var device; // 设备不存在 if(deviceList.length > 0){ device = deviceList[0]; }else if(analysisResult.hasOwnProperty('pumpCode')){ device = { id: analysisResult.pumpCode, }; device.items = {}; device.models = {}; device.createTime = new Date(); }else { throw 'pumpCode不存在'; } device.productId = analysisResult.productId; // 更新属性 updateItems(device, analysisResult); var json = JSON.stringify(device); return json; } /* * 向web端发送的数据 * 第三步:发送数据。 * 发送到web端 */ function toWeb(rawData){ var device = JSON.parse(rawData); var items = device.items; // web端对象 var obj = { deviceId: items.pumpCode, // 设备编号 classification: items.infusionId, // 输注编号 dataNumber: items.dataNumber, // 数据编号 type: items.pumpType, // 设备类型 runState: items.runStatus, // 运行状态 appendDose: items.appendDose, // 追加量 continueDose: items.continueDose, // 持续量 electricQuantity: items.electricity, // 电量 firstDose: items.firstDose, // 首次量 inputDose: items.finishDose, // 已输入量 maxDose: items.ultimateDose, // 极限量 patientCode: items.patientCode, // 住院号 pcaValidCount: items.validTimes, // 有效次数 pcaInvalidCount: items.invalidTimes, // 无效次数 totalDose: items.totalDose, // 总量 userId: items.userId, // 医院编号 selfControlLockTime: items.lockTime, // 锁时 alarm: 0, // 报警 }; var alarms = items.alarm; // 气泡无液 if (includes(alarms, 1)){ obj.alarm = 1; } // 堵塞 if (includes(alarms, 2)){ obj.alarm = 2; } // 未装药盒 if (includes(alarms, 9)){ obj.alarm = 3; } // 输注总量报警 if (includes(alarms, 3)){ obj.alarm = 4; } // 极限报警 if (includes(alarms, 4)){ obj.alarm = 5; } // 输液结束 if (includes(alarms, 6)){ obj.alarm = 6; } // 电量耗尽 if (includes(alarms, 5)){ obj.alarm = 7; } // 机械故障 if (includes(alarms, 8)){ obj.alarm = 9; } // 预报 var forcasts = items.forcast; // 0:无预报 // 1:输液将结束 if (includes(forcasts, 1)){ } // 2:镇痛不足 if (includes(forcasts, 2)){ } // 3:电量偏低 if (includes(forcasts, 3)){ } // 4:遗忘; if (includes(forcasts, 4)){ } // 输出json字符串 var json = JSON.stringify(obj); return json; } /* * 向PC端数据库发送的数据 * 第三步:发送数据。 * 存储到上位机数据库 */ function toPc(rawData){ var device = JSON.parse(rawData); var items = device.items; var obj = { sysProductId: items.pumpType, // 泵类型 sysHospitalId: items.userId, // 医院编号 pumpCode: items.pumpCode, // 泵号 patientCode: items.patientCode, // 住院号 ward: items.ward, // 病区 bedNo: items.bedNo, // 床号 continueQuantity: items.continueDose, // 持续量 lockTime: items.lockTime, // 锁时时间 maxQuantity: items.ultimateDose, // 极限量 firstQuantity: items.firstDose, // 首次量 singleQuantity: items.appendDose, // 追加量 runState: items.runStatus, // 运行状态 bufState6: items.electricity, // 电量 // user1: items.infusionId, // 输注编号 // number2: items.dataNumber, // 数据编号 allQuantity: items.totalDose, // 总量 inputQuantity: items.finishDose, // 已输入量 trueNum: items.validTimes, // 有效次数 falseNum: items.invalidTimes, // 无效次数 // 报警 bufState1: 0, bufState2: 0, bufState3: 0, bufState4: 0, bufState5: 0, bufState7: 0, bufState8: 0, viewState: '', // 脉冲泵 firstLockTime: items.firstLockTime, // 首次量锁时 pulseQuantity: items.pulseDose, // 脉冲量 pulseLockTime: items.pulseLockTime, // 脉冲量锁时 }; // 判断运行状态 if (obj.runState == 0){ obj.viewState = '关机'; obj.stateFlag = 2; }else if (obj.runState == 1){ obj.viewState = '开机'; obj.stateFlag = 1; }else if (obj.runState == 2){ obj.viewState = '运行'; obj.stateFlag = 3; }else if (obj.runState == 3){ obj.viewState = '暂停'; obj.stateFlag = 1; }else if (obj.runState == 4){ obj.viewState = '待机'; obj.stateFlag = 4; } // 报警和预报 var alarms = items.alarm; // 气泡无液 if (includes(alarms, 1)){ obj.bufState1 = 1; obj.viewState = '气泡无液'; obj.stateFlag = 4; } // 堵塞 if (includes(alarms, 2)){ obj.bufState1 = 2; obj.viewState = '堵塞'; obj.stateFlag = 4; } // 未装药盒 if (includes(alarms, 9)){ obj.bufState1 = 3; obj.viewState = '未装药盒'; obj.stateFlag = 4; } // 输注总量报警 if (includes(alarms, 3)){ obj.bufState2 = 1; obj.viewState = '输入总量报警'; obj.stateFlag = 4; } // 极限报警 if (includes(alarms, 4)){ obj.bufState3 = 1; obj.viewState = '极限'; obj.stateFlag = 4; } // 输液结束 if (includes(alarms, 6)){ obj.bufState3 = 3; obj.viewState = '输液结束'; obj.stateFlag = 4; } // 电量耗尽 if (includes(alarms, 5)){ obj.bufState5 = 1; obj.viewState = '电量耗尽'; obj.stateFlag = 4; } // 电机失控 if (includes(alarms, 7)){ obj.bufState7 = 1; obj.viewState = '电机失控'; obj.stateFlag = 4; } // 机械故障 if (includes(alarms, 8)){ obj.bufState8 = 1; obj.viewState = '机械故障'; obj.stateFlag = 4; } // 预报 var forcasts = items.forcast; // 0:无预报 // 1:输液将结束 if (includes(forcasts, 1)){ obj.bufState3 = 2; obj.viewState = '输液将结束'; obj.stateFlag = 4; } // 2:镇痛不足 if (includes(forcasts, 2)){ obj.bufState4 = 1; } // 3:电量偏低 if (includes(forcasts, 3)){ obj.bufState5 = 2; } // 4:遗忘; if (includes(forcasts, 4)){ } // 判断需要转换的java类型 if (items.dataType == 5){ obj.javaObject = 'AlarmInfo'; }else if (items.dataType == 3){ obj.javaObject = 'RunParameter'; }else { obj.javaObject = 'Pump'; } // 输出json字符串 var json = JSON.stringify(obj); return json; }