fleetmap-reports 1.0.957 → 1.0.959
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/package.json +1 -1
- package/src/index.js +10 -0
- package/src/partnerReports/performance-report.js +135 -1
- package/src/tests/gpsjump.test.js +23 -0
- package/src/tests/zones.test.js +17 -0
- package/src/zone-report.js +62 -35
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -182,5 +182,15 @@ function Reports (config, axios, cookieJar) {
|
|
|
182
182
|
this.stopReportToExcel = (userData, reportData) => {
|
|
183
183
|
return require('./stop-report').exportStopReportToExcel(userData, reportData)
|
|
184
184
|
}
|
|
185
|
+
|
|
186
|
+
this.performanceReport = (from, to, userData) => {
|
|
187
|
+
return require('./partnerReports/performance-report').createPerformanceReport(from, to, userData, this.traccar)
|
|
188
|
+
}
|
|
189
|
+
this.performanceReportToPDF = (userData, reportData) => {
|
|
190
|
+
return require('./partnerReports/performance-report').exportPerformanceReportToPDF(userData, reportData)
|
|
191
|
+
}
|
|
192
|
+
this.performanceReportToExcel = (userData, reportData) => {
|
|
193
|
+
return require('./partnerReports/performance-report').exportPerformanceReportToExcel(userData, reportData)
|
|
194
|
+
}
|
|
185
195
|
}
|
|
186
196
|
module.exports = Reports
|
|
@@ -2,7 +2,11 @@ const automaticReports = require('../automaticReports')
|
|
|
2
2
|
const traccarHelper = require('../util/traccar')
|
|
3
3
|
const { getUncompletedTrip } = require('../util/trips')
|
|
4
4
|
const { getEndTripMessages, getCanDriverStyleMessages, parseEndTripMessage, parseCanDriverStyleMessage } = require('../util/xpert')
|
|
5
|
-
const { calculateDistance } = require('../util/utils')
|
|
5
|
+
const { calculateDistance, getTranslations, convertMS } = require('../util/utils')
|
|
6
|
+
const { addTable } = require('../util/pdfDocument')
|
|
7
|
+
const jsPDF = require('jspdf')
|
|
8
|
+
const { getStyle } = require('../reportStyle')
|
|
9
|
+
const { getUserPartner } = require('fleetmap-partners')
|
|
6
10
|
|
|
7
11
|
async function createPerformanceReport (from, to, userData, traccar) {
|
|
8
12
|
const reportData = []
|
|
@@ -214,4 +218,134 @@ function calculateRPMSections (route, min, max) {
|
|
|
214
218
|
return sections
|
|
215
219
|
}
|
|
216
220
|
|
|
221
|
+
function exportPerformanceReportToPDF (userData, reportData) {
|
|
222
|
+
const translations = getTranslations(userData)
|
|
223
|
+
|
|
224
|
+
const headers = [
|
|
225
|
+
translations.report.vehicle,
|
|
226
|
+
'Utilisation',
|
|
227
|
+
'Conduite',
|
|
228
|
+
'Ralenti',
|
|
229
|
+
'Totale (L)',
|
|
230
|
+
'Conduite (L)',
|
|
231
|
+
'Ralenti (L)',
|
|
232
|
+
'Conduite (Km)',
|
|
233
|
+
'Économique',
|
|
234
|
+
'Économique (L)',
|
|
235
|
+
'Économique (Km)',
|
|
236
|
+
'Cruise Control',
|
|
237
|
+
'Cruise Control (L)',
|
|
238
|
+
'Cruise Control (Km)',
|
|
239
|
+
'Tour moteur élevé',
|
|
240
|
+
'Accéleration brusque (Sum)',
|
|
241
|
+
'Freinage brusque (Sum)',
|
|
242
|
+
'Virages brusque'
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
const data = []
|
|
246
|
+
reportData.forEach(d => {
|
|
247
|
+
const row = [
|
|
248
|
+
d.device,
|
|
249
|
+
convertMS((d.drivingTime + d.idleTime) * 1000),
|
|
250
|
+
convertMS(d.drivingTime * 1000),
|
|
251
|
+
convertMS(d.idleTime * 1000),
|
|
252
|
+
d.drivingConsumption + d.idleConsumption,
|
|
253
|
+
d.drivingConsumption,
|
|
254
|
+
d.idleConsumption,
|
|
255
|
+
d.drivingDistance.toFixed(2),
|
|
256
|
+
convertMS(d.economicTime * 1000),
|
|
257
|
+
d.economicConsumption.toFixed(1),
|
|
258
|
+
d.economicDistance.toFixed(2),
|
|
259
|
+
convertMS(d.cruiseControlTime * 1000),
|
|
260
|
+
d.cruiseControlConsumption.toFixed(1),
|
|
261
|
+
d.cruiseControlDistance.toFixed(2),
|
|
262
|
+
convertMS(d.highEngineRPM * 1000),
|
|
263
|
+
d.hardAcceleration,
|
|
264
|
+
d.hardBraking,
|
|
265
|
+
d.hardCornering
|
|
266
|
+
]
|
|
267
|
+
data.push(row)
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
const doc = new jsPDF.jsPDF('l')
|
|
271
|
+
const style = getStyle(getUserPartner(userData.user))
|
|
272
|
+
|
|
273
|
+
const footValues = []
|
|
274
|
+
|
|
275
|
+
doc.setFontSize(11)
|
|
276
|
+
doc.text(new Date(userData.from).toLocaleString() + ' - ' + new Date(userData.to).toLocaleString(), 20, 33)
|
|
277
|
+
|
|
278
|
+
style.headerFontSize = 8
|
|
279
|
+
style.bodyFontSize = 7
|
|
280
|
+
const columnStyles = {}
|
|
281
|
+
for (let i = 1; i < 18; i++) {
|
|
282
|
+
columnStyles[i] = { halign: 'right' }
|
|
283
|
+
}
|
|
284
|
+
addTable(doc, headers, data, footValues, style, 40, columnStyles)
|
|
285
|
+
return doc
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function exportPerformanceReportToExcel (userData, reportData) {
|
|
289
|
+
const translations = getTranslations(userData)
|
|
290
|
+
|
|
291
|
+
const settings = {
|
|
292
|
+
sheetName: 'Rapport de Performance', // The name of the sheet
|
|
293
|
+
fileName: 'RapportPerformance' // The name of the spreadsheet
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const headers = [{ label: translations.report.vehicle, value: 'name' },
|
|
297
|
+
{ label: 'Totale Utilisation', value: 'totalUse' },
|
|
298
|
+
{ label: 'Conduite', value: 'drivingTime' },
|
|
299
|
+
{ label: 'Ralenti', value: 'idleTime' },
|
|
300
|
+
{ label: 'Totale (L)', value: 'totalConsumption' },
|
|
301
|
+
{ label: 'Conduite (L)', value: 'drivingConsumption' },
|
|
302
|
+
{ label: 'Ralenti (L)', value: 'idleConsumption' },
|
|
303
|
+
{ label: 'Conduite (Km)', value: 'drivingDistance' },
|
|
304
|
+
{ label: 'Économique', value: 'economicTime' },
|
|
305
|
+
{ label: 'Économique (L)', value: 'economicConsumption' },
|
|
306
|
+
{ label: 'Économique (Km)', value: 'economicDistance' },
|
|
307
|
+
{ label: 'Cruise Control', value: 'cruiseControlTime' },
|
|
308
|
+
{ label: 'Cruise Control (L)', value: 'cruiseControlConsumption' },
|
|
309
|
+
{ label: 'Cruise Control (Km)', value: 'cruiseControlDistance' },
|
|
310
|
+
{ label: 'Tour moteur élevé', value: 'highEngineRPM' },
|
|
311
|
+
{ label: 'Accéleration brusque (Sum)', value: 'hardAcceleration' },
|
|
312
|
+
{ label: 'Freinage brusque (Sum)', value: 'hardBraking' },
|
|
313
|
+
{ label: 'Virages brusque', value: 'hardCornering' }]
|
|
314
|
+
|
|
315
|
+
let data = []
|
|
316
|
+
data = data.concat([{}])
|
|
317
|
+
/*data = data.concat(reportData.map(d => {
|
|
318
|
+
return {
|
|
319
|
+
name: d.device,
|
|
320
|
+
totalUse: convertMS((d.drivingTime + d.idleTime) * 1000),
|
|
321
|
+
drivingTime: convertMS(d.drivingTime * 1000),
|
|
322
|
+
idleTime: convertMS(d.idleTime * 1000),
|
|
323
|
+
totalConsumption: d.drivingConsumption + d.idleConsumption,
|
|
324
|
+
drivingConsumption: d.drivingConsumption,
|
|
325
|
+
idleConsumption: d.idleConsumption,
|
|
326
|
+
drivingDistance: d.drivingDistance,
|
|
327
|
+
economicTime: convertMS(d.economicTime * 1000),
|
|
328
|
+
economicConsumption: d.economicConsumption,
|
|
329
|
+
economicDistance: d.economicDistance,
|
|
330
|
+
cruiseControlTime: convertMS(d.cruiseControlTime * 1000),
|
|
331
|
+
cruiseControlConsumption: d.cruiseControlConsumption,
|
|
332
|
+
cruiseControlDistance: d.cruiseControlDistance,
|
|
333
|
+
highEngineRPM: d.highEngineRPM,
|
|
334
|
+
hardAcceleration: d.hardAcceleration,
|
|
335
|
+
hardBraking: d.hardBraking,
|
|
336
|
+
hardCornering: d.hardCornering
|
|
337
|
+
}
|
|
338
|
+
}))*/
|
|
339
|
+
|
|
340
|
+
console.log(data)
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
headers,
|
|
344
|
+
data,
|
|
345
|
+
settings
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
217
349
|
exports.createPerformanceReport = createPerformanceReport
|
|
350
|
+
exports.exportPerformanceReportToPDF = exportPerformanceReportToPDF
|
|
351
|
+
exports.exportPerformanceReportToExcel = exportPerformanceReportToExcel
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { getReports } = require('./index')
|
|
2
|
+
const assert = require('assert')
|
|
3
|
+
const { convertMS } = require('../util/utils')
|
|
4
|
+
const { createGPSJumpReport } = require('../partnerReports/gpsjump-report')
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line no-undef
|
|
7
|
+
describe('gpsjump', function () {
|
|
8
|
+
// eslint-disable-next-line no-undef
|
|
9
|
+
it('gpsjump report', async () => {
|
|
10
|
+
const report = await getReports()
|
|
11
|
+
const userData = await report.getUserData()
|
|
12
|
+
userData.devices = userData.devices.filter(d => d.id === 22326)
|
|
13
|
+
const data = await createGPSJumpReport(
|
|
14
|
+
new Date(Date.UTC(2023, 6, 1, 0, 0, 0, 0)),
|
|
15
|
+
new Date(Date.UTC(2023, 6, 17, 23, 59, 59, 0)),
|
|
16
|
+
userData,
|
|
17
|
+
report.traccar)
|
|
18
|
+
console.log(data)
|
|
19
|
+
assert.equal(data[0].consumption, 19.799999999999997)
|
|
20
|
+
assert.equal(data[0].distance, 326.7657)
|
|
21
|
+
assert.equal(convertMS(data[1].time * 1000, false), '08:19')
|
|
22
|
+
}, 8000000)
|
|
23
|
+
})
|
package/src/tests/zones.test.js
CHANGED
|
@@ -61,4 +61,21 @@ describe('zones', function () {
|
|
|
61
61
|
assert.equal(first.days[0].distanceOut, 0)
|
|
62
62
|
assert.equal(first.days[2].distanceOut, 0)
|
|
63
63
|
}, 4000000)
|
|
64
|
+
|
|
65
|
+
it('works with casais zones in columns 2', async () => {
|
|
66
|
+
const report = await getReports()
|
|
67
|
+
const userData = await report.getUserData()
|
|
68
|
+
userData.zonesByColumn = true
|
|
69
|
+
userData.devices = userData.devices.filter(d => d.id === 81201)
|
|
70
|
+
userData.geofences = userData.geofences.filter(g => g.name === 'baliza 1 - raio verde ' ||
|
|
71
|
+
g.name === 'baliza 2 - raio amarelo')
|
|
72
|
+
userData.onlyWithKmsOut = false
|
|
73
|
+
const result = await report.zoneReport(
|
|
74
|
+
new Date(Date.UTC(2023, 7, 7, 0, 0, 0, 0)),
|
|
75
|
+
new Date(Date.UTC(2023, 7, 7, 23, 59, 59, 0)),
|
|
76
|
+
userData)
|
|
77
|
+
const first = result[0].devices[0]
|
|
78
|
+
console.log(first)
|
|
79
|
+
assert.equal(first.days[0].distanceOut, 554.6037095121401)
|
|
80
|
+
}, 4000000)
|
|
64
81
|
})
|
package/src/zone-report.js
CHANGED
|
@@ -270,6 +270,19 @@ function getAnyNextIn (outDate, alerts, deviceRoute) {
|
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
function getAnyLastExit (inDate, alerts, deviceRoute) {
|
|
274
|
+
const next = alerts
|
|
275
|
+
.filter(a => a.type === 'geofenceExit')
|
|
276
|
+
.find(a => new Date(a.position.fixTime).getTime() < inDate)
|
|
277
|
+
|
|
278
|
+
if (next && !alerts.filter(a => a.type === 'geofenceEnter' &&
|
|
279
|
+
new Date(a.position.fixTime).getTime() < inDate &&
|
|
280
|
+
new Date(a.position.fixTime).getTime() > new Date(next.position.fixTime).getTime()).length) {
|
|
281
|
+
return (next && next.position && new Date(next.position.fixTime).getTime()) ||
|
|
282
|
+
new Date(deviceRoute.slice(-1)[0].fixTime).getTime()
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
273
286
|
function calculateDistanceIn (zoneInOutDayData, deviceRoute, dayRoute, fromByDay, toByDay, to, device) {
|
|
274
287
|
let distanceIn = zoneInOutDayData.length && dayRoute.length ? zoneInOutDayData.reduce((a, b) => a + (b.distanceIn || 0), 0) : 0
|
|
275
288
|
|
|
@@ -297,7 +310,7 @@ function calculateDistanceOut (zoneInOutDayData, dayRoute, fromByDay, toByDay, f
|
|
|
297
310
|
const dataInsideDay = zoneInOutDayData.filter(z => z.outTime && new Date(z.outTime.fixTime) < toByDay)
|
|
298
311
|
let distanceOut = dataInsideDay.reduce((a, b) => a + (b[field] || 0), 0)
|
|
299
312
|
|
|
300
|
-
if (zoneInOutDayData[0].inTime && new Date(zoneInOutDayData[0].inTime.fixTime) > fromByDay) {
|
|
313
|
+
if (zoneInOutDayData[0].inTime && new Date(zoneInOutDayData[0].inTime.fixTime) > fromByDay && (field !== 'distanceOutAny' || zoneInOutDayData[0].anyLastExit)) {
|
|
301
314
|
// Add distanceOut before the first entry
|
|
302
315
|
const routeOut = dayRoute.filter(p =>
|
|
303
316
|
new Date(p.fixTime).getTime() < new Date(zoneInOutDayData[0].inTime.fixTime).getTime() &&
|
|
@@ -306,7 +319,8 @@ function calculateDistanceOut (zoneInOutDayData, dayRoute, fromByDay, toByDay, f
|
|
|
306
319
|
distanceOut = distanceOut + calculateDistance(routeOut, device.attributes['report.ignoreOdometer'])
|
|
307
320
|
}
|
|
308
321
|
|
|
309
|
-
if (zoneInOutDayData[zoneInOutDayData.length - 1].outTime && new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime) < toByDay) {
|
|
322
|
+
if (zoneInOutDayData[zoneInOutDayData.length - 1].outTime && new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime) < toByDay && (field !== 'distanceOutAny' || zoneInOutDayData[zoneInOutDayData.length - 1].anyNextIn)) {
|
|
323
|
+
// Add distanceOut after last exit
|
|
310
324
|
distanceOut = distanceOut - zoneInOutDayData[zoneInOutDayData.length - 1][field]
|
|
311
325
|
const routeOut = dayRoute.filter(p =>
|
|
312
326
|
new Date(p.fixTime) >= new Date(zoneInOutDayData[zoneInOutDayData.length - 1].outTime.fixTime).getTime() &&
|
|
@@ -322,7 +336,7 @@ function calculateDistanceOut (zoneInOutDayData, dayRoute, fromByDay, toByDay, f
|
|
|
322
336
|
|
|
323
337
|
function analyseAlerts (alerts, deviceRoute, userData, from, to, device) {
|
|
324
338
|
const zoneInOutData = []
|
|
325
|
-
|
|
339
|
+
let zoneInData = []
|
|
326
340
|
|
|
327
341
|
alerts.forEach(a => {
|
|
328
342
|
if (!a.position) {
|
|
@@ -331,7 +345,12 @@ function analyseAlerts (alerts, deviceRoute, userData, from, to, device) {
|
|
|
331
345
|
if (a.type === 'geofenceEnter') {
|
|
332
346
|
const driver = userData.drivers.find(d => a.position.attributes && d.uniqueId === a.position.attributes.driverUniqueId)
|
|
333
347
|
a.position.driverName = driver ? driver.name : ''
|
|
334
|
-
zoneInData
|
|
348
|
+
if (zoneInData.length === 0) {
|
|
349
|
+
const anyLastExist = getAnyLastExit(new Date(a.position.fixTime).getTime(), alerts, deviceRoute)
|
|
350
|
+
a.anyLastExit = anyLastExist || from
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
zoneInData.push(a)
|
|
335
354
|
} else if (a.type === 'geofenceExit') {
|
|
336
355
|
const geofence = userData.geofences.find(g => g.id === a.geofenceId)
|
|
337
356
|
if (!geofence) {
|
|
@@ -346,34 +365,41 @@ function analyseAlerts (alerts, deviceRoute, userData, from, to, device) {
|
|
|
346
365
|
const totalOutTime = nextIn - outDate
|
|
347
366
|
const distanceOut = calculateDistance(routeAfterOut, device.attributes['report.ignoreOdometer'])
|
|
348
367
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
368
|
+
let anyNextIn
|
|
369
|
+
let distanceOutAny = 0
|
|
370
|
+
if (zoneInData.length < 2) {
|
|
371
|
+
anyNextIn = getAnyNextIn(outDate, alerts, deviceRoute)
|
|
372
|
+
const routeAfterOutAndBeforeAnyNextIn = deviceRoute.filter(p =>
|
|
373
|
+
new Date(p.fixTime).getTime() >= outDate &&
|
|
374
|
+
new Date(p.fixTime).getTime() <= anyNextIn
|
|
375
|
+
)
|
|
376
|
+
distanceOutAny = zoneInData.length < 2 ? Math.round(calculateDistance(routeAfterOutAndBeforeAnyNextIn, device.attributes['report.ignoreOdometer'])) : 0
|
|
377
|
+
}
|
|
355
378
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const
|
|
379
|
+
const inData = zoneInData.find(z => z.geofenceId === a.geofenceId)
|
|
380
|
+
if (inData) {
|
|
381
|
+
const totalInTime = new Date(a.position.fixTime).getTime() - new Date(inData.position.fixTime).getTime()
|
|
382
|
+
const inDate = new Date(inData.position.fixTime).getTime()
|
|
359
383
|
const routeIn = deviceRoute.filter(p =>
|
|
360
384
|
new Date(p.fixTime).getTime() >= inDate &&
|
|
361
385
|
new Date(p.fixTime).getTime() < outDate
|
|
362
386
|
)
|
|
363
387
|
zoneInOutData.push({
|
|
364
|
-
inTime:
|
|
388
|
+
inTime: inData.position,
|
|
365
389
|
outTime: a.position,
|
|
366
390
|
totalInTime,
|
|
367
391
|
totalOutTime,
|
|
368
392
|
distanceIn: calculateDistance(routeIn, device.attributes['report.ignoreOdometer']),
|
|
369
393
|
distanceOut,
|
|
370
394
|
distanceOutAny,
|
|
395
|
+
anyNextIn,
|
|
396
|
+
anyLastExit: a.anyLastExit,
|
|
371
397
|
geofenceName: geofence.name,
|
|
372
398
|
geofenceId: geofence.id,
|
|
373
399
|
stopped: routeIn.filter(p => !p.attributes.ignition).length > 0,
|
|
374
|
-
driverName:
|
|
400
|
+
driverName: inData.position.driverName
|
|
375
401
|
})
|
|
376
|
-
|
|
402
|
+
zoneInData = zoneInData.filter(z => z.geofenceId === a.geofenceId)
|
|
377
403
|
} else {
|
|
378
404
|
const totalInTime = new Date(a.position.fixTime).getTime() - from.getTime()
|
|
379
405
|
const routeIn = deviceRoute.filter(p =>
|
|
@@ -389,35 +415,36 @@ function analyseAlerts (alerts, deviceRoute, userData, from, to, device) {
|
|
|
389
415
|
distanceIn: calculateDistance(routeIn, device.attributes['report.ignoreOdometer']),
|
|
390
416
|
driverName: '',
|
|
391
417
|
distanceOut,
|
|
392
|
-
distanceOutAny
|
|
418
|
+
distanceOutAny,
|
|
419
|
+
anyNextIn,
|
|
420
|
+
anyLastExit: a.anyLastExit
|
|
393
421
|
})
|
|
394
422
|
}
|
|
395
423
|
}
|
|
396
424
|
})
|
|
397
425
|
|
|
398
|
-
for (const
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
new Date(p.fixTime).getTime() >= new Date(zoneInData[i].position.fixTime).getTime() &&
|
|
426
|
+
for (const inData of zoneInData) {
|
|
427
|
+
const geofence = userData.geofences.find(g => g.id === inData.geofenceId)
|
|
428
|
+
if (geofence) {
|
|
429
|
+
const totalInTime = to.getTime() - new Date(inData.position.fixTime).getTime()
|
|
430
|
+
const routeIn = deviceRoute.filter(p =>
|
|
431
|
+
new Date(p.fixTime).getTime() >= new Date(inData.position.fixTime).getTime() &&
|
|
405
432
|
new Date(p.fixTime).getTime() < to.getTime()
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
433
|
+
)
|
|
434
|
+
zoneInOutData.push({
|
|
435
|
+
inTime: inData.position,
|
|
436
|
+
totalInTime,
|
|
437
|
+
distanceIn: calculateDistance(routeIn, device.attributes['report.ignoreOdometer']),
|
|
438
|
+
geofenceName: geofence.name,
|
|
439
|
+
geofenceId: geofence.id,
|
|
440
|
+
driverName: inData.position.driverName,
|
|
441
|
+
anyLastExit: inData.anyLastExit
|
|
442
|
+
})
|
|
416
443
|
}
|
|
417
444
|
}
|
|
418
445
|
|
|
419
446
|
if (!zoneInOutData.length && userData.groupByDay) {
|
|
420
|
-
const geofenceIn = alerts.find(a => a.type === '
|
|
447
|
+
const geofenceIn = alerts.find(a => a.type === 'geofenceEnter')
|
|
421
448
|
zoneInOutData.push({
|
|
422
449
|
distanceIn: geofenceIn ? calculateDistance(deviceRoute, device.attributes['report.ignoreOdometer']) : 0,
|
|
423
450
|
distanceOut: !geofenceIn ? calculateDistance(deviceRoute, device.attributes['report.ignoreOdometer']) : 0,
|