modbus.js 26 KB

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