queclink-parser 1.3.20

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/src/utils.js ADDED
@@ -0,0 +1,592 @@
1
+ 'use strict'
2
+
3
+ const langEs = require('./messages/es.json')
4
+ const langEn = require('./messages/en.json')
5
+ const langs = { es: langEs, en: langEn }
6
+
7
+ /*
8
+ Data patterns
9
+ */
10
+ const patterns = {
11
+ message: /^\+RESP.+\$$/,
12
+ ack: /^\+ACK.+\$$/,
13
+ buffer: /^\+BUFF.+\$$/,
14
+ heartbeat: /^\+ACK:GTHBD.+\$$/
15
+ }
16
+
17
+ /*
18
+ Homologued devices
19
+ */
20
+ const devices = {
21
+ '52': 'GL50',
22
+ '55': 'GL50B',
23
+ '02': 'GL200',
24
+ '04': 'GV200',
25
+ '06': 'GV300',
26
+ '08': 'GMT100',
27
+ '09': 'GV50P', // GV50 Plus
28
+ '0F': 'GV55',
29
+ '10': 'GV55 LITE',
30
+ '11': 'GL500',
31
+ '1A': 'GL300',
32
+ '1F': 'GV500',
33
+ '25': 'GV300', // New Version
34
+ '35': 'GV200', // New Version
35
+ '27': 'GV300W',
36
+ '2F': 'GV55', // New Version
37
+ '30': 'GL300', // New Version
38
+ '36': 'GV500', // New Version
39
+ '2C': 'GL300W', // New version
40
+ '3F': 'GMT100', // New version
41
+ F8: 'GV800W',
42
+ '41': 'GV75W',
43
+ FC: 'GV600W'
44
+ }
45
+
46
+ /*
47
+ Possible device's motions states
48
+ */
49
+ const states = {
50
+ '16': 'Tow',
51
+ '1A': 'Fake Tow',
52
+ '11': 'Ignition Off Rest',
53
+ '12': 'Ignition Off Moving',
54
+ '21': 'Ingition On Rest',
55
+ '22': 'Ignition On Moving',
56
+ '41': 'Sensor Rest',
57
+ '42': 'Sensor Motion',
58
+ '': 'Unknown'
59
+ }
60
+
61
+ /*
62
+ Possible OBDII Protoccols
63
+ */
64
+ const OBDIIProtocols = {
65
+ '0': 'Unknown',
66
+ '1': 'J1850 PWM',
67
+ '2': 'J1850 VPW',
68
+ '3': 'ISO 9141-2',
69
+ '4': 'ISO 14230',
70
+ '5': 'ISO 14230',
71
+ '6': 'ISO 15765',
72
+ '7': 'ISO 15765',
73
+ '8': 'ISO 15765',
74
+ '9': 'ISO 15765',
75
+ A: 'J1939'
76
+ }
77
+
78
+ /*
79
+ Uart Devices
80
+ */
81
+ const uartDeviceTypes = {
82
+ '0': 'No device',
83
+ '1': 'Digit Fuel Sensor',
84
+ '2': 'AC100 1 Wire Bus'
85
+ }
86
+
87
+ /*
88
+ Gets the Queclink Device Type
89
+ */
90
+ const getDevice = raw => {
91
+ raw = raw.substr(0, raw.length - 1)
92
+ const parsedData = raw.split(',')
93
+ const protocol = getProtocolVersion(parsedData[1])
94
+ return protocol.deviceType
95
+ }
96
+
97
+ /*
98
+ Gets the protocol version
99
+ */
100
+ const getProtocolVersion = protocol => {
101
+ return {
102
+ raw: protocol,
103
+ deviceType: devices.hasOwnProperty(protocol.substring(0, 2))
104
+ ? devices[protocol.substring(0, 2)]
105
+ : null,
106
+ version: `${parseInt(protocol.substring(2, 4), 16)}.${parseInt(
107
+ protocol.substring(4, 6),
108
+ 16
109
+ )}`
110
+ }
111
+ }
112
+
113
+ /*
114
+ Checks if the location has a valid gps position
115
+ */
116
+ const checkGps = (lng, lat) => {
117
+ // loc: { type: 'Point', coordinates: [ parseFloat(parsedData[11]), parseFloat(parsedData[12]) ] },
118
+ if (lng !== 0 && lat !== 0 && !isNaN(lng) && !isNaN(lat)) {
119
+ return true
120
+ }
121
+ return false
122
+ }
123
+
124
+ /*
125
+ Gets the temperature from AC100 device in celcious degrees
126
+ */
127
+ const getTempInCelciousDegrees = hexTemp => {
128
+ if (hexTemp.substring(0, 4) === 'FFFF') {
129
+ hexTemp = hexTemp.substring(4)
130
+ }
131
+ const binTemp = nHexDigit(hex2bin(hexTemp), 16)
132
+ if (binTemp.substring(0, 5) === '11111') {
133
+ // Negative value
134
+ return (parseInt('FFFF', 16) - parseInt(hexTemp, 16) + 1) * -0.0625
135
+ }
136
+ return parseFloat(hex2dec(hexTemp)) * 0.0625
137
+ }
138
+
139
+ /*
140
+ Gets fuel consumption from string
141
+ */
142
+ const getFuelConsumption = fuelString => {
143
+ try {
144
+ if (
145
+ fuelString.indexOf('NaN') === -1 &&
146
+ fuelString.indexOf('Inf') === -1 &&
147
+ fuelString.indexOf('inf') === -1
148
+ ) {
149
+ return parseFloat(fuelString)
150
+ } else {
151
+ return null
152
+ }
153
+ } catch (e) {
154
+ return null
155
+ }
156
+ }
157
+
158
+ /*
159
+ Returns hormeter in hours from string hourmeter
160
+ in format HHHHH:MM:SS
161
+ */
162
+ const getHoursForHourmeter = hourmeter => {
163
+ try {
164
+ const hours = parseInt(hourmeter.split(':')[0], 10)
165
+ const minutes = parseInt(hourmeter.split(':')[1], 10)
166
+ const seconds = parseInt(hourmeter.split(':')[2], 10)
167
+ return hours + (minutes + seconds / 60) / 60
168
+ } catch (e) {
169
+ return null
170
+ }
171
+ }
172
+
173
+ /*
174
+ Gets the alarm type
175
+ */
176
+ const getAlarm = (command, report, extra = false) => {
177
+ const messages = langs['es']
178
+ if (
179
+ command === 'GTFRI' ||
180
+ command === 'GTERI' ||
181
+ command === 'GTPNL' ||
182
+ command === 'GTPFL' ||
183
+ command === 'GTSTR' ||
184
+ command === 'GTCTN'
185
+ ) {
186
+ return { type: 'Gps' }
187
+ } else if (command === 'GTCAN') {
188
+ const reportType = parseInt(report, 10)
189
+ return {
190
+ type: 'Gps',
191
+ status: 'CAN_Bus',
192
+ report: reportType
193
+ // message: messages[command].replace('data', reportType)
194
+ }
195
+ } else if (command === 'GTOBD') {
196
+ return { type: 'Gps', status: 'OBDII' }
197
+ } else if (command === 'GTJES') {
198
+ return { type: 'OBDII_Summary', message: messages[command] }
199
+ } else if (command === 'GTOSM') {
200
+ const reportID = report[0]
201
+ const reportType = report[1]
202
+ return {
203
+ type: 'OBDII_Monitor',
204
+ status: reportType === '0' ? 'Inside' : 'Outside',
205
+ message: messages[command][reportID][reportType]
206
+ }
207
+ } else if (command === 'GTOPN') {
208
+ return { type: 'OBDII_Connected', status: true, message: messages[command] }
209
+ } else if (command === 'GTOPF') {
210
+ return {
211
+ type: 'OBDII_Connected',
212
+ status: false,
213
+ message: messages[command]
214
+ }
215
+ } else if (command === 'GTRTL') {
216
+ return { type: 'Gps', status: 'Requested' }
217
+ } else if (command === 'GTGSM') {
218
+ return {
219
+ type: 'GSM_Report',
220
+ message: messages[command]
221
+ }
222
+ } else if (command === 'GTINF') {
223
+ return { type: 'General_Info_Report' }
224
+ } else if (command === 'GTDIS') {
225
+ let reportID = parseInt(report[0], 10)
226
+ const reportType = parseInt(report[1], 10)
227
+ if (extra === true && reportID === 1) {
228
+ reportID = 2
229
+ } else if (
230
+ ['gv800w', 'gv600w', 'gv300w', 'gv75w', 'GMT100'].includes(extra)
231
+ ) {
232
+ reportID += 1
233
+ }
234
+ return {
235
+ type: 'DI',
236
+ number: reportID,
237
+ status: reportType === 1,
238
+ message: messages[command][reportType].replace('port', reportID)
239
+ }
240
+ } else if (command === 'GTNMR') {
241
+ const reportType = report[1]
242
+ return {
243
+ type: 'Movement',
244
+ status: reportType === '1',
245
+ message: messages[command][reportType]
246
+ }
247
+ } else if (command === 'GTTOW') {
248
+ return { type: 'Towing', message: messages[command] }
249
+ } else if (command === 'GTSOS') {
250
+ return { type: 'SOS_Button', message: messages[command] }
251
+ } else if (command === 'GTSPD') {
252
+ const reportType = parseInt(report[1], 10)
253
+ return {
254
+ type: 'Over_Speed',
255
+ status: reportType === 0,
256
+ message: messages[command][reportType]
257
+ }
258
+ } else if (command === 'GTIGL') {
259
+ const reportType = parseInt(report[1], 16)
260
+ return {
261
+ type: 'DI',
262
+ number: 1,
263
+ status: reportType === 0,
264
+ message: messages[command][reportType === 0 ? '1' : '0']
265
+ }
266
+ } else if (command === 'GTIGN') {
267
+ const duration = report !== '' ? parseInt(report, 10) : null
268
+ return {
269
+ type: 'DI',
270
+ number: 1,
271
+ status: true,
272
+ duration: duration,
273
+ message: messages[command]
274
+ }
275
+ } else if (command === 'GTIGF') {
276
+ const duration = report !== '' ? parseInt(report, 10) : null
277
+ return {
278
+ type: 'DI',
279
+ number: 1,
280
+ status: false,
281
+ duration: duration,
282
+ message: messages[command]
283
+ }
284
+ } else if (command === 'GTPNA') {
285
+ return { type: 'Power', status: true, message: messages[command] }
286
+ } else if (command === 'GTPFA') {
287
+ return { type: 'Power', status: false, message: messages[command] }
288
+ } else if (command === 'GTMPN' || command === 'GTEPN') {
289
+ // Change for connected to power supply
290
+ return { type: 'Charge', status: true, message: messages[command] }
291
+ } else if (command === 'GTMPF' || command === 'GTEPF') {
292
+ return { type: 'Charge', status: false, message: messages[command] }
293
+ } else if (command === 'GTBTC') {
294
+ return { type: 'Charging', status: true, message: messages[command] }
295
+ } else if (command === 'GTSTC') {
296
+ return { type: 'Charging', status: false, message: messages[command] }
297
+ } else if (command === 'GTBPL') {
298
+ return { type: 'Low_Battery', message: messages[command] }
299
+ } else if (command === 'GTIDN') {
300
+ return { type: 'Idling', status: true, message: messages[command] }
301
+ } else if (command === 'GTIDF') {
302
+ const duration = report !== '' ? parseInt(report, 10) : null
303
+ return {
304
+ type: 'Idling',
305
+ status: false,
306
+ duration: duration,
307
+ message: messages[command]
308
+ }
309
+ } else if (command === 'GTJDR') {
310
+ return {
311
+ type: 'Jamming',
312
+ status: true,
313
+ gps: false,
314
+ message: messages[command]
315
+ }
316
+ } else if (command === 'GTJDS') {
317
+ return {
318
+ type: 'Jamming',
319
+ status: report === '2',
320
+ gps: false,
321
+ message: messages[command][report]
322
+ }
323
+ } else if (command === 'GTGPJ') {
324
+ // GPS Jamming
325
+ return {
326
+ type: 'Jamming',
327
+ status: report === '3',
328
+ gps: true,
329
+ message: messages[command][report]
330
+ }
331
+ } else if (command === 'GTEPS') {
332
+ return { type: 'External_Low_battery', message: messages[command] }
333
+ } else if (command === 'GTAIS' || command === 'GTMAI') {
334
+ const reportID = parseInt(report[0], 10)
335
+ const reportType = parseInt(report[1], 10)
336
+ if (reportID === 2) {
337
+ return { type: 'SOS_Button', message: messages[command][reportID] }
338
+ }
339
+ return { type: 'AI', number: reportID, status: reportType === '0' }
340
+ } else if (command === 'GTANT') {
341
+ return {
342
+ type: 'GPS_Antena',
343
+ status: report === '0',
344
+ message: messages[command][report]
345
+ }
346
+ // } else if (command === 'GTSTR') {
347
+ // return {
348
+ // type: 'Vehicle_Start_Status',
349
+ // status: true,
350
+ // message: messages[command]
351
+ // }
352
+ } else if (command === 'GTSTP' || command === 'GTLSP') {
353
+ return {
354
+ type: 'Vehicle_Start_Status',
355
+ status: false,
356
+ message: messages[command]
357
+ }
358
+ } else if (command === 'GTRMD') {
359
+ return {
360
+ type: 'Roaming',
361
+ status: report === '1',
362
+ message: messages[command][report]
363
+ }
364
+ } else if (command === 'GTHBD') {
365
+ return { type: 'Heartbeat', message: messages[command] }
366
+ } else if (command === 'GTSTT') {
367
+ return { type: 'Motion_State_Changed', message: messages[command] }
368
+ } else if (command === 'GTPDP') {
369
+ return { type: 'GPRS_Connection_Established', message: messages[command] }
370
+ } else if (command === 'GTGSS') {
371
+ return {
372
+ type: 'Gps_Status',
373
+ status: report === '1',
374
+ message: messages[command][typeof report !== 'undefined' ? report : '0']
375
+ }
376
+ } else if (command === 'GTTMP') {
377
+ const number = parseInt(report[0], 10)
378
+ const temperature = extra[1] !== '' ? parseFloat(extra[1]) : null
379
+ return {
380
+ type: 'Outside_Temperature',
381
+ number: number,
382
+ deviceID: extra[0],
383
+ status: report[1] === '0', // 0 means outside the range, 1 means inside
384
+ temperature: temperature,
385
+ message: messages[command][report[1]].replace('()', `(${temperature}°C)`)
386
+ }
387
+ } else if (command === 'GTFLA') {
388
+ const before =
389
+ report.split(',')[0] !== null ? parseInt(report.split(',')[0], 10) : 0
390
+ const now =
391
+ report.split(',')[1] !== null ? parseInt(report.split(',')[1], 10) : 0
392
+ const consumption = before - now
393
+ return {
394
+ type: 'Unusual_Fuel_Consumption',
395
+ status: consumption,
396
+ message: messages[command].replace('consumption', consumption)
397
+ }
398
+ } else if (command === 'GTIDA') {
399
+ const status =
400
+ report.split(',')[1] !== null ? parseInt(report.split(',')[1], 10) : null
401
+ const driverID = report.split(',')[0] !== null ? report.split(',')[0] : null
402
+ return {
403
+ type: 'Driver_Identification',
404
+ status: status === 1,
405
+ driverID: driverID,
406
+ message: messages[command][status]
407
+ }
408
+ } else if (command === 'GTDOS') {
409
+ const outputId = report.split(',')[0]
410
+ ? parseInt(report.split(',')[0], 10)
411
+ : null
412
+ const outputStatus = report.split(',')[0] ? report.split(',')[1] : null
413
+ return {
414
+ type: 'DO',
415
+ number: outputId,
416
+ status: outputStatus === '1',
417
+ message: messages[command][outputStatus].replace('port', outputId)
418
+ }
419
+ } else if (command === 'GTDAT') {
420
+ return {
421
+ type: 'Serial_Data',
422
+ data: report,
423
+ message: messages[command]
424
+ }
425
+ } else if (command === 'GTDTT') {
426
+ return {
427
+ type: 'Transparent_Data',
428
+ dataType: extra,
429
+ data: report,
430
+ message: messages[command]
431
+ }
432
+ } else if (command === 'GTSOA') {
433
+ return {
434
+ type: 'Shell_Open',
435
+ message: messages[command]
436
+ }
437
+ } else if (command === 'GTNMD') {
438
+ return {
439
+ type: 'Movement',
440
+ status: report === '0',
441
+ message: messages[command][report]
442
+ }
443
+ } else if (command === 'GTHBM') {
444
+ /*
445
+ status:
446
+ 0: braking
447
+ 1: acceleration
448
+ 2: turning
449
+ 3: braking turning
450
+ 4: acceleration turning
451
+ 5: unknown harsh behavior
452
+ */
453
+ // const reportID = parseInt(report[0], 10)
454
+ const reportType = report[1]
455
+ return {
456
+ type: 'Harsh_Behavior',
457
+ status: parseInt(reportType, 10),
458
+ message: messages[command][reportType]
459
+ }
460
+ } else if (command === 'GTCRA') {
461
+ return {
462
+ type: 'Crash',
463
+ status: true,
464
+ counter: report,
465
+ message: messages[command]
466
+ }
467
+ } else if (command === 'GTGEO') {
468
+ return {
469
+ type: 'Device_Geofence'
470
+ }
471
+ } else {
472
+ return {
473
+ type: command,
474
+ message: messages[command] ? messages[command] : ''
475
+ }
476
+ }
477
+ }
478
+
479
+ /*
480
+ Converst num to base number
481
+ */
482
+ const ConvertBase = num => {
483
+ return {
484
+ from: function (baseFrom) {
485
+ return {
486
+ to: function (baseTo) {
487
+ return parseInt(num, baseFrom).toString(baseTo)
488
+ }
489
+ }
490
+ }
491
+ }
492
+ }
493
+
494
+ /*
495
+ Converts binary to decimal number
496
+ */
497
+ const bin2dec = num => {
498
+ return ConvertBase(num)
499
+ .from(2)
500
+ .to(10)
501
+ }
502
+
503
+ /*
504
+ Converts binary to hexadecimal number
505
+ */
506
+ const bin2hex = num => {
507
+ return ConvertBase(num)
508
+ .from(2)
509
+ .to(16)
510
+ }
511
+
512
+ /*
513
+ Converts decimal to binary number
514
+ */
515
+ const dec2bin = num => {
516
+ return ConvertBase(num)
517
+ .from(10)
518
+ .to(2)
519
+ }
520
+
521
+ /*
522
+ Converts decimal to hexadecimal number
523
+ */
524
+ const dec2hex = num => {
525
+ return ConvertBase(num)
526
+ .from(10)
527
+ .to(16)
528
+ }
529
+
530
+ /*
531
+ Converts hexadecimal to binary number
532
+ */
533
+ const hex2bin = num => {
534
+ return ConvertBase(num)
535
+ .from(16)
536
+ .to(2)
537
+ }
538
+
539
+ /*
540
+ Converts hexadecimal to decimal number
541
+ */
542
+ const hex2dec = num => {
543
+ return ConvertBase(num)
544
+ .from(16)
545
+ .to(10)
546
+ }
547
+
548
+ /*
549
+ Return hexadecimal number with n digits
550
+ */
551
+ const nHexDigit = (num, n) => {
552
+ let hex = num
553
+ while (hex.length < n) {
554
+ hex = `0${hex}`
555
+ }
556
+ return hex
557
+ }
558
+
559
+ /*
560
+ Parses date
561
+ */
562
+ const parseDate = date => {
563
+ return new Date(
564
+ `${date.replace(
565
+ /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/,
566
+ '$1-$2-$3T$4:$5:$6'
567
+ )}+00:00`
568
+ )
569
+ }
570
+
571
+ module.exports = {
572
+ langs: langs,
573
+ patterns: patterns,
574
+ OBDIIProtocols: OBDIIProtocols,
575
+ states: states,
576
+ uartDeviceTypes: uartDeviceTypes,
577
+ getDevice: getDevice,
578
+ getProtocolVersion: getProtocolVersion,
579
+ checkGps: checkGps,
580
+ getTempInCelciousDegrees: getTempInCelciousDegrees,
581
+ getFuelConsumption: getFuelConsumption,
582
+ getHoursForHourmeter: getHoursForHourmeter,
583
+ getAlarm: getAlarm,
584
+ bin2dec: bin2dec,
585
+ bin2hex: bin2hex,
586
+ dec2bin: dec2bin,
587
+ dec2hex: dec2hex,
588
+ hex2bin: hex2bin,
589
+ hex2dec: hex2dec,
590
+ nHexDigit: nHexDigit,
591
+ parseDate: parseDate
592
+ }