@jiabaida/tools 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/core/BleApiManager.js +1 -1
- package/dist/cjs/core/BleCmdAnalysis/BaseParamProtocol.js +1 -0
- package/dist/cjs/core/BleCmdAnalysis/BleCmdAnalysis.js +1 -0
- package/dist/cjs/core/BleCmdAnalysis/BleCmdDD.js +1 -0
- package/dist/cjs/core/BleCmdAnalysis/BleCmdFFAA.js +1 -0
- package/dist/cjs/core/BleCmdAnalysis/BleCmdHVES.js +1 -0
- package/dist/cjs/core/BleCmdAnalysis/ESHostProtocol.js +1 -0
- package/dist/cjs/core/BleCmdAnalysis/readAndSetParam.js +1 -0
- package/dist/cjs/core/BleCmdAnalysis.js +1 -0
- package/dist/cjs/core/BleDataProcess.js +1 -0
- package/dist/cjs/core/OtaUpgrade.js +1 -0
- package/dist/cjs/core/TelinkApi.js +1 -0
- package/dist/cjs/core/Transfer.js +1 -0
- package/dist/cjs/core/commonfun.js +1 -1
- package/dist/cjs/core/dataJson/baseParamsJson.js +1 -0
- package/dist/cjs/core/keyAndPwdManager.js +1 -0
- package/dist/cjs/index.js +1 -1
- package/dist/esm/core/BleApiManager.js +1 -1
- package/dist/esm/core/BleCmdAnalysis/BaseParamProtocol.js +1 -0
- package/dist/esm/core/BleCmdAnalysis/BleCmdAnalysis.js +1 -0
- package/dist/esm/core/BleCmdAnalysis/BleCmdDD.js +1 -0
- package/dist/esm/core/BleCmdAnalysis/BleCmdFFAA.js +1 -0
- package/dist/esm/core/BleCmdAnalysis/BleCmdHVES.js +1 -0
- package/dist/esm/core/BleCmdAnalysis/ESHostProtocol.js +1 -0
- package/dist/esm/core/BleCmdAnalysis/readAndSetParam.js +1 -0
- package/dist/esm/core/BleCmdAnalysis.js +1 -0
- package/dist/esm/core/BleDataProcess.js +1 -0
- package/dist/esm/core/OtaUpgrade.js +1 -0
- package/dist/esm/core/TelinkApi.js +1 -0
- package/dist/esm/core/Transfer.js +1 -0
- package/dist/esm/core/commonfun.js +1 -1
- package/dist/esm/core/dataJson/baseParamsJson.js +1 -0
- package/dist/esm/core/keyAndPwdManager.js +1 -0
- package/dist/esm/index.js +1 -1
- package/package.json +13 -3
- package/src/core/BleApiManager.js +487 -0
- package/src/core/BleCmdAnalysis/BaseParamProtocol.js +651 -0
- package/src/core/BleCmdAnalysis/BleCmdAnalysis.js +222 -0
- package/src/core/BleCmdAnalysis/BleCmdDD.js +1214 -0
- package/src/core/BleCmdAnalysis/BleCmdDDA4.js +762 -0
- package/src/core/BleCmdAnalysis/BleCmdFFAA.js +407 -0
- package/src/core/BleCmdAnalysis/BleCmdHVES.js +1222 -0
- package/src/core/BleCmdAnalysis/ESHostProtocol.js +829 -0
- package/src/core/BleCmdAnalysis/index.js +7 -0
- package/src/core/BleCmdAnalysis/readAndSetParam.js +288 -0
- package/src/core/BleDataProcess.js +355 -0
- package/src/core/OtaUpgrade.js +338 -0
- package/src/core/TelinkApi.js +73 -0
- package/src/core/Transfer.js +516 -0
- package/src/core/array.js +10 -0
- package/src/core/commonfun.js +87 -0
- package/src/core/dataJson/baseParamsJson.js +754 -0
- package/src/core/dataJson/index.js +1 -0
- package/src/core/keyAndPwdManager.js +346 -0
- package/src/core/mqttServer.js +296 -0
- package/src/core/rsaEncrypt.js +45 -0
- package/src/index.js +11 -0
|
@@ -0,0 +1,1214 @@
|
|
|
1
|
+
import { getData } from './BleCmdAnalysis.js'
|
|
2
|
+
import { generateCrcCheckSum, decimalToHex, hex2string, hexArr2string, hexToDecimal, stringToTwoHexArray, hexArr2Assic, generateCrc16modbusCheck } from '../BleDataProcess';
|
|
3
|
+
import { getFFAA80Async, resolveFFAA80 } from './BleCmdFFAA.js';
|
|
4
|
+
// #region 进出工厂
|
|
5
|
+
/**进工厂 */
|
|
6
|
+
export const enterFactory = async (deviceId) => {
|
|
7
|
+
return set_PlanCMD_DD(deviceId, 'DD5A00025678FF3077');
|
|
8
|
+
}
|
|
9
|
+
/**写参数退工厂 */
|
|
10
|
+
export const existFactory = async (deviceId) => {
|
|
11
|
+
return set_PlanCMD_DD(deviceId, 'DD5A01022828ffad77');
|
|
12
|
+
}
|
|
13
|
+
/**读参数退工厂 */
|
|
14
|
+
export const readExistFactory = async (deviceId) => {
|
|
15
|
+
return set_PlanCMD_DD(deviceId, 'DD5A01020000FFFD77');
|
|
16
|
+
}
|
|
17
|
+
// #region DD通用指令
|
|
18
|
+
/**DD通用指令
|
|
19
|
+
* @param {*} deviceId
|
|
20
|
+
* @param {*} command eg: 'DD5A01020000FFFD77'
|
|
21
|
+
* @returns
|
|
22
|
+
*/
|
|
23
|
+
export const set_PlanCMD_DD = async (deviceId, command) => {
|
|
24
|
+
if (typeof command == 'string') {
|
|
25
|
+
command = command.match(/(.{2})/g).map((o) => `0x${o}`);
|
|
26
|
+
}
|
|
27
|
+
let result = null;
|
|
28
|
+
try {
|
|
29
|
+
const data = await getData(
|
|
30
|
+
deviceId,
|
|
31
|
+
{
|
|
32
|
+
command,
|
|
33
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd, pkgLen: hexArr[3] + 7 }),
|
|
34
|
+
pkgVerifyHandler: (pkg) => ({ verified: true }),
|
|
35
|
+
},
|
|
36
|
+
'PlanCMD_DD'
|
|
37
|
+
);
|
|
38
|
+
if (data) {
|
|
39
|
+
const dataStr = hexArr2string(data);
|
|
40
|
+
const content = data.slice(4, data.length - 3);
|
|
41
|
+
result = {
|
|
42
|
+
data,
|
|
43
|
+
dataStr,
|
|
44
|
+
status: hex2string(data[2]),
|
|
45
|
+
len: data[3],
|
|
46
|
+
content,
|
|
47
|
+
};
|
|
48
|
+
} else {
|
|
49
|
+
result = {
|
|
50
|
+
status: 0,
|
|
51
|
+
content: [],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error(error);
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// #region 3B3C指令
|
|
61
|
+
export const set_PlanCMD_3B3C = async (deviceId, command) => {
|
|
62
|
+
if (typeof command == 'string') {
|
|
63
|
+
command = command.match(/(.{2})/g).map((o) => `0x${o}`);
|
|
64
|
+
}
|
|
65
|
+
let result = null;
|
|
66
|
+
try {
|
|
67
|
+
const data = await getData(
|
|
68
|
+
deviceId,
|
|
69
|
+
{
|
|
70
|
+
command,
|
|
71
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0x3b && hexArr[1] == 0x3c, pkgLen: hexArr[11] + 15 }),
|
|
72
|
+
pkgVerifyHandler: (pkg) => ({ verified: true }),
|
|
73
|
+
},
|
|
74
|
+
'PlanCMD_3B3C'
|
|
75
|
+
);
|
|
76
|
+
if (data) {
|
|
77
|
+
const dataStr = hexArr2string(data);
|
|
78
|
+
const content = data.slice(12, data.length - 3);
|
|
79
|
+
result = {
|
|
80
|
+
dataStr,
|
|
81
|
+
status: data[10] == 0x80 ? 1 : 0, // 0x80表示失败
|
|
82
|
+
addr: hexArr2string(data.slice(2, 10)),
|
|
83
|
+
content,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(error);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// #region 获取设备3B3C信息包
|
|
93
|
+
/** 获取设备3B3C信息包
|
|
94
|
+
*
|
|
95
|
+
* @param {*} deviceId
|
|
96
|
+
* @returns
|
|
97
|
+
*/
|
|
98
|
+
export const get3B3CAsync = async (deviceId) => {
|
|
99
|
+
const /*帧头*/ header = ['0x3b', '0x3c'];
|
|
100
|
+
const /*地址码*/ addressCode = ['0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00'];
|
|
101
|
+
const /*命令码*/ commandCode = ['0x01'];
|
|
102
|
+
const /*数据内容*/ data = [];
|
|
103
|
+
const /*数据内容长度*/ dataLength = [`0x${decimalToHex(data.length, 2)}`];
|
|
104
|
+
const /*校验位(地址码~数据内容)*/ check = generateCrc16modbusCheck([...addressCode, ...commandCode, ...dataLength, ...data], false);
|
|
105
|
+
const /*帧尾*/ footer = ['0x0d'];
|
|
106
|
+
|
|
107
|
+
// 帧头-地址码-命令码-数据内容长度-数据内容(没有时为空)-校验位-帧尾 3B3C-0000000000000000-01-00-NULL-9771-0D
|
|
108
|
+
|
|
109
|
+
const command = [...header, ...addressCode, ...commandCode, ...dataLength, ...data, ...check, ...footer];
|
|
110
|
+
const response = '3B3C010000000000000001470104070235000208555031365330313503154A42442D4C313653323030412E31302E30322E3031041E4A4244343831303030303100000000000000000000000000000000000000327A0D';
|
|
111
|
+
return getData(
|
|
112
|
+
deviceId,
|
|
113
|
+
{
|
|
114
|
+
command,
|
|
115
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0x3b && hexArr[1] == 0x3c, pkgLen: hexArr[11] + 15 }),
|
|
116
|
+
pkgVerifyHandler: (pkg) => {
|
|
117
|
+
const len = pkg.length;
|
|
118
|
+
const [c1, c2] = generateCrc16modbusCheck(pkg.slice(2, len - 3));
|
|
119
|
+
const [_c1, _c2] = [pkg[len - 3], pkg[len - 2]];
|
|
120
|
+
return { verified: c1 == _c1 && c2 == _c2 };
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
'3B3C'
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// #region 解析3B3C信息包
|
|
128
|
+
/** 解析3B3C信息包
|
|
129
|
+
*
|
|
130
|
+
* @param {number[]} data 3b3c十六进制数据数组
|
|
131
|
+
* @returns
|
|
132
|
+
*/
|
|
133
|
+
export const resolve3B3C = (data) => {
|
|
134
|
+
if (!data) return null;
|
|
135
|
+
// [59, 60, 1, 0, 0, 0, 0, 0, 0, 0, 1, 71, 1, 4, 7, 2, 53, 0, 2, 8, 85, 80, 49, 54, 83, 48, 49, 53, 3, 21, 74, 66, 68, 45, 76, 49, 54, 83, 50, 48, 48, 65, 46, 49, 48, 46, 48, 50, 46, 48, 49, 4, 30, 74, 66, 68, 52, 56, 49, 48, 48, 48, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 122, 13]
|
|
136
|
+
// 3B3C010000000000000001470104070235000208555031365330313503154A42442D4C313653323030412E31302E30322E3031041E4A4244343831303030303100000000000000000000000000000000000000327A0D
|
|
137
|
+
|
|
138
|
+
// [59, 60, 0, 0, 0, 0, 0, 0, 0, 0, 1, 22, 1, 4, 0, 2, 1, 0, 2, 0, 3, 10, 68, 72, 48, 52, 46, 49, 46, 52, 46, 55, 4, 0, 119, 90, 13];
|
|
139
|
+
// 3B3C000000000000000001160104000201000200030A444830342E312E342E370400775A0D
|
|
140
|
+
const dataStr = hexArr2string(data);
|
|
141
|
+
let result = {
|
|
142
|
+
dataStr,
|
|
143
|
+
status: hex2string(data[10]),
|
|
144
|
+
addr: hexArr2string(data.slice(2, 10)),
|
|
145
|
+
};
|
|
146
|
+
const content = data.slice(12, data.length - 3);
|
|
147
|
+
const CONTENT = [/*产品类型-TYPE*/ '01', /*产品类型标识长度*/ '04', /*产品类型表示*/ '00', /*前端类型标识*/ '02', /*协议版本标识*/ '01', /*层级关系架构*/ '00', /*硬件版本号-TYPE*/ '02', /*硬件版本号长度*/ '00', /*软件版本号-TYPE*/ '03', /*软件版本号长度*/ '0a', /*软件版本号内容ASCII*/ '44', /*软件版本号内容ASCII*/ '48', /*软件版本号内容ASCII*/ '30', /*软件版本号内容ASCII*/ '34', /*软件版本号内容ASCII*/ '2e', /*软件版本号内容ASCII*/ '31', /*软件版本号内容ASCII*/ '2e', /*软件版本号内容ASCII*/ '34', /*软件版本号内容ASCII*/ '2e', /*软件版本号内容ASCII*/ '37', /*PCB-TYPE*/ '04', /*PCB内容长度*/ '00'];
|
|
148
|
+
while (content.length > 0) {
|
|
149
|
+
const [v1, v2, ...v] = content.splice(0, content[1] + 2);
|
|
150
|
+
if (v1 == 0x01) {
|
|
151
|
+
result['productFlag'] = hex2string(v1);
|
|
152
|
+
result['productLen'] = v2;
|
|
153
|
+
if (v) {
|
|
154
|
+
const [productType, frontType, protocolV, level] = v.map((o) => hex2string(o));
|
|
155
|
+
Object.assign(result, { productType, frontType, protocolV, level });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (v1 == 0x02) {
|
|
159
|
+
result['hardwareFlag'] = hex2string(v1);
|
|
160
|
+
result['hardwareLen'] = v2;
|
|
161
|
+
result['hardwareV'] = hexArr2Assic(v);
|
|
162
|
+
}
|
|
163
|
+
if (v1 == 0x03) {
|
|
164
|
+
result['sofewareFlag'] = hex2string(v1);
|
|
165
|
+
result['sofewareLen'] = v2;
|
|
166
|
+
result['sofewareV'] = hexArr2Assic(v);
|
|
167
|
+
}
|
|
168
|
+
if (v1 == 0x04) {
|
|
169
|
+
result['pcbFlag'] = hex2string(v1);
|
|
170
|
+
result['pcbLen'] = v2;
|
|
171
|
+
result['pcbContent'] = hexArr2Assic(v);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// #region 获取设备基本信息03指令
|
|
178
|
+
/** 获取设备基本信息03指令
|
|
179
|
+
*
|
|
180
|
+
* @param {*} deviceId
|
|
181
|
+
* @returns
|
|
182
|
+
*/
|
|
183
|
+
export const getDDA503Async = async (deviceId) => {
|
|
184
|
+
const command = ['0xdd', '0xa5', '0x03', '0x00', '0xff', '0xfd', '0x77'];
|
|
185
|
+
const response = 'DD03002313E10000199D27100000310F00000000000014410310060BB60BC60BC10BBA0BBD0BC1F89777';
|
|
186
|
+
|
|
187
|
+
const hex = await getData(
|
|
188
|
+
deviceId,
|
|
189
|
+
{
|
|
190
|
+
command,
|
|
191
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x03, pkgLen: hexArr[3] + 7 }),
|
|
192
|
+
pkgVerifyHandler: (pkg) => {
|
|
193
|
+
const len = pkg.length;
|
|
194
|
+
const [c1, c2] = generateCrcCheckSum(pkg.slice(2, len - 3));
|
|
195
|
+
const [_c1, _c2] = [pkg[len - 3], pkg[len - 2]];
|
|
196
|
+
return { verified: c1 == _c1 && c2 == _c2 };
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
'DDA5_03'
|
|
200
|
+
);
|
|
201
|
+
if (!hex) return null;
|
|
202
|
+
return resolveDDA503(hex);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// #region 解析基本信息03指令
|
|
206
|
+
/** 解析基本信息03指令
|
|
207
|
+
*
|
|
208
|
+
* @param {number[]} data DDA503十六进制数据数组
|
|
209
|
+
* @returns {Object} 解析后的设备信息对象
|
|
210
|
+
* @returns {string} dataStr 十六进制数据数组转换后的字符串
|
|
211
|
+
* @returns {string} status 状态信息的字符串表示
|
|
212
|
+
* @returns {number} len 数据长度
|
|
213
|
+
* @returns {string} softwareV 软件版本信息的字符串表示
|
|
214
|
+
* @returns {boolean} chargeSwitch 充电开关状态,true 表示打开,false 表示关闭
|
|
215
|
+
* @returns {boolean} dischargeSwitch 放电开关状态,true 表示打开,false 表示关闭
|
|
216
|
+
* @returns {string} BMSVersion BMS 软件版本号
|
|
217
|
+
* @returns {string} totalVoltage 总电压,单位伏特,保留两位小数
|
|
218
|
+
* @returns {number} electricity 总电流,单位安培,保留两位小数
|
|
219
|
+
* @returns {string} power 功率,单位瓦特,保留两位小数
|
|
220
|
+
* @returns {number} soc 电池剩余电量百分比
|
|
221
|
+
* @returns {number} surplusCapacity 剩余容量,单位 Ah,保留两位小数
|
|
222
|
+
* @returns {number} normCap 标称容量,单位 Ah,保留两位小数
|
|
223
|
+
* @returns {number} humidity 湿度值
|
|
224
|
+
* @returns {number} cycleIndex 循环次数
|
|
225
|
+
* @returns {number} fullChargeCapacity 满充容量,单位 Ah
|
|
226
|
+
* @returns {number} SOH 电池健康状态百分比
|
|
227
|
+
* @returns {boolean} heatingState 是否正在加热,true 表示正在加热,false 表示未加热
|
|
228
|
+
* @returns {number} heatingCurrent 加热电流,单位 mA
|
|
229
|
+
* @returns {boolean} isFactoryMode 是否处于工厂模式,true 表示是,false 表示否
|
|
230
|
+
* @returns {boolean} equilibriumStatus 均衡状态,true 表示正在均衡,false 表示未均衡
|
|
231
|
+
* @returns {boolean} protectStatus 保护状态,true 表示存在保护状态,false 表示正常
|
|
232
|
+
* @returns {string} protectStatusIndex 发生保护的状态索引,以逗号分隔的字符串
|
|
233
|
+
* @returns {number} ntcNums NTC 传感器数量
|
|
234
|
+
* @returns {string[]} temperaturesList 温度列表,单位摄氏度,保留一位小数
|
|
235
|
+
* @returns {string[]} fahTempList 华氏度温度列表,保留一位小数
|
|
236
|
+
* @returns {number} fet FET 状态值
|
|
237
|
+
* balances 单串均衡数组
|
|
238
|
+
*/
|
|
239
|
+
export const resolveDDA503 = (data) => {
|
|
240
|
+
if (!data) return null;
|
|
241
|
+
const cmdResp03 = hexArr2string(data);
|
|
242
|
+
const content = data.slice(4, data.length - 3);
|
|
243
|
+
|
|
244
|
+
const binArr = data[24].toString(2).padStart(8, '0').split('').reverse();
|
|
245
|
+
|
|
246
|
+
const balances = data.slice(16, 20).flatMap((byte) =>
|
|
247
|
+
byte.toString(2).padStart(8, "0").split("").reverse()
|
|
248
|
+
);
|
|
249
|
+
console.warn("BC: ---------------", balances);
|
|
250
|
+
const fet = data[24];
|
|
251
|
+
/**充电开关 */
|
|
252
|
+
const chargeSwitch = !!Number(binArr[0]);
|
|
253
|
+
const dischargeSwitch = !!Number(binArr[1]);
|
|
254
|
+
|
|
255
|
+
/** 软件版本 */
|
|
256
|
+
const BMSVersion = decimalToHex(data[22]);
|
|
257
|
+
|
|
258
|
+
const humidityIndex = data[26] * 2 + 27;
|
|
259
|
+
/**湿度 */
|
|
260
|
+
const humidity = data[humidityIndex];
|
|
261
|
+
// 判断电流及容量单位,默认10mAh, 当fet控制状态bit7=1或者湿度值为88(十六进制,对应十进制136)时,单位为100mAh
|
|
262
|
+
const n = !!Number(binArr[7]) || humidity == 136 ? 10 : 100;
|
|
263
|
+
/**总电压 */
|
|
264
|
+
const totalVoltage = Number((((data[4] << 8) + (data[5] & 0x0ff)) / 100).toFixed(2))
|
|
265
|
+
let current = (data[6] << 8) + (data[7] & 0x0ff);
|
|
266
|
+
current = current > 32768 ? (current - 65536) / n : current / n;
|
|
267
|
+
/**总电流 */
|
|
268
|
+
const electricity = Number(current.toFixed(2));
|
|
269
|
+
/**功率 */
|
|
270
|
+
const power = Number((totalVoltage * electricity).toFixed(2));
|
|
271
|
+
/** soc % */
|
|
272
|
+
const soc = data[23] ?? 0;
|
|
273
|
+
/** 串数 */
|
|
274
|
+
const seriesNum = data[25] ?? 0;
|
|
275
|
+
|
|
276
|
+
const volumeHex = decimalToHex(data[8]) + decimalToHex(data[9]); // 10mAh
|
|
277
|
+
|
|
278
|
+
/** 剩余容量 */
|
|
279
|
+
const surplusCapacity = Number((hexToDecimal(volumeHex) / n).toFixed(2));
|
|
280
|
+
|
|
281
|
+
const normCapHex = decimalToHex(data[10]) + decimalToHex(data[11]);
|
|
282
|
+
/** 标称容量 */
|
|
283
|
+
const normCap = Number((hexToDecimal(normCapHex) / n).toFixed(2));
|
|
284
|
+
|
|
285
|
+
const cycleHex = decimalToHex(data[12]) + decimalToHex(data[13]);
|
|
286
|
+
/** 循环次数 */
|
|
287
|
+
const cycleIndex = hexToDecimal(cycleHex);
|
|
288
|
+
|
|
289
|
+
const fccHex = decimalToHex(data[humidityIndex + 3]) + decimalToHex(data[humidityIndex + 4]);
|
|
290
|
+
/** 满充容量 */
|
|
291
|
+
const fullChargeCapacity = Number((hexToDecimal(fccHex) / n).toFixed(2));
|
|
292
|
+
const SOH = cycleIndex <= 100 ? 100 : Math.min(100, (fullChargeCapacity / normCap) * 100);
|
|
293
|
+
/**是否正在加热 当FET bit3=1 OR bit4=1(任意一个为1),则加热状态=开*/
|
|
294
|
+
const heatingState = !!Number(binArr[4]) || !!Number(binArr[3])
|
|
295
|
+
/**加热电流 */
|
|
296
|
+
let heatingCurrent = null;
|
|
297
|
+
// 判断模组回复长度(内容长度加起止位命令码等)是否大于计算出的加热电流的索引加上校验码停止位(由于是索引,长度需加1)
|
|
298
|
+
const hasHeatingCurrent = data[3] + 7 >= humidityIndex + 9 + 4 + 1;
|
|
299
|
+
const hasEquilCurrent = data[3] + 7 >= humidityIndex + 7 + 4 + 1;
|
|
300
|
+
// 如果处于加热状态且存在加热电流数据
|
|
301
|
+
if (heatingState && hasHeatingCurrent) {
|
|
302
|
+
const i = humidityIndex + 9;
|
|
303
|
+
heatingCurrent = heatingCurrent = (
|
|
304
|
+
((data[i] << 8) | data[i + 1]) /
|
|
305
|
+
100
|
|
306
|
+
).toFixed(2);
|
|
307
|
+
}
|
|
308
|
+
let equilibriumCurrent = null;
|
|
309
|
+
// 均衡电流
|
|
310
|
+
if (hasEquilCurrent) {
|
|
311
|
+
const i = humidityIndex + 7;
|
|
312
|
+
let equilCurrent = (data[i] << 8) + (data[i + 1] & 0x0ff);
|
|
313
|
+
equilCurrent = equilCurrent > 32768 ? (equilCurrent - 65536) / 1000 : equilCurrent / 1000;
|
|
314
|
+
equilibriumCurrent = Number(equilCurrent.toFixed(2));
|
|
315
|
+
}
|
|
316
|
+
/**是否是工厂模式 */
|
|
317
|
+
const isFactoryMode = !!Number(binArr[6]);
|
|
318
|
+
/**均衡状态 */
|
|
319
|
+
const equilibriumStatus = !(data[16] === 0 && data[17] === 0);
|
|
320
|
+
/**保护状态 */
|
|
321
|
+
const protectStatus = !(data[20] === 0 && data[21] === 0);
|
|
322
|
+
// 解析保护状态
|
|
323
|
+
const protectStateHex = decimalToHex(data[20]) + decimalToHex(data[21]);
|
|
324
|
+
const protVal = hexToDecimal(protectStateHex);
|
|
325
|
+
|
|
326
|
+
// 告警状态下标
|
|
327
|
+
let alarmIndex = data[26] * 2 + 28;
|
|
328
|
+
let alarmStateHex = decimalToHex(data[alarmIndex]) + decimalToHex(data[alarmIndex + 1]);
|
|
329
|
+
let alarmsState = hexToDecimal(alarmStateHex);
|
|
330
|
+
|
|
331
|
+
const ntcNums = data[26] & 0xff;
|
|
332
|
+
/**温度 */
|
|
333
|
+
const temperaturesList = Array.from({ length: ntcNums }, (_, i) => {
|
|
334
|
+
const tempH = data[26 + i * 2 + 1] & 0xff;
|
|
335
|
+
const tempL = data[26 + i * 2 + 2] & 0xff;
|
|
336
|
+
return ((tempH * 256 + tempL - 2731) / 10).toFixed(1);
|
|
337
|
+
});
|
|
338
|
+
/** 华氏度 */
|
|
339
|
+
const fahTempList = []
|
|
340
|
+
temperaturesList.forEach((el) => {
|
|
341
|
+
const value = Number(el) * (9 / 5) + 32;
|
|
342
|
+
fahTempList.push(value.toFixed(1))
|
|
343
|
+
})
|
|
344
|
+
const temperatures = temperaturesList.map((item, index) => ({
|
|
345
|
+
name: index + 1,
|
|
346
|
+
value: item,
|
|
347
|
+
}));
|
|
348
|
+
return {
|
|
349
|
+
cmdResp03,
|
|
350
|
+
status: hex2string(data[2]),
|
|
351
|
+
len: data[3],
|
|
352
|
+
softwareV: hex2string(content[18]),
|
|
353
|
+
balances,
|
|
354
|
+
BMSVersion,
|
|
355
|
+
soc,
|
|
356
|
+
normCap,
|
|
357
|
+
surplusCapacity,
|
|
358
|
+
totalVoltage,
|
|
359
|
+
electricity,
|
|
360
|
+
power,
|
|
361
|
+
cycleIndex,
|
|
362
|
+
equilibriumStatus,
|
|
363
|
+
chargeSwitch,
|
|
364
|
+
dischargeSwitch,
|
|
365
|
+
temperatures,
|
|
366
|
+
humidity,
|
|
367
|
+
protectStatus,
|
|
368
|
+
fullChargeCapacity,
|
|
369
|
+
SOH,
|
|
370
|
+
heatingState,
|
|
371
|
+
heatingCurrent,
|
|
372
|
+
equilibriumCurrent,
|
|
373
|
+
isFactoryMode,
|
|
374
|
+
protVal,
|
|
375
|
+
alarmsState,
|
|
376
|
+
ntcNums,
|
|
377
|
+
temperaturesList,
|
|
378
|
+
fahTempList,
|
|
379
|
+
fet,
|
|
380
|
+
seriesNum,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// #region 获取DDA504数据
|
|
385
|
+
/** 获取DDA504数据
|
|
386
|
+
* 电压数据
|
|
387
|
+
* @param {*} deviceId
|
|
388
|
+
* @returns
|
|
389
|
+
*/
|
|
390
|
+
export const getDDA504Async = async (deviceId, dataScope) => {
|
|
391
|
+
const command = ['0xdd', '0xa5', '0x04', '0x00', '0xff', '0xfc', '0x77'];
|
|
392
|
+
const response = '';
|
|
393
|
+
|
|
394
|
+
const hex = await getData(
|
|
395
|
+
deviceId,
|
|
396
|
+
{
|
|
397
|
+
command,
|
|
398
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x04, pkgLen: hexArr[3] + 7 }),
|
|
399
|
+
pkgVerifyHandler: (pkg) => {
|
|
400
|
+
const len = pkg.length;
|
|
401
|
+
const [c1, c2] = generateCrcCheckSum(pkg.slice(2, len - 3));
|
|
402
|
+
const [_c1, _c2] = [pkg[len - 3], pkg[len - 2]];
|
|
403
|
+
return { verified: c1 == _c1 && c2 == _c2 };
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
'DDA5_04'
|
|
407
|
+
);
|
|
408
|
+
if (!hex) return null;
|
|
409
|
+
return resolveDDA504(hex, dataScope);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// #region 解析DDA504数据
|
|
413
|
+
/**
|
|
414
|
+
* 解析硬件电压数据 04 指令
|
|
415
|
+
*
|
|
416
|
+
* @param {number[]} data DDA504十六进制数据数组
|
|
417
|
+
* @param {boolean} [dataScope=false] 是否启用高精度数据解析,默认值为 false
|
|
418
|
+
* @returns {Object} 解析后的电压数据对象
|
|
419
|
+
* @returns {string} dataStr 十六进制数据数组转换后的字符串
|
|
420
|
+
* @returns {number[]} voltageList 电压列表,根据 dataScope 保留不同精度
|
|
421
|
+
* @returns {number|null} highestVoltage 最高电压,根据 dataScope 保留不同精度,若电压列表为空则为 null
|
|
422
|
+
* @returns {number|null} lowestVoltage 最低电压,根据 dataScope 保留不同精度,若电压列表为空则为 null
|
|
423
|
+
* @returns {number|null} averageVoltage 平均电压,根据 dataScope 保留不同精度,若电压列表为空则为 null
|
|
424
|
+
* @returns {number|null} dropoutVoltage 电压差,根据 dataScope 保留不同精度,若电压列表为空则为 null
|
|
425
|
+
*/
|
|
426
|
+
export const resolveDDA504 = (data, dataScope) => {
|
|
427
|
+
console.log('data: ', data);
|
|
428
|
+
if (!data) return null;
|
|
429
|
+
const cmdResp04 = hexArr2string(data);
|
|
430
|
+
const voltageList = [];
|
|
431
|
+
for (let index = 4; index < parseInt(data[3]) + 4; index += 2) {
|
|
432
|
+
|
|
433
|
+
let voltageHex = decimalToHex(data[index]) + decimalToHex(data[index + 1]);
|
|
434
|
+
let voltage = hexToDecimal(voltageHex);
|
|
435
|
+
voltageList.push(Number(voltage));
|
|
436
|
+
}
|
|
437
|
+
let highestVoltage = null
|
|
438
|
+
let lowestVoltage = null
|
|
439
|
+
let averageVoltage = null
|
|
440
|
+
let dropoutVoltage = null
|
|
441
|
+
if (voltageList.length > 0) {
|
|
442
|
+
// 最高、最低电压
|
|
443
|
+
highestVoltage = Math.max(...voltageList);
|
|
444
|
+
lowestVoltage = Math.min(...voltageList);
|
|
445
|
+
// 计算平均电压
|
|
446
|
+
const sum = voltageList.reduce((acc, cur) => acc + cur, 0);
|
|
447
|
+
averageVoltage = sum / voltageList.length;
|
|
448
|
+
// 压差
|
|
449
|
+
const dropout = highestVoltage - lowestVoltage;
|
|
450
|
+
|
|
451
|
+
// 精度处理
|
|
452
|
+
const precision = dataScope ? 3 : 2;
|
|
453
|
+
highestVoltage = Number((highestVoltage / 1000).toFixed(precision));
|
|
454
|
+
lowestVoltage = Number((lowestVoltage / 1000).toFixed(precision));
|
|
455
|
+
averageVoltage = Number((averageVoltage / 1000).toFixed(precision));
|
|
456
|
+
dropoutVoltage = Number((dropout / 1000).toFixed(precision));
|
|
457
|
+
}
|
|
458
|
+
voltageList.forEach((v, i) => {
|
|
459
|
+
let newV = v / 1000
|
|
460
|
+
voltageList[i] = Number(newV.toFixed(dataScope ? 3 : 2));
|
|
461
|
+
});
|
|
462
|
+
const voltageSeries = voltageList.map((item, index) => ({
|
|
463
|
+
name: index + 1,
|
|
464
|
+
value: item,
|
|
465
|
+
}));
|
|
466
|
+
return {
|
|
467
|
+
cmdResp04,
|
|
468
|
+
voltageList,
|
|
469
|
+
voltageSeries,
|
|
470
|
+
highestVoltage,
|
|
471
|
+
lowestVoltage,
|
|
472
|
+
averageVoltage,
|
|
473
|
+
dropoutVoltage
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// #region 获取芯片类型
|
|
478
|
+
/** 获取芯片类型
|
|
479
|
+
*
|
|
480
|
+
* @param {*} deviceId
|
|
481
|
+
* @returns
|
|
482
|
+
*/
|
|
483
|
+
export const getChipTypeAsync = async (deviceId) => {
|
|
484
|
+
const command = ['0xdd', '0xa5', '0x00', '0x00', '0x00', '0x00', '0x77'];
|
|
485
|
+
const hex = await getData(
|
|
486
|
+
deviceId,
|
|
487
|
+
{
|
|
488
|
+
command,
|
|
489
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x00, pkgLen: hexArr[3] + 7 }),
|
|
490
|
+
pkgVerifyHandler: (pkg) => {
|
|
491
|
+
const len = pkg.length;
|
|
492
|
+
const [c1, c2] = generateCrcCheckSum(pkg.slice(2, len - 3));
|
|
493
|
+
const [_c1, _c2] = [pkg[len - 3], pkg[len - 2]];
|
|
494
|
+
return { verified: c1 == _c1 && c2 == _c2 };
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
'DDA5_00'
|
|
498
|
+
);
|
|
499
|
+
if (!hex) return null;
|
|
500
|
+
return resolveDDA500(hex);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// #region 解析芯片类型
|
|
504
|
+
/** 解析芯片类型
|
|
505
|
+
*
|
|
506
|
+
* @param {number[]} data DDA500十六进制数据数组
|
|
507
|
+
* @returns {Object} 解析后的芯片类型对象
|
|
508
|
+
* @returns {string} dataStr 十六进制数据数组转换后的字符串
|
|
509
|
+
* @returns {string} status 状态码
|
|
510
|
+
* @returns {number} len 数据长度
|
|
511
|
+
* @returns {string} type 芯片类型
|
|
512
|
+
*/
|
|
513
|
+
export const resolveDDA500 = (data) => {
|
|
514
|
+
// 'dd00 0002 0005 fff9 77';
|
|
515
|
+
// 当XX = 0或者发送该指令不回复时,默认BMS方案为TI方案,
|
|
516
|
+
// 当XX = 1时 为凹凸方案
|
|
517
|
+
// 当XX = 2时 为新塘松下方案
|
|
518
|
+
// 当XX = 3时 为中颖309方案
|
|
519
|
+
// 当XX = 4时 为中颖303方案,
|
|
520
|
+
// 当XX = 5时 为集澈芯片
|
|
521
|
+
if (!data) return null;
|
|
522
|
+
const dataStr = hexArr2string(data);
|
|
523
|
+
return {
|
|
524
|
+
dataStr,
|
|
525
|
+
status: hex2string(data[2]),
|
|
526
|
+
len: data[3],
|
|
527
|
+
type: data[5],
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// #region 设置MOS开关 E1指令
|
|
532
|
+
/**设置MOS开关
|
|
533
|
+
* value:
|
|
534
|
+
* 0x00 - 充电MOS打开 放电MOS打开
|
|
535
|
+
* 0x01 - 充电MOS关闭 放电MOS打开
|
|
536
|
+
* 0x02 - 充电MOS打开 放电MOS关闭
|
|
537
|
+
* 0x03 - 充电MOS关闭 放电MOS关闭
|
|
538
|
+
*/
|
|
539
|
+
export const getDD5AE1Async = async (deviceId, value) => {
|
|
540
|
+
const _command = ['0xE1', '0x02', '0x00' /*0x00用户级别控制 0xaa运营级别控制*/, value];
|
|
541
|
+
const checks = generateCrcCheckSum(_command);
|
|
542
|
+
const command = ['0xDD', '0x5A', ..._command, ...checks, '0x77'];
|
|
543
|
+
|
|
544
|
+
// T: DD 5A E1 02 00 02 FF 1B 77
|
|
545
|
+
// R: DD E1 00000000 77
|
|
546
|
+
return getData(
|
|
547
|
+
deviceId,
|
|
548
|
+
{
|
|
549
|
+
command,
|
|
550
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0xe1, pkgLen: hexArr[3] + 7 }),
|
|
551
|
+
pkgVerifyHandler: (pkg) => {
|
|
552
|
+
return { verified: true };
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
'DD5A_E1'
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// #region 设置MOS开关 FB指令
|
|
560
|
+
/**设置MOS开关 FB
|
|
561
|
+
* type:
|
|
562
|
+
* 0x00 - 放电MOS
|
|
563
|
+
* 0x01 - 充电MOS
|
|
564
|
+
* 0x03 - 预放电MOS
|
|
565
|
+
* 0x05 - 电池激活
|
|
566
|
+
* 0x06 - 加热控制
|
|
567
|
+
*
|
|
568
|
+
* value:
|
|
569
|
+
* 0x00 - 打开
|
|
570
|
+
* 0x01 - 关闭
|
|
571
|
+
*
|
|
572
|
+
* 电池激活 value:
|
|
573
|
+
* 0x00 - 解除激活
|
|
574
|
+
* 0x01 - 激活
|
|
575
|
+
* 0x02 - 试用30分钟激活
|
|
576
|
+
* 0x03 - 试用一小时激活
|
|
577
|
+
* 0x04 - 试用两小时激活
|
|
578
|
+
* 0x05 - 试用三小时激活
|
|
579
|
+
* * 加热控制 value:
|
|
580
|
+
* 0x00 - 关闭加热
|
|
581
|
+
* 0x01 - 开启加热
|
|
582
|
+
*/
|
|
583
|
+
export const getDD5AFBAsync = async (deviceId, type, value) => {
|
|
584
|
+
const _command = ['0xFB', '0x02', type, value];
|
|
585
|
+
const checks = generateCrcCheckSum(_command);
|
|
586
|
+
const command = ['0xDD', '0x5A', ..._command, ...checks, '0x77'];
|
|
587
|
+
console.log('command: ', command);
|
|
588
|
+
return getData(
|
|
589
|
+
deviceId,
|
|
590
|
+
{
|
|
591
|
+
command,
|
|
592
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0xfb, pkgLen: hexArr[3] + 7 }),
|
|
593
|
+
pkgVerifyHandler: (pkg) => {
|
|
594
|
+
return { verified: true };
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
'DD5A_FB'
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
/** 解析DD指令响应
|
|
601
|
+
*
|
|
602
|
+
* @param {*} data
|
|
603
|
+
* @returns
|
|
604
|
+
*/
|
|
605
|
+
export const resolveBaseDD = (data) => {
|
|
606
|
+
if (!data) return null;
|
|
607
|
+
const dataStr = hexArr2string(data);
|
|
608
|
+
const content = data.slice(4, data.length - 3);
|
|
609
|
+
return {
|
|
610
|
+
dataStr,
|
|
611
|
+
status: hex2string(data[2]),
|
|
612
|
+
len: data[3],
|
|
613
|
+
content,
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
// #region 0A 控制指令
|
|
618
|
+
/**
|
|
619
|
+
* 0A 控制指令
|
|
620
|
+
* @param {*} deviceId 设备id
|
|
621
|
+
* @param {*} values 指令内容 十六进制字符串 eg: '0100'
|
|
622
|
+
*
|
|
623
|
+
* @param 0100 - 重置容量 重置容量按照电压参数重新估算容量
|
|
624
|
+
* @param 0200 - 清除历史保护次数
|
|
625
|
+
* @param 0300 - 复位一次单片机
|
|
626
|
+
* @param 0400 - 清除保护状态和告警状态
|
|
627
|
+
* @param 0500 - 保护板进入到休眠状态
|
|
628
|
+
* @param 0600 - 进入超低功耗,需要充电唤醒。
|
|
629
|
+
* @param 0700 - 自动均衡模式,发送该指令自动开启均衡
|
|
630
|
+
* @param 0800 - 进入储运模式
|
|
631
|
+
* @param 0900 - 设置SOC-20%开关功能 双聚版本,设置为1之后则低于20%会关闭放电
|
|
632
|
+
* @param 0A00 - 低于20%强制打开指令 当SOC_20%功能有效,并且SOC<20%,可发送此指令强制打开
|
|
633
|
+
* @param 0B00 - 强制启动-启动模式 启动电池模式时强启启动输出,强制输出1小时
|
|
634
|
+
* @param 0C00 - 强制加热 加热到15℃自动停止
|
|
635
|
+
* @param 0D00 - 低电量蜂鸣器报警功能 蜂鸣器 放电时 每30S响一次 放电结束延时5分钟
|
|
636
|
+
* @param 0E00 - 打开低电量闪断功能 闪断功能为当从10%或者5%时触发,触发后控制MOS打嗝,开1S断1S,循环3次
|
|
637
|
+
* @param 0F00 - 关闭低电量闪断功能
|
|
638
|
+
* @param 1881 - 恢复出厂设置参数
|
|
639
|
+
* @param FFFF - 心跳包
|
|
640
|
+
* @returns hex 数组
|
|
641
|
+
*/
|
|
642
|
+
export const getDD5A0AAsync = async (deviceId, value) => {
|
|
643
|
+
const values = stringToTwoHexArray(value);
|
|
644
|
+
const _command = ['0x0A', '0x02', ...values];
|
|
645
|
+
const checks = generateCrcCheckSum(_command);
|
|
646
|
+
const command = ['0xDD', '0x5A', ..._command, ...checks, '0x77'];
|
|
647
|
+
console.log('command: ', command);
|
|
648
|
+
return getData(
|
|
649
|
+
deviceId,
|
|
650
|
+
{
|
|
651
|
+
command,
|
|
652
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x0A, pkgLen: hexArr[3] + 7 }),
|
|
653
|
+
pkgVerifyHandler: (pkg) => {
|
|
654
|
+
return { verified: true };
|
|
655
|
+
},
|
|
656
|
+
},
|
|
657
|
+
`DD5A_0A_${value}`
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// #region 设置满充容量
|
|
662
|
+
/**设置满充容量
|
|
663
|
+
*
|
|
664
|
+
* @param {*} deviceId
|
|
665
|
+
* @param {*} value AH
|
|
666
|
+
* @returns
|
|
667
|
+
*/
|
|
668
|
+
export const getDD5AFAAsync = async (deviceId, value) => {
|
|
669
|
+
const hex = value * 100; /*AH => 10mAh*/
|
|
670
|
+
const hexStr = decimalToHex(hex, 4);
|
|
671
|
+
const _values = [`0x${hexStr.slice(0, 2)}`, `0x${hexStr.slice(2, 4)}`];
|
|
672
|
+
const _command = ['0xFA', '0x05', '0x00', '0x70', '0x01', ..._values];
|
|
673
|
+
const checks = generateCrcCheckSum(_command);
|
|
674
|
+
const command = ['0xDD', '0x5A', ..._command, ...checks, '0x77'];
|
|
675
|
+
return getData(
|
|
676
|
+
deviceId,
|
|
677
|
+
{
|
|
678
|
+
command,
|
|
679
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0xfa, pkgLen: hexArr[3] + 7 }),
|
|
680
|
+
pkgVerifyHandler: (pkg) => {
|
|
681
|
+
return { verified: true };
|
|
682
|
+
},
|
|
683
|
+
},
|
|
684
|
+
'DD5A_FA'
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// #region 获取设备硬件版本号05指令
|
|
689
|
+
/** 获取设备硬件版本号05指令
|
|
690
|
+
*
|
|
691
|
+
* @param {*} deviceId
|
|
692
|
+
* @returns
|
|
693
|
+
*/
|
|
694
|
+
export const getDDA505Async = async (deviceId) => {
|
|
695
|
+
const command = ['0xdd', '0xa5', '0x05', '0x00', '0xff', '0xfb', '0x77'];
|
|
696
|
+
const response = 'DD 05 00 0A 30 31 32 33 34 35 36 37 38 39 FD E9 77';
|
|
697
|
+
|
|
698
|
+
return getData(
|
|
699
|
+
deviceId,
|
|
700
|
+
{
|
|
701
|
+
command,
|
|
702
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x05, pkgLen: hexArr[3] + 7 }),
|
|
703
|
+
pkgVerifyHandler: (pkg) => {
|
|
704
|
+
const len = pkg.length;
|
|
705
|
+
const [c1, c2] = generateCrcCheckSum(pkg.slice(2, len - 3));
|
|
706
|
+
const [_c1, _c2] = [pkg[len - 3], pkg[len - 2]];
|
|
707
|
+
return { verified: c1 == _c1 && c2 == _c2 };
|
|
708
|
+
},
|
|
709
|
+
},
|
|
710
|
+
'DDA5_05'
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
/** 解析硬件版本号05指令
|
|
714
|
+
*
|
|
715
|
+
* @param {number[]} data DDA505十六进制数据数组
|
|
716
|
+
* @returns
|
|
717
|
+
*/
|
|
718
|
+
export const resolveDDA505 = (data) => {
|
|
719
|
+
if (!data) return null;
|
|
720
|
+
const dataStr = hexArr2string(data);
|
|
721
|
+
return {
|
|
722
|
+
dataStr,
|
|
723
|
+
status: hex2string(data[2]),
|
|
724
|
+
len: data[3],
|
|
725
|
+
hardwareV: hexArr2Assic(data.slice(4, data.length - 3)),
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
// #region 新协议读参数
|
|
730
|
+
/**DDA5FA 读参数FA指令 新协议
|
|
731
|
+
* @param {string[]} paramNo 参数号 十六进制字符串数组
|
|
732
|
+
* @param {string} length 长度 十六进制字符串数组
|
|
733
|
+
* @returns
|
|
734
|
+
*/
|
|
735
|
+
export const getDDA5FAAsync = async (deviceId, paramNo, length) => {
|
|
736
|
+
const header = ['0xDD', '0xA5']
|
|
737
|
+
const _values = [...paramNo, length];
|
|
738
|
+
const lengthHex = decimalToHex(_values.length)
|
|
739
|
+
const _command = ['0xFA', lengthHex, ..._values];
|
|
740
|
+
const checks = generateCrcCheckSum(_command);
|
|
741
|
+
const command = [...header, ..._command, ...checks, '0x77'];
|
|
742
|
+
return getData(
|
|
743
|
+
deviceId,
|
|
744
|
+
{
|
|
745
|
+
command,
|
|
746
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0xFA, pkgLen: hexArr[3] + 7 }),
|
|
747
|
+
pkgVerifyHandler: (pkg) => {
|
|
748
|
+
return { verified: true };
|
|
749
|
+
},
|
|
750
|
+
},
|
|
751
|
+
'READ_DDA5_FA'
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// #region 写参数FA指令 新协议
|
|
756
|
+
/**DDA5FA 写参数FA指令 新协议
|
|
757
|
+
* @param {string[]} paramNo 参数号 十六进制字符串数组
|
|
758
|
+
* @param {string} valueLength 写入值的长度(写入几个值) 十六进制字符串数组
|
|
759
|
+
* @param {string[]} content 写入值内容(按对应序号的长度字节) 十六进制字符串数组
|
|
760
|
+
* @returns
|
|
761
|
+
*/
|
|
762
|
+
export const setDDA5FAAsync = async (deviceId, paramNo, valueLength, content) => {
|
|
763
|
+
const header = ['0xDD', '0x5A']
|
|
764
|
+
const _values = [...paramNo, valueLength, ...content];
|
|
765
|
+
const lengthHex = decimalToHex(_values.length)
|
|
766
|
+
const _command = ['0xFA', lengthHex, ..._values];
|
|
767
|
+
const checks = generateCrcCheckSum(_command);
|
|
768
|
+
const command = [...header, ..._command, ...checks, '0x77'];
|
|
769
|
+
return getData(
|
|
770
|
+
deviceId,
|
|
771
|
+
{
|
|
772
|
+
command,
|
|
773
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0xFA, pkgLen: hexArr[3] + 7 }),
|
|
774
|
+
pkgVerifyHandler: (pkg) => {
|
|
775
|
+
return { verified: true };
|
|
776
|
+
},
|
|
777
|
+
},
|
|
778
|
+
'SET_DDA5_FA'
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* 解析DDA5FA数据
|
|
783
|
+
* @param {*} data
|
|
784
|
+
* @returns
|
|
785
|
+
*/
|
|
786
|
+
export const resolveDDA5FA = (data) => {
|
|
787
|
+
if (!data) return null;
|
|
788
|
+
const dataStr = hexArr2string(data);
|
|
789
|
+
return {
|
|
790
|
+
dataStr,
|
|
791
|
+
status: hex2string(data[2]),
|
|
792
|
+
len: data[3],
|
|
793
|
+
type: data[5],
|
|
794
|
+
value: data.slice(7, -3),
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// #region 读参数旧协议
|
|
799
|
+
/** 获取读参数FA指令 旧协议
|
|
800
|
+
*
|
|
801
|
+
* @param {*} deviceId
|
|
802
|
+
* @returns
|
|
803
|
+
*/
|
|
804
|
+
export const getDDA5OldAsync = async (deviceId, path) => {
|
|
805
|
+
const values = [path, '0x00']
|
|
806
|
+
const checks = generateCrcCheckSum(values);
|
|
807
|
+
const command = ['0xDD', '0xa5', ...values, ...checks, '0x77'];
|
|
808
|
+
|
|
809
|
+
return getData(
|
|
810
|
+
deviceId,
|
|
811
|
+
{
|
|
812
|
+
command,
|
|
813
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == path, pkgLen: hexArr[3] + 7 }),
|
|
814
|
+
pkgVerifyHandler: (pkg) => {
|
|
815
|
+
const len = pkg.length;
|
|
816
|
+
const [c1, c2] = generateCrcCheckSum(pkg.slice(2, len - 3));
|
|
817
|
+
const [_c1, _c2] = [pkg[len - 3], pkg[len - 2]];
|
|
818
|
+
return { verified: c1 == _c1 && c2 == _c2 };
|
|
819
|
+
},
|
|
820
|
+
},
|
|
821
|
+
'DDA5_读旧协议读参'
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// #region 写参数旧协议
|
|
826
|
+
/**写入参数 旧协议
|
|
827
|
+
*
|
|
828
|
+
* @param {*} deviceId
|
|
829
|
+
* @param {*} path "0x17"
|
|
830
|
+
* @param {string[]} value 写入值内容 十六进制字符串数组 ["0x00,0x00"]
|
|
831
|
+
* @returns
|
|
832
|
+
*/
|
|
833
|
+
export const setDDA5OldAsync = async (deviceId, path, value, type = '') => {
|
|
834
|
+
|
|
835
|
+
const values = [path, '0x02', ...value]
|
|
836
|
+
const checks = generateCrcCheckSum(values);
|
|
837
|
+
const command = ['0xDD', '0x5A', ...values, ...checks, '0x77'];
|
|
838
|
+
|
|
839
|
+
return getData(
|
|
840
|
+
deviceId,
|
|
841
|
+
{
|
|
842
|
+
command,
|
|
843
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == path, pkgLen: hexArr[3] + 7 }),
|
|
844
|
+
pkgVerifyHandler: (pkg) => ({ verified: true }),
|
|
845
|
+
},
|
|
846
|
+
'DDA5_写旧协议读参' + type
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// #region 设置剩余容量
|
|
851
|
+
/**设置剩余容量 */
|
|
852
|
+
export const getDD5AE0Async = async (deviceId, value) => {
|
|
853
|
+
const hex = value * 100; /*AH => 10mAh*/
|
|
854
|
+
const hexStr = decimalToHex(hex, 4);
|
|
855
|
+
const _values = [`0x${hexStr.slice(0, 2)}`, `0x${hexStr.slice(2, 4)}`];
|
|
856
|
+
const _command = ['0xE0', '0x02', ..._values];
|
|
857
|
+
const checks = generateCrcCheckSum(_command);
|
|
858
|
+
// DD5AE0 02 XXXXCCCC77
|
|
859
|
+
const command = ['0xDD', '0x5A', ..._command, ...checks, '0x77'];
|
|
860
|
+
console.warn('=======CCCCCCC=SET===', { value, hex, hexStr, _values, _command, checks, command });
|
|
861
|
+
const response = 'DDE0 0000 0000 77';
|
|
862
|
+
return getData(
|
|
863
|
+
deviceId,
|
|
864
|
+
{
|
|
865
|
+
command,
|
|
866
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0xe0, pkgLen: hexArr[3] + 7 }),
|
|
867
|
+
pkgVerifyHandler: (pkg) => {
|
|
868
|
+
return { verified: true };
|
|
869
|
+
},
|
|
870
|
+
},
|
|
871
|
+
'DD5A_E0'
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// #region BMS复位
|
|
876
|
+
/** BMS复位 */
|
|
877
|
+
export const getDD5A0EAsync = async (deviceId) => {
|
|
878
|
+
const command = ['0xDD', '0x5A', '0x0E', '0x02', '0x81', '0x18', '0xFF', '0x57', '0x77'];
|
|
879
|
+
return getData(
|
|
880
|
+
deviceId,
|
|
881
|
+
{
|
|
882
|
+
command,
|
|
883
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x0e, pkgLen: hexArr[3] + 7 }),
|
|
884
|
+
pkgVerifyHandler: (pkg) => {
|
|
885
|
+
return { verified: true };
|
|
886
|
+
},
|
|
887
|
+
},
|
|
888
|
+
'DD5A_0E_复位'
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// #region 加热测试
|
|
893
|
+
/** 加热测试 */
|
|
894
|
+
export const getDD5AE4Async = async (deviceId) => {
|
|
895
|
+
const command = ['0xdd', '0x5a', '0xe4', '0x02', '0x18', '0x81', '0xfe', '0x81', '0x77'];
|
|
896
|
+
return getData(
|
|
897
|
+
deviceId,
|
|
898
|
+
{
|
|
899
|
+
command,
|
|
900
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0xe4, pkgLen: hexArr[3] + 7 }),
|
|
901
|
+
pkgVerifyHandler: (pkg) => {
|
|
902
|
+
return { verified: true };
|
|
903
|
+
},
|
|
904
|
+
},
|
|
905
|
+
'DD5A_E4'
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
// #region 5A通用写入指令
|
|
911
|
+
/**发送5A写入指令
|
|
912
|
+
*
|
|
913
|
+
* @param {*} deviceId
|
|
914
|
+
* @param {*} path 指令路径
|
|
915
|
+
* @param path 0xB0 电压校准
|
|
916
|
+
* @param path 0x0B 清除保护次数
|
|
917
|
+
* @param {*} lengthHex 内容长度
|
|
918
|
+
* @param {*} values 指令值
|
|
919
|
+
* @returns
|
|
920
|
+
*/
|
|
921
|
+
export const getDD5AAllAsync = async (deviceId, path, lengthHex, values, type) => {
|
|
922
|
+
const _command = [path, lengthHex, ...values];
|
|
923
|
+
const checks = generateCrcCheckSum(_command);
|
|
924
|
+
const command = ['0xDD', '0x5A', ..._command, ...checks, '0x77'];
|
|
925
|
+
return getData(
|
|
926
|
+
deviceId,
|
|
927
|
+
{
|
|
928
|
+
command,
|
|
929
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == path, pkgLen: hexArr[3] + 7 }),
|
|
930
|
+
pkgVerifyHandler: (pkg) => {
|
|
931
|
+
return { verified: true };
|
|
932
|
+
},
|
|
933
|
+
},
|
|
934
|
+
`DD5A_${path}_${type}`
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// #region 发送BMS指令 原始指令
|
|
939
|
+
/**发送BMS指令 原始指令
|
|
940
|
+
*
|
|
941
|
+
* @param {*} deviceId
|
|
942
|
+
* @param {*} values 原始指令值字符串
|
|
943
|
+
* @returns
|
|
944
|
+
*/
|
|
945
|
+
export const sendBMSAsync = async (deviceId, values) => {
|
|
946
|
+
// 将 values 字符串按两两字符分隔,并在每一项前添加 '0x'
|
|
947
|
+
const command = values.match(/[0-9a-fA-F]{2}/g)?.map(item => `0x${item}`) || [];
|
|
948
|
+
return getData(
|
|
949
|
+
deviceId,
|
|
950
|
+
{
|
|
951
|
+
command,
|
|
952
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == command[2], pkgLen: hexArr[3] + 7 }),
|
|
953
|
+
pkgVerifyHandler: (pkg) => {
|
|
954
|
+
return { verified: true };
|
|
955
|
+
},
|
|
956
|
+
},
|
|
957
|
+
`DD5A_${command[2]}`
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
// #region 解析BMS指令 原始指令
|
|
961
|
+
/** 解析BMS指令 原始指令
|
|
962
|
+
*
|
|
963
|
+
* @param {number[]} data DD 十六进制数据数组
|
|
964
|
+
* @returns {object} 解析后的数据 十六进制数据字符串
|
|
965
|
+
*/
|
|
966
|
+
export const resolveBMSCmd = (data) => {
|
|
967
|
+
if (!data) return null;
|
|
968
|
+
const dataStr = hexArr2string(data);
|
|
969
|
+
return {
|
|
970
|
+
dataStr,
|
|
971
|
+
status: hex2string(data[2]),
|
|
972
|
+
len: data[3],
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
// #region 保护次数
|
|
976
|
+
/**
|
|
977
|
+
* 保护次数
|
|
978
|
+
* @param {*} deviceId
|
|
979
|
+
* @returns 保护次数数组
|
|
980
|
+
*/
|
|
981
|
+
export const getProtectCountCmd = async (deviceId) => {
|
|
982
|
+
const command = ['0xdd', '0xa5', '0xaa', '0x00', '0xff', '0x56', '0x77'];
|
|
983
|
+
const data = await getData(
|
|
984
|
+
deviceId,
|
|
985
|
+
{
|
|
986
|
+
command,
|
|
987
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0xaa, pkgLen: hexArr[3] + 7 }),
|
|
988
|
+
pkgVerifyHandler: (pkg) => {
|
|
989
|
+
return { verified: true };
|
|
990
|
+
},
|
|
991
|
+
},
|
|
992
|
+
'DDA5_AA保护次数'
|
|
993
|
+
);
|
|
994
|
+
const arr = [];
|
|
995
|
+
// 使用循环来简化索引操作
|
|
996
|
+
for (let i = 4; i < data.length - 4; i += 2) {
|
|
997
|
+
const offset = i;
|
|
998
|
+
console.log('offset', offset);
|
|
999
|
+
// 优化位运算,确保位移和加法的优先级正确
|
|
1000
|
+
let value = ((data[offset] & 0xff) << 8) + (data[offset + 1] & 0xff)
|
|
1001
|
+
arr.push(value);
|
|
1002
|
+
}
|
|
1003
|
+
return arr;
|
|
1004
|
+
}
|
|
1005
|
+
// #region 清除电池循环次数
|
|
1006
|
+
/**清除电池循环次数
|
|
1007
|
+
* - 需要进/退工厂
|
|
1008
|
+
*/
|
|
1009
|
+
export const getDD5A17Async = async (deviceId) => {
|
|
1010
|
+
// DD5A17020001FFE677
|
|
1011
|
+
const command = ['0xDD', '0x5A', '0x17', '0x02', '0x00', '0x01', '0xFF', '0xE6', '0x77'];
|
|
1012
|
+
return getData(
|
|
1013
|
+
deviceId,
|
|
1014
|
+
{
|
|
1015
|
+
command,
|
|
1016
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x17, pkgLen: hexArr[3] + 7 }),
|
|
1017
|
+
pkgVerifyHandler: (pkg) => {
|
|
1018
|
+
return { verified: true };
|
|
1019
|
+
},
|
|
1020
|
+
},
|
|
1021
|
+
'DD5A_17-清除电池循环次数'
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// #region 历史运行履历
|
|
1026
|
+
/** 获取历史运行履历总数 */
|
|
1027
|
+
export const getDDA507Async = async (deviceId) => {
|
|
1028
|
+
// DD A5 07 00 FF F9 77
|
|
1029
|
+
// DD 07 00 04 00 02 00 02 FF F8 77
|
|
1030
|
+
// DD07000400020002FFF877
|
|
1031
|
+
const command = ['0xDD', '0xA5', '0x07', '0x00', '0xFF', '0xF9', '0x77'];
|
|
1032
|
+
return getData(
|
|
1033
|
+
deviceId,
|
|
1034
|
+
{
|
|
1035
|
+
command,
|
|
1036
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x07, pkgLen: hexArr[3] + 7 }),
|
|
1037
|
+
pkgVerifyHandler: (pkg) => {
|
|
1038
|
+
const len = pkg.length;
|
|
1039
|
+
const [c1, c2] = generateCrcCheckSum(pkg.slice(2, len - 3));
|
|
1040
|
+
const [_c1, _c2] = [pkg[len - 3], pkg[len - 2]];
|
|
1041
|
+
return { verified: c1 == _c1 && c2 == _c2 };
|
|
1042
|
+
},
|
|
1043
|
+
},
|
|
1044
|
+
'DDA5_07历史运行履历总数'
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
/**读取履历详情
|
|
1048
|
+
*
|
|
1049
|
+
* @param {*} deviceId
|
|
1050
|
+
* @param {*} i 当前条数
|
|
1051
|
+
* @returns
|
|
1052
|
+
*/
|
|
1053
|
+
export const getDDA508Async = async (deviceId, i = 0) => {
|
|
1054
|
+
// DD A5 08 00 FF F8 77
|
|
1055
|
+
|
|
1056
|
+
// DD08004400020000042F215B0009400500000000
|
|
1057
|
+
// 983A0A000000990B980B0000000037022802020F
|
|
1058
|
+
// 0100350237023602350233023502330233023402
|
|
1059
|
+
// 32023202320233022F022802F90E77
|
|
1060
|
+
|
|
1061
|
+
const _command = ['0x08', `0x${i.toString(16).padStart(2, 0)}`];
|
|
1062
|
+
const checks = generateCrcCheckSum(_command);
|
|
1063
|
+
|
|
1064
|
+
const command = ['0xDD', '0xA5', ..._command, ...checks, '0x77'];
|
|
1065
|
+
return getData(
|
|
1066
|
+
deviceId,
|
|
1067
|
+
{
|
|
1068
|
+
command,
|
|
1069
|
+
commandVerifyHandler: (hexArr) => ({ verified: hexArr[0] == 0xdd && hexArr[1] == 0x08, pkgLen: hexArr[3] + 7 }),
|
|
1070
|
+
pkgVerifyHandler: (pkg) => {
|
|
1071
|
+
const len = pkg.length;
|
|
1072
|
+
const [c1, c2] = generateCrcCheckSum(pkg.slice(2, len - 3));
|
|
1073
|
+
const [_c1, _c2] = [pkg[len - 3], pkg[len - 2]];
|
|
1074
|
+
return { verified: c1 == _c1 && c2 == _c2 };
|
|
1075
|
+
},
|
|
1076
|
+
},
|
|
1077
|
+
'DDA5_08读取履历详情'
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// #region 解析保护状态
|
|
1082
|
+
/** 解析保护状态 */
|
|
1083
|
+
export const resolveProtections = (binary) => {
|
|
1084
|
+
const arr = binary.split('').reverse();
|
|
1085
|
+
// "device.0041": "单体过压保护",
|
|
1086
|
+
// "device.0042": "单体欠压保护",
|
|
1087
|
+
// "device.0043": "整组过压保护",
|
|
1088
|
+
// "device.0044": "整组欠压保护",
|
|
1089
|
+
// "device.0045": "充电过温保护",
|
|
1090
|
+
// "device.0046": "充电低温保护",
|
|
1091
|
+
// "device.0047": "放电过温保护",
|
|
1092
|
+
// "device.0048": "放电低温保护",
|
|
1093
|
+
// "device.0049": "充电过流保护",
|
|
1094
|
+
// "device.0050": "放电过流保护",
|
|
1095
|
+
// "device.0051": "短路保护",
|
|
1096
|
+
// "device.0052": "前端检测IC错误",
|
|
1097
|
+
// "device.0053": "软件锁定MOS",
|
|
1098
|
+
// "device.0054": "充电MOS击穿",
|
|
1099
|
+
// "device.0055": "放电MOS击穿",
|
|
1100
|
+
// "device.00551": "PCB高温",
|
|
1101
|
+
const map = ['device.0041', 'device.0042', 'device.0043', 'device.0044', 'device.0045', 'device.0046', 'device.0047', 'device.0048', 'device.0049', 'device.0050', 'device.0051', 'device.0052', 'device.0053', 'device.0054', 'device.0055', 'device.00551'];
|
|
1102
|
+
let _arr = [];
|
|
1103
|
+
let _indexs = [];
|
|
1104
|
+
for (let i = 0; i < arr.length; i++) {
|
|
1105
|
+
if (arr[i] == 1) {
|
|
1106
|
+
_arr.push(map[i])
|
|
1107
|
+
_indexs.push(i)
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
return { _arr, _indexs };
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// #region 解析履历详情
|
|
1114
|
+
/** 解析履历详情 */
|
|
1115
|
+
export const resolveDDA508 = (data) => {
|
|
1116
|
+
if (!data) return null;
|
|
1117
|
+
// DD 08 00 44 0002 0000 04 2F21 5B0009 4005 0000 0000
|
|
1118
|
+
// 983A0A000000990B980B0000000037022802020F
|
|
1119
|
+
// 0100350237023602350233023502330233023402
|
|
1120
|
+
// 32023202320233022F022802F90E77
|
|
1121
|
+
if (!data) return null;
|
|
1122
|
+
const hexArr = data.map((o) => o.toString(16).padStart(2, '0').toUpperCase());
|
|
1123
|
+
const f = (s) => parseInt(s, 16);
|
|
1124
|
+
const fet = data[40].toString(2).padStart(8, '0').split('').reverse();
|
|
1125
|
+
const protection = f(hexArr[23] + hexArr[22])
|
|
1126
|
+
.toString(2)
|
|
1127
|
+
.padStart(16, '0');
|
|
1128
|
+
const n = !!Number(fet[7]) ? 10 : 100; // bit7(1: 100mAh 0:10mAh)
|
|
1129
|
+
let current = (hexArr[16] << 8) + (hexArr[17] & 0x0ff);
|
|
1130
|
+
current = current > 32768 ? (current - 65536) / n : current / n;
|
|
1131
|
+
const electricity = Number(current.toFixed(2));
|
|
1132
|
+
const sysTime = (data[30] * 65536) + (data[31] * 256) + data[32]; // 系统时间 分钟数
|
|
1133
|
+
console.log('hexArr[30], hexArr[31], hexArr[32]', hexArr[30], hexArr[31], hexArr[32]);
|
|
1134
|
+
|
|
1135
|
+
console.warn('sysTime: ', sysTime);
|
|
1136
|
+
const reportTime = (data[11] * 65536) + (data[12] * 256) + data[13]; // 上报时间 分钟数
|
|
1137
|
+
console.log('data[11], data[12], data[13]', data[11], data[12], data[13]);
|
|
1138
|
+
|
|
1139
|
+
console.warn('reportTime: ', reportTime);
|
|
1140
|
+
const time = Math.abs(sysTime - reportTime) * 60 * 1000; // 时间差 毫秒数 相差的时间戳
|
|
1141
|
+
console.warn('time: ', time);
|
|
1142
|
+
const timestamp = new Date().getTime() - time; // 当前系统时间减去时间差,得到历史时间戳
|
|
1143
|
+
console.warn('new Date().getTime(): ', new Date().getTime());
|
|
1144
|
+
return {
|
|
1145
|
+
dataStr: hexArr.join(''),
|
|
1146
|
+
status: hex2string(data[2]),
|
|
1147
|
+
len: data[3],
|
|
1148
|
+
total: f(hexArr[4] + hexArr[5]), // 总页数
|
|
1149
|
+
page: f(hexArr[6] + hexArr[7]), // 当前页(从0开始)
|
|
1150
|
+
type: data[8], // 1放电开始 2充电开始 3电流停止 4保护 5告警 6定时
|
|
1151
|
+
sysTime: sysTime,
|
|
1152
|
+
timestamp, // 历史时间戳
|
|
1153
|
+
sumV: (f(hexArr[15] + hexArr[14]) / 100).toFixed(2), // 总电压,单位10mV=>V
|
|
1154
|
+
sumE: electricity, // 总电流,单位A
|
|
1155
|
+
restC: (f(hexArr[19] + hexArr[18]) / n).toFixed(2), // 剩余容量,单位Ah
|
|
1156
|
+
designC: (f(hexArr[21] + hexArr[20]) / n).toFixed(2), // 剩余容量,单位Ah
|
|
1157
|
+
protections: resolveProtections(protection)._arr, // 保护标志
|
|
1158
|
+
protectionIndex: resolveProtections(protection)._indexs, // 保护标志
|
|
1159
|
+
tH: ((f(hexArr[27] + hexArr[26]) - 2731) / 10).toFixed(2), // 最高温度
|
|
1160
|
+
tL: ((f(hexArr[29] + hexArr[28]) - 2731) / 10).toFixed(2), // 最低温度
|
|
1161
|
+
vH: (f(hexArr[35] + hexArr[34]) / 1000).toFixed(3), // 最高单体电压 mv=>V
|
|
1162
|
+
vL: (f(hexArr[37] + hexArr[36]) / 1000).toFixed(3), // 最低单体电压 mv=>V
|
|
1163
|
+
nH: f(hexArr[38]), // 最高单体编号
|
|
1164
|
+
nL: f(hexArr[39]), // 最低单体编号
|
|
1165
|
+
fet: fet.join(''),
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
// #region 3B3C指令
|
|
1169
|
+
export const get3B3CInfo = ({ deviceId, macAddr, moduleType, productType }) => {
|
|
1170
|
+
return new Promise(async (resolve, reject) => {
|
|
1171
|
+
let reportData = { macAddr, moduleTypeKey: moduleType, productKey: productType };
|
|
1172
|
+
let _3b3c = null;
|
|
1173
|
+
let _dda5_03 = null;
|
|
1174
|
+
let _dda5_05 = null;
|
|
1175
|
+
let _ffaa_80 = null;
|
|
1176
|
+
try {
|
|
1177
|
+
if (!deviceId) throw new Error('deviceId required');
|
|
1178
|
+
// 发送FFAA_80
|
|
1179
|
+
const _ffaa_80_hex = await getFFAA80Async(deviceId, 'AT^VERSION?');
|
|
1180
|
+
_ffaa_80 = resolveFFAA80(_ffaa_80_hex);
|
|
1181
|
+
console.warn('Report-3B3C _ffaa_80 ', { _ffaa_80_hex, _ffaa_80 });
|
|
1182
|
+
if (_ffaa_80) reportData['moduleVersion'] = _ffaa_80?.moduleVersion;
|
|
1183
|
+
|
|
1184
|
+
// 发送3B3C
|
|
1185
|
+
const _3b3c_hex = await get3B3CAsync(deviceId);
|
|
1186
|
+
_3b3c = resolve3B3C(_3b3c_hex);
|
|
1187
|
+
console.warn('Report-3B3C _3b3c ', { _3b3c_hex, _3b3c });
|
|
1188
|
+
if (_3b3c) {
|
|
1189
|
+
reportData['cmdContent'] = _3b3c?.dataStr;
|
|
1190
|
+
} /*3B3C超时 or 不支持3B3C*/ else {
|
|
1191
|
+
// 发送DDA5_03
|
|
1192
|
+
const _dda5_03_hex = await getDDA503Async(deviceId);
|
|
1193
|
+
_dda5_03 = resolveDDA503(_dda5_03_hex);
|
|
1194
|
+
console.warn('Report-3B3C _dda5_03 ', { _dda5_03_hex, _dda5_03 });
|
|
1195
|
+
if (_dda5_03) reportData['bmsVersion'] = _dda5_03?.softwareV;
|
|
1196
|
+
|
|
1197
|
+
// 发送DDA5_05
|
|
1198
|
+
const _dda5_05_hex = await getDDA505Async(deviceId);
|
|
1199
|
+
_dda5_05 = resolveDDA505(_dda5_05_hex);
|
|
1200
|
+
console.warn('Report-3B3C _dda5_05 ', { _dda5_05_hex, _dda5_05 });
|
|
1201
|
+
if (_dda5_05) reportData['bmsSn'] = _dda5_05?.hardwareV;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
// try {
|
|
1205
|
+
// // 直接上报
|
|
1206
|
+
// const res = await serverApi.request({ url: serverApi.urls['report3B3C'], method: 'post', data: reportData }, 'json');
|
|
1207
|
+
// console.warn('Report-3B3C Report: ', { reportData, res });
|
|
1208
|
+
// } catch (error) {}
|
|
1209
|
+
resolve({ reportData, _3b3c, _dda5_03, _dda5_05, _ffaa_80 });
|
|
1210
|
+
} catch (error) {
|
|
1211
|
+
reject(error);
|
|
1212
|
+
}
|
|
1213
|
+
});
|
|
1214
|
+
}
|