modbus.js 28 KB

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