modbus.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. // modbus.js - 优化版本
  2. let _globalSlaveAddress = 0x01; //全局初始从机地址
  3. let connected = false; // 连接状态
  4. let sharedHeartbeatInterval = null; // 心跳定时器变量
  5. let agreement = 'Lora'; //协议类型 默认为 Lora
  6. let taskInterval = 500; // 定时任务频率
  7. let timeStatus = true;
  8. import store from '@/store';
  9. import { MODBUS_FRAME_CONFIG,MODBUS_TYPES } from '@/utils/modbusConfig.js';
  10. // 先声明变量
  11. let ecUI, ecBLE;
  12. // #ifdef APP
  13. import _ecUI from '@/utils/ecUI.js'
  14. import _ecBLE from '@/utils/ecBLE/ecBLE.js'
  15. ecUI = _ecUI;
  16. ecBLE = _ecBLE;
  17. // #endif
  18. // #ifdef MP
  19. const _ecUI = require('@/utils/ecUI.js')
  20. const _ecBLE = require('@/utils/ecBLE/ecBLE.js')
  21. ecUI = _ecUI;
  22. ecBLE = _ecBLE;
  23. // #endif
  24. // 将 ecBLE 导出,供其他页面使用
  25. export { ecBLE, ecUI };
  26. // 获取连接状态
  27. export function getConnected() {
  28. return connected;
  29. }
  30. //设置协议类型
  31. export function setAgreement(type) {
  32. agreement = type;
  33. }
  34. export function getAgreement() {
  35. return agreement;
  36. }
  37. export function setTime(value) {
  38. if (value === taskInterval && isHeartbeatRunning()){
  39. return;
  40. }
  41. taskInterval = value;
  42. stopHeartbeat();
  43. startHeartbeat();
  44. }
  45. export function setTimeStatus(value) {
  46. timeStatus = value;
  47. }
  48. export function initBLE() {
  49. // 监听连接状态变化
  50. ecBLE.onBLEConnectionStateChange((res) => {
  51. console.log(res);
  52. if (res.ok && !connected) {
  53. connected = true;
  54. console.log("连接成功");
  55. store.dispatch('ble/updateConnected', res.ok)
  56. ecBLE.stopBluetoothDevicesDiscovery();
  57. } else {
  58. store.dispatch('ble/updateConnected', false)
  59. connected = false;
  60. ecUI.hideLoading();
  61. this.$modal.showToast("请检查是否配置成功");
  62. }
  63. });
  64. // 接收数据
  65. ecBLE.onBLECharacteristicValueChange((str, strHex) => {
  66. try {
  67. console.log("数据来了");
  68. let data = strHex.replace(/[0-9a-fA-F]{2}/g, ' $&') ;
  69. const parsedData = readRegister(data)
  70. if (parsedData.device === '255' || parsedData.device === 'FF' || parsedData.device === 255){
  71. let b = isHeartbeatRunning();
  72. if (!b){
  73. if (parsedData.Addres_23 !== _globalSlaveAddress){
  74. _globalSlaveAddress = parsedData.Addres_23;
  75. }
  76. startHeartbeat();
  77. }
  78. }
  79. store.dispatch('ble/updateData', parsedData)
  80. } catch (error) {
  81. store.dispatch('ble/updateError', error)
  82. console.error('数据解析失败:', error);
  83. this.$modal.showToast("数据解析失败");
  84. }
  85. });
  86. }
  87. // Getter/Setter 管理全局从机地址
  88. export function setGlobalSlaveAddress(addr) {
  89. if (addr < 0 || addr > 247) {
  90. throw new RangeError('slaveAddress 必须在 0~247 之间');
  91. }
  92. _globalSlaveAddress = addr;
  93. }
  94. export function getGlobalSlaveAddress() {
  95. return _globalSlaveAddress;
  96. }
  97. /**
  98. * 写入操作
  99. */
  100. const TS = ['READ_TIME','READ_LIMIT','READ_INCLINATION','READ_FREQUENCY','READ_TEMPERATURE'];
  101. export function writeRegister(action, valueToWrite) {
  102. try{
  103. if (action !='TIMED_TASKS'){
  104. console.log("停止心跳--------");
  105. stopHeartbeat();
  106. }
  107. let value = valueToWrite;
  108. // 特殊处理 WRITE_ADDRESS_OLD,避免将数组转换为整数
  109. if (!TS.includes(action) && valueToWrite !== null && valueToWrite !== '' && valueToWrite !== undefined && action !== "WRITE_ADDRESS_OLD") {
  110. value = parseInt(valueToWrite, 10);
  111. }
  112. if (action =='WRITE_ADDRESS'){
  113. value = parseInt(valueToWrite, 10);
  114. setGlobalSlaveAddress(value);
  115. }
  116. const buffer = generateModbusFrame(agreement, action, value);
  117. const arrayBuffer = arrayBufferToHex(buffer);
  118. const writeBLECharacteristicValue = ecBLE.writeBLECharacteristicValue(arrayBuffer, true);
  119. return writeBLECharacteristicValue;
  120. }catch(exception){
  121. console.log("写入失败:", exception);
  122. }finally {
  123. if (action =='WRITE_ADDRESS'){
  124. setGlobalSlaveAddress(value);
  125. }else if (action !='TIMED_TASKS' && action !='GET_ADDRESS' && timeStatus){
  126. setTimeout(() => {
  127. if (getConnected() && timeStatus) {
  128. startHeartbeat();
  129. }
  130. }, 100);
  131. }
  132. }
  133. }
  134. /**
  135. * 读取寄存器
  136. */
  137. export function readRegister(buffer) {
  138. return parseBluetoothData(buffer);
  139. }
  140. //心跳函数
  141. export function heartbeat() {
  142. try {
  143. // 示例:发送一个读取寄存器的请求作为心跳
  144. writeRegister('TIMED_TASKS',null);
  145. } catch (error) {
  146. console.error('心跳请求失败:', error);
  147. // 可以在这里添加重连逻辑
  148. }
  149. }
  150. // 启动心跳定时器
  151. export function startHeartbeat() {
  152. if (!getConnected()) {
  153. return;
  154. }
  155. if (!isHeartbeatRunning()) {
  156. sharedHeartbeatInterval = setInterval(heartbeat, taskInterval);
  157. }
  158. }
  159. // 停止心跳定时器
  160. export function stopHeartbeat() {
  161. if (isHeartbeatRunning()) {
  162. clearInterval(sharedHeartbeatInterval);
  163. sharedHeartbeatInterval = null;
  164. }
  165. }
  166. // 检查心跳定时器是否正在运行
  167. export function isHeartbeatRunning() {
  168. return sharedHeartbeatInterval !== null;
  169. }
  170. /**
  171. * 参数校验函数
  172. */
  173. function validateParams({ slaveAddress, functionCode, startAddress, valueToWrite }) {
  174. if (slaveAddress < 0 || slaveAddress > 247) {
  175. throw new RangeError('slaveAddress 必须在 0~247 之间');
  176. }
  177. if (functionCode < 1 || functionCode > 255) {
  178. throw new RangeError('functionCode 必须在 1~255 之间');
  179. }
  180. if (startAddress < 0 || startAddress > 0xFFFF) {
  181. throw new RangeError('startAddress 必须在 0~65535 之间');
  182. }
  183. if (valueToWrite < 0 || valueToWrite > 0xFFFF) {
  184. throw new RangeError('valueToWrite 必须在 0~65535 之间');
  185. }
  186. }
  187. /**
  188. * 创建 Modbus RTU 请求帧(根据配置)
  189. * @param {string} protocol 设备协议类型(DEVICE_A / DEVICE_B / DEVICE_C)
  190. * @param {string} action 操作
  191. * @param {number} [valueToWrite=0] 写入值 (0~65535)
  192. * @returns {Buffer} Modbus RTU 请求帧
  193. */
  194. function generateModbusFrame(protocol, action, valueToWrite) {
  195. console.log(_globalSlaveAddress);
  196. const config = MODBUS_FRAME_CONFIG[protocol]?.[action];
  197. if (!config) {
  198. throw new Error(`不支持的协议或写入类型: ${protocol} - ${action}`);
  199. }
  200. let { type, slaveAddress, functionCode, startAddress, value } = config;
  201. if ((valueToWrite == NaN || valueToWrite == null) && config.value !== undefined && config.value !== null) {
  202. valueToWrite = config.value;
  203. }
  204. if (action !== 'WRITE_ADDRESS' && action !== "GET_ADDRESS" && action !== "WRITE_ADDRESS_OLD"){
  205. slaveAddress = _globalSlaveAddress;
  206. }
  207. let buffer;
  208. if (type === MODBUS_TYPES.READ_REGISTER) {
  209. // valueToWrite 应该包含日期数据
  210. const dateData = valueToWrite; // 应该是一个数组,包含年、月、日、时、分、秒
  211. let buffer1 = new Uint8Array(7 + dateData.length * 2); // 基础7字节 + 数据字节
  212. buffer1[0] = slaveAddress;
  213. buffer1[1] = functionCode;
  214. buffer1[2] = (startAddress >> 8) & 0xFF;
  215. buffer1[3] = startAddress & 0xFF;
  216. buffer1[4] = (dateData.length >> 8) & 0xFF; // 寄存器数量高字节
  217. buffer1[5] = dateData.length & 0xFF; // 寄存器数量低字节
  218. buffer1[6] = dateData.length * 2; // 字节数
  219. // 填充数据
  220. for (let i = 0; i < dateData.length; i++) {
  221. buffer1[7 + i*2] = (dateData[i] >> 8) & 0xFF; // 高字节
  222. buffer1[8 + i*2] = dateData[i] & 0xFF; // 低字节
  223. }
  224. const crc = calculateCRC(buffer1.subarray(0, 7 + dateData.length * 2));
  225. const finalBuffer = new Uint8Array(buffer1.length + 2);
  226. finalBuffer.set(buffer1);
  227. finalBuffer[buffer1.length] = crc[0];
  228. finalBuffer[buffer1.length + 1] = crc[1];
  229. buffer = finalBuffer;
  230. } else {
  231. // 特殊处理 WRITE_ADDRESS_OLD 指令 - 不添加CRC
  232. if (action === "WRITE_ADDRESS_OLD" && Array.isArray(valueToWrite)) {
  233. // 为 WRITE_ADDRESS_OLD 创建特定的帧格式,不带CRC
  234. buffer = new Uint8Array(4 + valueToWrite.length * 2); // 4字节头 + 数据
  235. buffer[0] = slaveAddress;
  236. buffer[1] = functionCode;
  237. buffer[2] = (startAddress >> 8) & 0xFF;
  238. buffer[3] = startAddress & 0xFF;
  239. // 填充数据 (两个16位寄存器值)
  240. for (let i = 0; i < valueToWrite.length; i++) {
  241. const offset = 4 + (i * 2);
  242. buffer[offset] = (valueToWrite[i] >> 8) & 0xFF;
  243. buffer[offset + 1] = valueToWrite[i] & 0xFF;
  244. }
  245. } else {
  246. // 原有逻辑保持不变
  247. buffer = new Uint8Array(6);
  248. buffer[0] = slaveAddress;
  249. buffer[1] = functionCode;
  250. buffer[2] = (startAddress >> 8) & 0xFF;
  251. buffer[3] = startAddress & 0xFF;
  252. buffer[4] = (valueToWrite >> 8) & 0xFF;
  253. buffer[5] = valueToWrite & 0xFF;
  254. const crc = calculateCRC(buffer);
  255. const finalBuffer = new Uint8Array(buffer.length + crc.length);
  256. finalBuffer.set(buffer);
  257. finalBuffer.set(crc, buffer.length);
  258. buffer = finalBuffer;
  259. }
  260. }
  261. return buffer;
  262. }
  263. function calculateCRC(buffer) {
  264. let crc = 0xFFFF;
  265. for (let i = 0; i < buffer.length; i++) {
  266. const tableIndex = (crc ^ buffer[i]) & 0xFF;
  267. crc = (crc >> 8) ^ crcTable[tableIndex];
  268. }
  269. const crcBuffer = new Uint8Array(2);
  270. crcBuffer[0] = crc & 0xFF;
  271. crcBuffer[1] = (crc >> 8) & 0xFF;
  272. return crcBuffer;
  273. }
  274. // CRC 表只构建一次
  275. const crcTable = buildCRCTable();
  276. function buildCRCTable() {
  277. const table = new Uint16Array(256);
  278. for (let i = 0; i < 256; i++) {
  279. let crc = i;
  280. for (let j = 0; j < 8; j++) {
  281. if (crc & 0x0001) {
  282. crc = (crc >> 1) ^ 0xA001;
  283. } else {
  284. crc >>= 1;
  285. }
  286. }
  287. table[i] = crc;
  288. }
  289. return table;
  290. }
  291. /**
  292. * 将 ArrayBuffer 转为十六进制字符串
  293. */
  294. export function arrayBufferToHex(buffer, withSpaces = false) {
  295. const hexArray = [...new Uint8Array(buffer)]
  296. .map(b => b.toString(16).padStart(2, '0'));
  297. if (withSpaces) {
  298. return hexArray.join(' ').toUpperCase();
  299. } else {
  300. return hexArray.join('').toUpperCase();
  301. }
  302. }
  303. const FAULT_CODE_MAP = {
  304. '0': '无故障',
  305. '1': '角度保护',
  306. '2': '角度矫正失败',
  307. '3': '过流保护',
  308. '4': '通讯保护',
  309. '5': '限位保护'
  310. };
  311. /**
  312. * 解析蓝牙数据
  313. */
  314. export function parseBluetoothData(hexString) {
  315. ecBLE.saveWriteDataToLocal("TX: " + hexString,"tx");
  316. if (!hexString || hexString.length < 6) {
  317. const error = new Error('蓝牙数据不完整');
  318. throw error;
  319. }
  320. // 移除所有空格并重新格式化为标准格式
  321. const cleanHexString = hexString.replace(/\s/g, '');
  322. if (cleanHexString.length < 6) {
  323. throw new Error('蓝牙数据不完整');
  324. }
  325. // 根据协议类型选择不同的解析方式
  326. if (agreement === 'Lora') {
  327. return parseLoraData(cleanHexString);
  328. } else if (agreement === 'zigbee') {
  329. console.log("zigbee协议")
  330. return parseZigbeeData(cleanHexString);
  331. }
  332. }
  333. function parseLoraData(cleanHexString) {
  334. // 将连续的十六进制字符串转换为带空格的格式
  335. const register = createEmptyRegister();
  336. const formattedHexString = cleanHexString.match(/.{1,2}/g)?.join(' ') || cleanHexString;
  337. const byteStrings = formattedHexString.split(' ').filter(s => s.length > 0);
  338. console.log("字节字符串数组:", JSON.stringify(byteStrings));
  339. console.log("字节数组长度:", byteStrings.length);
  340. if (byteStrings.length < 10 ){ //其他操作执行成功
  341. //就地址23 42 02 23 23 0C 96
  342. register.oldAddressValue = parseInt(byteStrings[0], 16);
  343. // 其他操作执行成功
  344. return register;
  345. }
  346. if (!byteStrings || byteStrings.length < 3) {
  347. throw new Error('蓝牙数据不完整');
  348. }
  349. register.device = parseInt(byteStrings[0], 16);
  350. register.function = parseInt(byteStrings[1], 16);
  351. register.registerNumber = parseInt(byteStrings[2], 16);
  352. const formattedOutput = [];
  353. for (let i = 3; i < byteStrings.length; i += 2) {
  354. const byte1 = parseInt(byteStrings[i], 16);
  355. const byte2 = i + 1 < byteStrings.length ? parseInt(byteStrings[i + 1], 16) : 0;
  356. const combined = ((byte1 << 8) | byte2).toString(16).padStart(4, '0');
  357. formattedOutput.push(combined.toUpperCase());
  358. }
  359. for (let i = 1; i < formattedOutput.length; i++) {
  360. const value = parseInt(formattedOutput[i], 16);
  361. const valueInt16 = (value > 0x7FFF) ? value - 0x10000 : value; // 转换为有符号16位整数
  362. if (i === 8) { // 故障码
  363. register.FaultCode_10 = valueInt16.toString();
  364. register.FaultCodeDescription = FAULT_CODE_MAP[register.FaultCode_10] || '未知故障';
  365. }
  366. if (i === 22) { // 地址写入
  367. register.Addres_23 = valueInt16.toString();
  368. } else if (i === 24) { // 频点[0-83]
  369. register.Frequence_25 = valueInt16.toString();
  370. } else if (i === 25) { // 网络ID[0-255]
  371. register.NetworkId_26 = valueInt16.toString();
  372. } else if (i === 26) { // 模块地址
  373. register.modAddre_27 = (parseInt(register.NetworkId_26, 10) * 256) + register.device;
  374. } else if (i === 29) { // 电机1电流1位小数(A)
  375. register.MotorCurrent_30 = valueInt16.toString();
  376. register.MotorCurrent_30 = insertDecimal(register.MotorCurrent_30, 1);
  377. } else if (i === 34) { // 电机2电流1位小数(A)
  378. register.MotorCurrent_35 = valueInt16.toString();
  379. register.MotorCurrent_35 = insertDecimal(register.MotorCurrent_35, 1);
  380. } else if (i === 31) { // 电池1位小数(V)
  381. register.Battery_32 = valueInt16.toString();
  382. register.Battery_32 = insertDecimal(register.Battery_32, 1);
  383. } else if (i === 32) { // 温度(度)
  384. register.Temperature_33 = valueInt16.toString();
  385. } else if (i === 35) { // 标定有效
  386. register.Demarcate_36 = valueInt16.toString();
  387. } else if (i === 36) { // 电机方向
  388. register.MotDrection_37 = valueInt16.toString();
  389. } else if (i === 37) { // 过流保护(A)
  390. register.OverProtection_38 = valueInt16.toString();
  391. } else if (i === 38) { // 跟踪精度
  392. register.TrackingAccuracy_39 = valueInt16.toString();
  393. } else if (i === 39) { // 十进制转二进制0111倾角+过流+限位
  394. register.Message_40 = valueInt16.toString(2); // 转二进制
  395. } else if (i === 40) { // 工作模式
  396. register.WorkModle_41 = valueInt16.toString(2); // 转二进制
  397. } else if (i === 41) { // 目标角度_两位小数
  398. register.TargetAngle_42 = valueInt16;
  399. register.qTargetAngle_42 = insertDecimal(register.TargetAngle_42.toString(), 2);
  400. } else if (i === 42) { // 实际角度1_两位小数
  401. register.RealAngle_43 = valueInt16;
  402. register.qRealAngle_43 = insertDecimal(register.RealAngle_43.toString(), 2);
  403. } else if (i === 30) { // 实际角度2_两位小数
  404. register.RealAngle_31 = valueInt16;
  405. register.qRealAngle_31 = insertDecimal(register.RealAngle_31.toString(), 2);
  406. } else if (i === 43) { // 年
  407. register.Year_44 = valueInt16.toString();
  408. } else if (i === 44) { // 月
  409. register.Month_45 = valueInt16.toString();
  410. } else if (i === 45) { // 日
  411. register.Day_46 = valueInt16.toString();
  412. } else if (i === 46) { // 时
  413. register.Hour_47 = valueInt16.toString();
  414. } else if (i === 47) { // 分
  415. register.Minute_48 = valueInt16.toString();
  416. } else if (i === 48) { // 秒
  417. register.Second_49 = valueInt16.toString();
  418. register.nowtime = `${register.Year_44}-${register.Month_45}-${register.Day_46} ${register.Hour_47.padStart(2, '0')}:${register.Minute_48.padStart(2, '0')}:${register.Second_49.padStart(2, '0')}`;
  419. } else if (i === 49) { // 太阳经度_两位小数
  420. register.Longitude_50 = valueInt16.toString();
  421. register.Longitude_50 = insertDecimal(register.Longitude_50, 2);
  422. } else if (i === 50) { // 太阳纬度_两位小数
  423. register.Latitude_51 = valueInt16.toString();
  424. register.Latitude_51 = insertDecimal(register.Latitude_51, 2);
  425. } else if (i === 51) { // 时区_两位小数800
  426. register.TimeZone_52 = valueInt16.toString();
  427. register.TimeZone_52 = insertDecimal(register.TimeZone_52, 2);
  428. } else if (i === 52) { // 太阳高度角_两位小数
  429. register.EleAngle_53 = valueInt16;
  430. register.qEleAngle_53 = insertDecimal(register.EleAngle_53.toString(), 2);
  431. } else if (i === 53) { // 太阳方位角_两位小数180调零
  432. register.Azimuth_54 = valueInt16;
  433. register.qAzimuth_54 = insertDecimal(register.Azimuth_54.toString(), 2);
  434. } else if (i === 59) { // 宽度_两位小数
  435. register.width_60 = valueInt16;
  436. register.qwidth_60 = insertDecimal(register.width_60.toString(), 2);
  437. } else if (i === 60) { // 间距_两位小数
  438. register.inter_61 = valueInt16;
  439. register.Interval_61 = insertDecimal(register.inter_61.toString(), 2);
  440. } else if (i === 61) { // 上坡度_两位小数
  441. register.UpGrade_62 = valueInt16.toString();
  442. register.UpGrade_62 = insertDecimal(register.UpGrade_62.toString(), 2);
  443. } else if (i === 62) { // 下坡度_两位小数
  444. register.DownGrade_63 = valueInt16.toString();
  445. register.DownGrade_63 = insertDecimal(register.DownGrade_63.toString(), 2);
  446. } else if (i === 63) { // 东限位
  447. register.EasternLimit_64 = valueInt16;
  448. register.qEasternLimit_64 = register.EasternLimit_64.toString();
  449. } else if (i === 64) { // 西限位
  450. register.WesternLimit_65 = valueInt16;
  451. register.qWesternLimit_65 = register.WesternLimit_65.toString();
  452. } else if (i === 65) { // 夜返角
  453. register.NightAngle_66 = valueInt16;
  454. register.qNightAngle_66 = register.NightAngle_66.toString();
  455. } else if (i === 66) { // 放平角度
  456. register.FlatAngle_67 = valueInt16;
  457. register.qFlatAngle_67 = register.FlatAngle_67.toString();
  458. } else if (i === 67) { // 雪天角度
  459. register.SnowAngle_68 = valueInt16;
  460. register.qSnowAngle_68 = register.SnowAngle_68.toString();
  461. } else if (i === 68) { // 指定角度
  462. register.SpecifiedAngle_69 = valueInt16;
  463. register.qSpecifiedAngle_69 = register.SpecifiedAngle_69.toString();
  464. } else if (i === 69) { // 大风角度
  465. register.WindAngle_70 = valueInt16;
  466. register.qWindAngle_70 = register.WindAngle_70.toString();
  467. }
  468. }
  469. console.log(JSON.stringify( register));
  470. return register;
  471. }
  472. function parseZigbeeData(hexString) {
  473. const register = createEmptyRegister();
  474. // 将连续的十六进制字符串转换为带空格的格式
  475. const formattedHexString = hexString.match(/.{1,2}/g)?.join(' ') || hexString;
  476. const byteStrings = formattedHexString.split(' ').filter(s => s.length > 0);
  477. if (byteStrings.length < 10 ){ //其他操作执行成功
  478. return;
  479. }
  480. if (!byteStrings || byteStrings.length < 3) {
  481. throw new Error('蓝牙数据不完整');
  482. }
  483. register.device = parseInt(byteStrings[0], 16);
  484. register.function = parseInt(byteStrings[1], 16);
  485. register.registerNumber = parseInt(byteStrings[2], 16);
  486. const formattedOutput = [];
  487. for (let i = 3; i < byteStrings.length; i += 2) {
  488. const byte1 = parseInt(byteStrings[i], 16);
  489. const byte2 = i + 1 < byteStrings.length ? parseInt(byteStrings[i + 1], 16) : 0;
  490. const combined = ((byte1 << 8) | byte2).toString(16).padStart(4, '0');
  491. formattedOutput.push(combined.toUpperCase());
  492. }
  493. for (let i = 1; i < formattedOutput.length; i++) {
  494. const value = parseInt(formattedOutput[i], 16);
  495. const valueInt16 = (value > 0x7FFF) ? value - 0x10000 : value; // 转换为有符号16位整数
  496. if (i === 30) { // 地址写入zigbee 31-30
  497. register.Addres_23 = valueInt16.toString();
  498. } else if (i === 46) { // 模块地址
  499. register.modAddre_27 = valueInt16;
  500. // 从模块地址中提取频点和网络ID
  501. const hexStr = valueInt16.toString(16).padStart(4, '0').toUpperCase();
  502. const highPartHex = hexStr.substring(0, 2);
  503. const lowPartHex = hexStr.substring(2, 4);
  504. register.NetworkId_26 = parseInt(highPartHex, 16).toString();
  505. register.Frequence_25 = parseInt(lowPartHex, 16).toString();
  506. } else if (i === 23) { // 电机电流1位小数(A)24-23
  507. register.MotorCurrent_30 = valueInt16.toString();
  508. register.MotorCurrent_30 = insertDecimal(register.MotorCurrent_30, 1);
  509. } else if (i === 47) { // 电池1位小数(V)48-47
  510. register.Battery_32 = valueInt16.toString();
  511. register.Battery_32 = insertDecimal(register.Battery_32, 1);
  512. } else if (i === 48) { // 温度(度)49-48
  513. register.Temperature_33 = valueInt16.toString();
  514. } else if (i === 45) { // 电机方向46-45
  515. register.MotDrection_37 = valueInt16.toString();
  516. } else if (i === 29) { // 过流保护(A)30-29
  517. register.OverProtection_38 = valueInt16.toString();
  518. } else if (i === 22) { // 跟踪精度 23-22
  519. register.TrackingAccuracy_39 = valueInt16.toString();
  520. } else if (i === 3) { // 十进制转二进制0111倾角+过流+限位 4-3
  521. register.Message_40 = valueInt16.toString(2); // 转二进制
  522. } else if (i === 1) { // 工作模式2-1
  523. register.WorkModle_41 = valueInt16.toString(2); // 转二进制
  524. } else if (i === 9) { // 目标角度_两位小数10-9
  525. register.TargetAngle_42 = valueInt16;
  526. register.qTargetAngle_42 = insertDecimal(register.TargetAngle_42.toString(), 2);
  527. } else if (i === 10) { // 实际角度_两位小数11-10
  528. register.RealAngle_43 = valueInt16;
  529. register.qRealAngle_43 = insertDecimal(register.RealAngle_43.toString(), 2);
  530. } else if (i === 13) { // 年 14-13
  531. register.Year_44 = valueInt16.toString();
  532. } else if (i === 14) { // 月
  533. register.Month_45 = valueInt16.toString();
  534. } else if (i === 15) { // 日
  535. register.Day_46 = valueInt16.toString();
  536. } else if (i === 16) { // 时
  537. register.Hour_47 = valueInt16.toString();
  538. } else if (i === 17) { // 分
  539. register.Minute_48 = valueInt16.toString();
  540. } else if (i === 18) { // 秒
  541. register.Second_49 = valueInt16.toString();
  542. register.nowtime = `${register.Year_44}-${register.Month_45}-${register.Day_46} ${register.Hour_47.padStart(2, '0')}:${register.Minute_48.padStart(2, '0')}:${register.Second_49.padStart(2, '0')}`;
  543. } else if (i === 19) { // 太阳经度_两位小数20-19
  544. register.Longitude_50 = valueInt16.toString();
  545. register.Longitude_50 = insertDecimal(register.Longitude_50, 2);
  546. } else if (i === 20) { // 太阳纬度_两位小数21-20
  547. register.Latitude_51 = valueInt16.toString();
  548. register.Latitude_51 = insertDecimal(register.Latitude_51, 2);
  549. } else if (i === 21) { // 时区_两位小数800 22-21
  550. register.TimeZone_52 = valueInt16.toString();
  551. register.TimeZone_52 = insertDecimal(register.TimeZone_52, 2);
  552. } else if (i === 5) { // 太阳高度角_两位小数6-5
  553. register.EleAngle_53 = valueInt16;
  554. register.qEleAngle_53 = insertDecimal(register.EleAngle_53.toString(), 2);
  555. } else if (i === 6) { // 太阳方位角_两位小数180调零7-6
  556. register.Azimuth_54 = valueInt16;
  557. register.qAzimuth_54 = insertDecimal(register.Azimuth_54.toString(), 2);
  558. } else if (i === 31) { // 宽度_两位小数32-31
  559. register.width_60 = valueInt16;
  560. register.qwidth_60 = insertDecimal(register.width_60.toString(), 2);
  561. } else if (i === 32) { // 间距_两位小数33-32
  562. register.inter_61 = valueInt16;
  563. register.Interval_61 = insertDecimal(register.inter_61.toString(), 2);
  564. } else if (i === 33) { // 上坡度_两位小数34-33
  565. register.UpGrade_62 = valueInt16.toString();
  566. register.UpGrade_62 = insertDecimal(register.UpGrade_62.toString(), 2);
  567. } else if (i === 34) { // 下坡度_两位小数35-34
  568. register.DownGrade_63 = valueInt16.toString();
  569. register.DownGrade_63 = insertDecimal(register.DownGrade_63.toString(), 2);
  570. } else if (i === 39) { // 东限位40-39
  571. register.EasternLimit_64 = valueInt16;
  572. register.qEasternLimit_64 = register.EasternLimit_64.toString();
  573. } else if (i === 40) { // 西限位
  574. register.WesternLimit_65 = valueInt16;
  575. register.qWesternLimit_65 = register.WesternLimit_65.toString();
  576. } else if (i === 89) { // 夜返角
  577. register.NightAngle_66 = valueInt16;
  578. register.qNightAngle_66 = register.NightAngle_66.toString();
  579. } else if (i === 42) { // 放平角度
  580. register.FlatAngle_67 = valueInt16;
  581. register.qFlatAngle_67 = register.FlatAngle_67.toString();
  582. } else if (i === 41) { // 雪天角度
  583. register.SnowAngle_68 = valueInt16;
  584. register.qSnowAngle_68 = register.SnowAngle_68.toString();
  585. } else if (i === 43) { // 指定角度
  586. register.SpecifiedAngle_69 = valueInt16;
  587. register.qSpecifiedAngle_69 = register.SpecifiedAngle_69.toString();
  588. } else if (i === 44) { // 大风角度
  589. register.WindAngle_70 = valueInt16;
  590. register.qWindAngle_70 = register.WindAngle_70.toString();
  591. }
  592. }
  593. return register;
  594. }
  595. function createEmptyRegister() {
  596. const register = {
  597. device: '',
  598. function: '',
  599. registerNumber: '',
  600. Addres_23: '',
  601. Frequence_25: '',
  602. NetworkId_26: '',
  603. modAddre_27: '',
  604. MotorCurrent_30: '',
  605. MotorCurrent_35: '',
  606. Battery_32: '',
  607. Temperature_33: '',
  608. MotDrection_37: '',
  609. OverProtection_38: '',
  610. TrackingAccuracy_39: '',
  611. Message_40: '',
  612. WorkModle_41: '',
  613. TargetAngle_42: '',
  614. RealAngle_43: '',
  615. RealAngle_31: '',
  616. Year_44: '',
  617. Month_45: '',
  618. Day_46: '',
  619. Hour_47: '',
  620. Minute_48: '',
  621. Second_49: '',
  622. nowtime: '',
  623. Longitude_50: '',
  624. Latitude_51: '',
  625. TimeZone_52: '',
  626. EleAngle_53: '',
  627. Azimuth_54: '',
  628. width_60: '',
  629. inter_61: '',
  630. UpGrade_62: '',
  631. DownGrade_63: '',
  632. EasternLimit_64: '',
  633. WesternLimit_65: '',
  634. NightAngle_66: '',
  635. FlatAngle_67: '',
  636. SnowAngle_68: '',
  637. SpecifiedAngle_69: '',
  638. WindAngle_70: '',
  639. qAzimuth_54: '',
  640. qwidth_60: '',
  641. Interval_61: '',
  642. qEasternLimit_64: '',
  643. qWesternLimit_65: '',
  644. qNightAngle_66: '',
  645. qFlatAngle_67: '',
  646. qSnowAngle_68: '',
  647. qSpecifiedAngle_69: '',
  648. qWindAngle_70: '',
  649. qEleAngle_53: '',
  650. qTargetAngle_42: '',
  651. qRealAngle_43: '',
  652. qRealAngle_31: '',
  653. FaultCode_10:'',
  654. FaultCodeDescription: '',
  655. oldAddressValue: '',
  656. };
  657. return register;
  658. }
  659. /**
  660. * 在数字中插入小数点
  661. */
  662. export function insertDecimal(value, digits = 2) {
  663. // 处理空值或无效值
  664. if (value === null || value === undefined || value === '') {
  665. return (0).toFixed(digits);
  666. }
  667. // 确保是数字类型
  668. const numValue = Number(value);
  669. // 如果不是有效数字,返回默认值
  670. if (isNaN(numValue)) {
  671. return (0).toFixed(digits);
  672. }
  673. // 计算除数
  674. const divisor = Math.pow(10, digits);
  675. // 执行除法并格式化为指定小数位数
  676. return (numValue / divisor).toFixed(digits);
  677. }
  678. /**
  679. * 显示接收数据(格式化为 16 字节一行)
  680. */
  681. export function displayReceiveData(buffer) {
  682. const hexStr = HexToAscii(buffer);
  683. const lines = hexStr.split(' ');
  684. let str = '';
  685. for (let i = 0; i < lines.length; i++) {
  686. str += lines[i] + ' ';
  687. if ((i + 1) % 16 === 0) str += '\n';
  688. }
  689. return str;
  690. }
  691. /**
  692. * 将字节数组转为十六进制字符串
  693. */
  694. export function HexToAscii(buffer) {
  695. return [...new Uint8Array(buffer)]
  696. .map(b => b.toString(16).padStart(2, '0'))
  697. .join(' ')
  698. .trim();
  699. }