fleetmap-reports 1.0.412 → 1.0.415
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/idle-report.js +14 -45
- package/src/index.test.js +2 -2
- package/src/trip-report.js +28 -24
- package/src/util/geofence.js +10 -9
- package/src/util/route.js +42 -0
- package/src/util/trips.js +154 -150
- package/src/zone-report.js +11 -13
package/package.json
CHANGED
package/src/idle-report.js
CHANGED
|
@@ -6,6 +6,7 @@ const { getUserPartner } = require('fleetmap-partners')
|
|
|
6
6
|
const { devicesToProcess } = require('./util/device')
|
|
7
7
|
const automaticReports = require('./automaticReports')
|
|
8
8
|
const traccarHelper = require('./util/traccar')
|
|
9
|
+
const { getIdleEvents } = require('./util/route')
|
|
9
10
|
|
|
10
11
|
const fileName = 'IdleReport'
|
|
11
12
|
|
|
@@ -41,7 +42,11 @@ async function createIdleReportByDriver (from, to, userData, traccarInstance) {
|
|
|
41
42
|
|
|
42
43
|
const { route } = await traccarHelper.getAllInOne(traccarInstance, from, to, devices, true, false, false, false)
|
|
43
44
|
|
|
44
|
-
return {
|
|
45
|
+
return {
|
|
46
|
+
drivers: processDrivers(from, to, userData, route),
|
|
47
|
+
from,
|
|
48
|
+
to
|
|
49
|
+
}
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
async function createIdleReportByGroup (from, to, userData, traccarInstance) {
|
|
@@ -107,17 +112,21 @@ async function createIdleReportByDevice (from, to, userData, traccarInstance) {
|
|
|
107
112
|
function processDrivers (from, to, userData, routes) {
|
|
108
113
|
const driversResult = []
|
|
109
114
|
userData.drivers.forEach(d => {
|
|
110
|
-
const route = routes.filter(p => p.attributes.driverUniqueId === d.uniqueId
|
|
115
|
+
const route = routes.filter(p => p.attributes.driverUniqueId === d.uniqueId)
|
|
111
116
|
|
|
112
|
-
const idleEvents = getIdleEvents(route)
|
|
113
|
-
console.log(idleEvents)
|
|
114
117
|
if (route.length > 0) {
|
|
118
|
+
const idleEvents = getIdleEvents(routes, d)
|
|
115
119
|
const filteredEvents = idleEvents.filter(e => userData.minimumIdleMinutes ? e.idleTime > userData.minimumIdleMinutes * 60 * 1000 : true)
|
|
116
120
|
|
|
117
121
|
if (filteredEvents.length) {
|
|
122
|
+
filteredEvents.forEach(e => {
|
|
123
|
+
const device = userData.devices.find(d => d.id === e.position.deviceId)
|
|
124
|
+
e.deviceName = device ? device.name : ''
|
|
125
|
+
})
|
|
126
|
+
|
|
118
127
|
const driverData = {
|
|
119
128
|
driver: d,
|
|
120
|
-
idleEvents: filteredEvents
|
|
129
|
+
idleEvents: filteredEvents.sort((a, b) => (new Date(a.position.fixTime) > new Date(b.position.fixTime)) ? 1 : -1)
|
|
121
130
|
}
|
|
122
131
|
driversResult.push(driverData)
|
|
123
132
|
}
|
|
@@ -149,46 +158,6 @@ function processDevices (from, to, devices, routes, userData) {
|
|
|
149
158
|
return devicesResult
|
|
150
159
|
}
|
|
151
160
|
|
|
152
|
-
function getIdleEvents (route) {
|
|
153
|
-
const idleEvents = []
|
|
154
|
-
|
|
155
|
-
const routeByDevice = route.reduce(function (a, x) {
|
|
156
|
-
(a[x.deviceId] = a[x.deviceId] || []).push(x)
|
|
157
|
-
return a
|
|
158
|
-
}, {})
|
|
159
|
-
|
|
160
|
-
Object.keys(routeByDevice).forEach(function (key) {
|
|
161
|
-
let inIdle = false
|
|
162
|
-
routeByDevice[key].forEach(p => {
|
|
163
|
-
if (p.attributes.ignition && p.speed === 0) {
|
|
164
|
-
if (!inIdle) {
|
|
165
|
-
const idleEvent = {
|
|
166
|
-
position: p,
|
|
167
|
-
idleTime: 0
|
|
168
|
-
}
|
|
169
|
-
idleEvents.push(idleEvent)
|
|
170
|
-
inIdle = true
|
|
171
|
-
} else {
|
|
172
|
-
if (!idleEvents[idleEvents.length - 1].position.attributes.driverUniqueId) {
|
|
173
|
-
idleEvents[idleEvents.length - 1].position.attributes.driverUniqueId = p.attributes.driverUniqueId
|
|
174
|
-
}
|
|
175
|
-
if (p.attributes.idleTime) {
|
|
176
|
-
idleEvents[idleEvents.length - 1].idleTime = p.attributes.idleTime
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
} else if (inIdle) {
|
|
180
|
-
const currentIdleEvent = idleEvents[idleEvents.length - 1]
|
|
181
|
-
if (p.attributes.idleTime === undefined) {
|
|
182
|
-
currentIdleEvent.idleTime = new Date(p.fixTime) - new Date(currentIdleEvent.position.fixTime)
|
|
183
|
-
}
|
|
184
|
-
inIdle = false
|
|
185
|
-
}
|
|
186
|
-
})
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
return idleEvents
|
|
190
|
-
}
|
|
191
|
-
|
|
192
161
|
async function exportIdleReportToPDF (userData, reportData) {
|
|
193
162
|
console.log('Export to PDF')
|
|
194
163
|
|
package/src/index.test.js
CHANGED
|
@@ -208,8 +208,8 @@ describe('Test_Reports', function () {
|
|
|
208
208
|
assert.equal(data.length, 1)
|
|
209
209
|
const driver = data[0].drivers.find(d => d.driver.id === 14020)
|
|
210
210
|
const totalIdleTime = driver.idleEvents.reduce((a, b) => a + b.idleTime, 0)
|
|
211
|
-
assert.equal(driver.idleEvents.length,
|
|
212
|
-
assert.equal(totalIdleTime,
|
|
211
|
+
assert.equal(driver.idleEvents.length, 1) // Total Alerts
|
|
212
|
+
assert.equal(totalIdleTime, 8267000) // Total Duration
|
|
213
213
|
}, 20000)
|
|
214
214
|
// eslint-disable-next-line no-undef
|
|
215
215
|
it('Activity by device', async () => {
|
package/src/trip-report.js
CHANGED
|
@@ -7,8 +7,9 @@ const { headerFromUser, addTable } = require('./util/pdfDocument')
|
|
|
7
7
|
const { getStyle } = require('./reportStyle')
|
|
8
8
|
const traccarHelper = require('./util/traccar')
|
|
9
9
|
const trips = require('./util/trips')
|
|
10
|
-
const { isInsideTimetable, addNearestPOIs, isPartialInsideTimetable, calculateTrip } = require('./util/trips')
|
|
10
|
+
const { isInsideTimetable, addNearestPOIs, isPartialInsideTimetable, calculateTrip, getTripIdleTime } = require('./util/trips')
|
|
11
11
|
const { devicesToProcess } = require('./util/device')
|
|
12
|
+
const { getIdleEvents } = require('./util/route')
|
|
12
13
|
|
|
13
14
|
const fileName = 'TripReport'
|
|
14
15
|
|
|
@@ -192,6 +193,13 @@ function processDrivers (from, to, userData, data) {
|
|
|
192
193
|
|
|
193
194
|
trips.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime())
|
|
194
195
|
|
|
196
|
+
const idleEvents = getIdleEvents(data.route, d)
|
|
197
|
+
|
|
198
|
+
trips.forEach(function (trip, i) {
|
|
199
|
+
trip.stopEngineHours = getTripIdleTime(trip, idleEvents)
|
|
200
|
+
trip.stopDuration = i !== trips.length - 1 ? (new Date(trips[i + 1].startTime) - new Date(trip.endTime)) : 0
|
|
201
|
+
})
|
|
202
|
+
|
|
195
203
|
addNearestPOIs(trips, userData)
|
|
196
204
|
|
|
197
205
|
if (trips.length > 0) {
|
|
@@ -229,7 +237,9 @@ async function exportTripReportToPDF (userData, reportData) {
|
|
|
229
237
|
]
|
|
230
238
|
|
|
231
239
|
if (userData.byDriver) {
|
|
232
|
-
headers.push(translations.report.
|
|
240
|
+
headers.push(translations.report.idleTime,
|
|
241
|
+
translations.report.stopTime,
|
|
242
|
+
translations.report.distance,
|
|
233
243
|
translations.report.avgSpeed,
|
|
234
244
|
translations.report.maxSpeed,
|
|
235
245
|
translations.report.name)
|
|
@@ -276,7 +286,7 @@ async function exportTripReportToPDF (userData, reportData) {
|
|
|
276
286
|
doc.text(translations.report.headerStartAddress + ': ' + d.trips[0].startAddress, 20, space + 35)
|
|
277
287
|
}
|
|
278
288
|
|
|
279
|
-
d.trips.
|
|
289
|
+
d.trips.forEach(a => {
|
|
280
290
|
const temp = [
|
|
281
291
|
getTripDate(userData.user, a),
|
|
282
292
|
getTripStart(userData.user, a),
|
|
@@ -285,18 +295,16 @@ async function exportTripReportToPDF (userData, reportData) {
|
|
|
285
295
|
convertMS(a.duration)
|
|
286
296
|
]
|
|
287
297
|
|
|
298
|
+
temp.push(convertMS(a.stopEngineHours),
|
|
299
|
+
convertMS(a.stopDuration),
|
|
300
|
+
Intl.NumberFormat(userData.user.attributes.lang, { maximumFractionDigits: 2 }).format(a.totalKms),
|
|
301
|
+
Math.round(a.averageSpeed * 1.85200),
|
|
302
|
+
Math.round(a.maxSpeed * 1.85200))
|
|
303
|
+
|
|
288
304
|
if (userData.byDriver) {
|
|
289
|
-
temp.push(
|
|
290
|
-
Math.round(a.averageSpeed * 1.85200),
|
|
291
|
-
Math.round(a.maxSpeed * 1.85200),
|
|
292
|
-
a.deviceName)
|
|
305
|
+
temp.push(a.deviceName)
|
|
293
306
|
} else {
|
|
294
|
-
temp.push(
|
|
295
|
-
convertMS(a.stopDuration),
|
|
296
|
-
Intl.NumberFormat(userData.user.attributes.lang, { maximumFractionDigits: 2 }).format(a.totalKms),
|
|
297
|
-
Math.round(a.averageSpeed * 1.85200),
|
|
298
|
-
Math.round(a.maxSpeed * 1.85200),
|
|
299
|
-
getDriverName(userData.drivers, a.driverUniqueId))
|
|
307
|
+
temp.push(getDriverName(userData.drivers, a.driverUniqueId))
|
|
300
308
|
}
|
|
301
309
|
|
|
302
310
|
data.push(temp)
|
|
@@ -307,17 +315,11 @@ async function exportTripReportToPDF (userData, reportData) {
|
|
|
307
315
|
convertMS(d.trips.reduce((a, b) => a + b.duration, 0))
|
|
308
316
|
]
|
|
309
317
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
footValues.push(convertMS(d.trips.reduce((a, b) => a + b.stopEngineHours, 0)),
|
|
316
|
-
convertMS(d.trips.reduce((a, b) => a + b.stopDuration, 0)),
|
|
317
|
-
getSumTotalKms(userData.user, d.trips),
|
|
318
|
-
getSumAvgSpeed(d.trips),
|
|
319
|
-
getMaxSpeed(d.trips))
|
|
320
|
-
}
|
|
318
|
+
footValues.push(convertMS(d.trips.reduce((a, b) => a + b.stopEngineHours, 0)),
|
|
319
|
+
convertMS(d.trips.reduce((a, b) => a + b.stopDuration, 0)),
|
|
320
|
+
getSumTotalKms(userData.user, d.trips),
|
|
321
|
+
getSumAvgSpeed(d.trips),
|
|
322
|
+
getMaxSpeed(d.trips))
|
|
321
323
|
|
|
322
324
|
const style = getStyle(getUserPartner(userData.user))
|
|
323
325
|
addTable(doc, headers, data, footValues, style, space + (userData.allWeek ? 40 : 45))
|
|
@@ -345,6 +347,8 @@ function exportTripReportToExcel (userData, reportData) {
|
|
|
345
347
|
{ label: translations.report.end, value: 'end' },
|
|
346
348
|
{ label: translations.report.endAddress, value: 'endAddress' },
|
|
347
349
|
{ label: translations.report.tripTime, value: 'tripTime' },
|
|
350
|
+
{ label: translations.report.idleTime, value: 'idleTime' },
|
|
351
|
+
{ label: translations.report.stopTime, value: 'stopTime' },
|
|
348
352
|
{ label: translations.report.distance, value: 'distance' },
|
|
349
353
|
{ label: translations.report.avgSpeed, value: 'averageSpeed' },
|
|
350
354
|
{ label: translations.report.maxSpeed, value: 'maxSpeed' },
|
package/src/util/geofence.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
const turf = require('turf')
|
|
1
|
+
const turf = require('@turf/helpers')
|
|
2
|
+
const inside = require('@turf/boolean-point-in-polygon')
|
|
2
3
|
|
|
3
|
-
exports.insideGeofence = function insideGeofence(position, geofence) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
exports.insideGeofence = function insideGeofence (position, geofence) {
|
|
5
|
+
const coords = geofence.area.slice(9, geofence.area.length - 2).split(',')
|
|
6
|
+
const coords2 = coords.map(c => {
|
|
7
|
+
return c.trim().split(' ').map(a => parseFloat(a))
|
|
8
|
+
})
|
|
9
|
+
const pt = turf.point([parseFloat(position.latitude), parseFloat(position.longitude)])
|
|
10
|
+
const poly = turf.polygon([coords2])
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
return inside.default(pt, poly)
|
|
12
13
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function getIdleEvents (route, driver) {
|
|
2
|
+
const idleEvents = []
|
|
3
|
+
|
|
4
|
+
const routeByDevice = route.reduce(function (a, x) {
|
|
5
|
+
(a[x.deviceId] = a[x.deviceId] || []).push(x)
|
|
6
|
+
return a
|
|
7
|
+
}, {})
|
|
8
|
+
|
|
9
|
+
Object.keys(routeByDevice).forEach(function (key) {
|
|
10
|
+
let inIdle = false
|
|
11
|
+
routeByDevice[key].forEach(p => {
|
|
12
|
+
if (p.attributes.ignition && p.speed === 0 && (!driver || p.attributes.driverUniqueId === driver.uniqueId)) {
|
|
13
|
+
if (!inIdle) {
|
|
14
|
+
const idleEvent = {
|
|
15
|
+
position: p,
|
|
16
|
+
idleTime: 0
|
|
17
|
+
}
|
|
18
|
+
idleEvents.push(idleEvent)
|
|
19
|
+
inIdle = true
|
|
20
|
+
} else {
|
|
21
|
+
if (!idleEvents[idleEvents.length - 1].position.attributes.driverUniqueId) {
|
|
22
|
+
idleEvents[idleEvents.length - 1].position.attributes.driverUniqueId = p.attributes.driverUniqueId
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (p.attributes.idleTime) {
|
|
26
|
+
idleEvents[idleEvents.length - 1].idleTime = p.attributes.idleTime
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
} else if (inIdle) {
|
|
30
|
+
const currentIdleEvent = idleEvents[idleEvents.length - 1]
|
|
31
|
+
if (p.attributes.idleTime === undefined) {
|
|
32
|
+
currentIdleEvent.idleTime = new Date(p.fixTime) - new Date(currentIdleEvent.position.fixTime)
|
|
33
|
+
}
|
|
34
|
+
inIdle = false
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return idleEvents
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
exports.getIdleEvents = getIdleEvents
|
package/src/util/trips.js
CHANGED
|
@@ -1,175 +1,178 @@
|
|
|
1
|
-
const {coordsDistance, convertFromUTC, weekDaySelected} = require(
|
|
1
|
+
const { coordsDistance, convertFromUTC, weekDaySelected } = require('./utils')
|
|
2
2
|
|
|
3
3
|
const minDistance = 0
|
|
4
4
|
const minAvgSpeed = 0
|
|
5
5
|
|
|
6
|
-
function addNearestPOIs(trips, userData){
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
const nearestPOIs = distance.filter(a => a.distance < 100).sort((a, b) => (a.distance > b.distance) ? 1 : -1)
|
|
21
|
-
if (nearestPOIs.length > 0) {
|
|
22
|
-
t.endPOIName = nearestPOIs[0].p.name
|
|
6
|
+
function addNearestPOIs (trips, userData) {
|
|
7
|
+
trips.forEach(t => {
|
|
8
|
+
t.totalKms = t.distance / 1000
|
|
9
|
+
|
|
10
|
+
const distance = userData.geofences
|
|
11
|
+
.filter(g => g && g.area.startsWith('CIRCLE'))
|
|
12
|
+
.map(g => {
|
|
13
|
+
const str = g.area.substring('CIRCLE ('.length, g.area.indexOf(','))
|
|
14
|
+
const coord = str.trim().split(' ')
|
|
15
|
+
return {
|
|
16
|
+
p: g,
|
|
17
|
+
distance: Math.round(coordsDistance(parseFloat(coord[1]), parseFloat(coord[0]), t.endLon, t.endLat))
|
|
23
18
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
console.log('checkTripsKms')
|
|
29
|
-
const trips = data.trips.filter(t => t.distance === minDistance && t.averageSpeed > minAvgSpeed)
|
|
30
|
-
if(trips.length > 0) {
|
|
31
|
-
//Vehicles with imported positions. calculate trip distance with route positions
|
|
32
|
-
trips.forEach(t => {
|
|
33
|
-
if(t.distance === minDistance && t.averageSpeed > minAvgSpeed) {
|
|
34
|
-
const tripRoute = data.route.filter(p => p.deviceId === t.deviceId
|
|
35
|
-
&& new Date(p.fixTime).getTime() >= new Date(t.startTime).getTime()
|
|
36
|
-
&& new Date(p.fixTime).getTime() <= new Date(t.endTime).getTime())
|
|
37
|
-
|
|
38
|
-
let current = null
|
|
39
|
-
const distances = []
|
|
40
|
-
for(const p of tripRoute) {
|
|
41
|
-
if(current) {
|
|
42
|
-
distances.push(coordsDistance(parseFloat(current.longitude),parseFloat(current.latitude),
|
|
43
|
-
parseFloat(p.longitude),parseFloat(p.latitude)))
|
|
44
|
-
}
|
|
45
|
-
current = p
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
t.distance = distances.reduce((a, b) => a + b, 0)
|
|
49
|
-
}
|
|
50
|
-
})
|
|
19
|
+
})
|
|
20
|
+
const nearestPOIs = distance.filter(a => a.distance < 100).sort((a, b) => (a.distance > b.distance) ? 1 : -1)
|
|
21
|
+
if (nearestPOIs.length > 0) {
|
|
22
|
+
t.endPOIName = nearestPOIs[0].p.name
|
|
51
23
|
}
|
|
24
|
+
})
|
|
52
25
|
}
|
|
53
26
|
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
t.endTimeIsOut = true
|
|
74
|
-
isPartialInside = true
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (tripStart.getTime() < startDate.getTime()
|
|
78
|
-
&& new Date(t.endTime).getTime() > startDate.getTime()) {
|
|
79
|
-
|
|
80
|
-
//Trip starts outside time period and ends inside time period
|
|
81
|
-
const routeInside = route.filter(p => new Date(p.fixTime).getTime() > startDate.getTime() && new Date(p.fixTime).getTime() < new Date(t.endTime).getTime() && t.deviceId === p.deviceId)
|
|
82
|
-
updateTrip(t, routeInside)
|
|
83
|
-
t.startTime = startDate.toISOString()
|
|
84
|
-
t.startTimeIsOut = true
|
|
85
|
-
isPartialInside = true
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
} else {
|
|
89
|
-
if (tripStart.getTime() < startDate.getTime()
|
|
90
|
-
&& tripEnd.getTime() > startDate.getTime()) {
|
|
91
|
-
//Trip starts outside time period and ends inside time period
|
|
92
|
-
const routeInside = route.filter(p => new Date(p.fixTime).getTime() > new Date(startDate.toUTCString()).getTime() && new Date(p.fixTime).getTime() < new Date(t.endTime).getTime() && t.deviceId === p.deviceId)
|
|
93
|
-
updateTrip(t, routeInside)
|
|
94
|
-
t.startTime = startDate.toISOString()
|
|
95
|
-
t.startTimeIsOut = true
|
|
96
|
-
isPartialInside = true
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (tripStart.getTime() < endDate.getTime()
|
|
100
|
-
&& tripEnd.getTime() > endDate.getTime()) {
|
|
101
|
-
//Trip starts inside time period and ends outside time period
|
|
102
|
-
const routeInside = route.filter(p => new Date(p.fixTime).getTime() < endDate.getTime() && new Date(p.fixTime).getTime() > new Date(t.startTime).getTime() && t.deviceId === p.deviceId)
|
|
103
|
-
updateTrip(t, routeInside)
|
|
104
|
-
t.endTime = endDate.toISOString()
|
|
105
|
-
t.endTimeIsOut = true
|
|
106
|
-
isPartialInside = true
|
|
107
|
-
}
|
|
27
|
+
function checkTripsKms (traccarInstance, from, to, devices, data) {
|
|
28
|
+
console.log('checkTripsKms')
|
|
29
|
+
const trips = data.trips.filter(t => t.distance === minDistance && t.averageSpeed > minAvgSpeed)
|
|
30
|
+
if (trips.length > 0) {
|
|
31
|
+
// Vehicles with imported positions. calculate trip distance with route positions
|
|
32
|
+
trips.forEach(t => {
|
|
33
|
+
if (t.distance === minDistance && t.averageSpeed > minAvgSpeed) {
|
|
34
|
+
const tripRoute = data.route.filter(p => p.deviceId === t.deviceId &&
|
|
35
|
+
new Date(p.fixTime).getTime() >= new Date(t.startTime).getTime() &&
|
|
36
|
+
new Date(p.fixTime).getTime() <= new Date(t.endTime).getTime())
|
|
37
|
+
|
|
38
|
+
let current = null
|
|
39
|
+
const distances = []
|
|
40
|
+
for (const p of tripRoute) {
|
|
41
|
+
if (current) {
|
|
42
|
+
distances.push(coordsDistance(parseFloat(current.longitude), parseFloat(current.latitude),
|
|
43
|
+
parseFloat(p.longitude), parseFloat(p.latitude)))
|
|
44
|
+
}
|
|
45
|
+
current = p
|
|
108
46
|
}
|
|
109
|
-
}
|
|
110
47
|
|
|
111
|
-
|
|
48
|
+
t.distance = distances.reduce((a, b) => a + b, 0)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
}
|
|
112
52
|
}
|
|
113
53
|
|
|
54
|
+
function isPartialInsideTimetable (t, userData, route) {
|
|
55
|
+
const tripStart = new Date(t.startTime)
|
|
56
|
+
const tripEnd = new Date(t.endTime)
|
|
57
|
+
|
|
58
|
+
let isPartialInside = false
|
|
59
|
+
|
|
60
|
+
if (weekDaySelected(tripStart, userData.weekDays)) {
|
|
61
|
+
const startDate = new Date(convertFromUTC(t.startTime, userData.user.attributes.timezone).toISOString().split('T')[0] + ' ' + userData.dayHours.startTime)
|
|
62
|
+
const endDate = new Date(convertFromUTC(t.startTime, userData.user.attributes.timezone).toISOString().split('T')[0] + ' ' + userData.dayHours.endTime)
|
|
63
|
+
|
|
64
|
+
if (startDate.getTime() > endDate.getTime()) {
|
|
65
|
+
// Trip starts outside time period and ends inside time period
|
|
66
|
+
if (tripStart.getTime() < endDate.getTime() &&
|
|
67
|
+
tripEnd.getTime() > endDate.getTime()) {
|
|
68
|
+
// Trip starts inside time period and ends outside time period
|
|
69
|
+
const routeInside = route.filter(p => new Date(p.fixTime).getTime() < new Date(endDate.toUTCString()).getTime() && new Date(p.fixTime).getTime() > new Date(t.startTime).getTime() && t.deviceId === p.deviceId)
|
|
70
|
+
updateTrip(t, routeInside)
|
|
71
|
+
t.endTime = endDate.toISOString()
|
|
72
|
+
t.endTimeIsOut = true
|
|
73
|
+
isPartialInside = true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (tripStart.getTime() < startDate.getTime() &&
|
|
77
|
+
new Date(t.endTime).getTime() > startDate.getTime()) {
|
|
78
|
+
// Trip starts outside time period and ends inside time period
|
|
79
|
+
const routeInside = route.filter(p => new Date(p.fixTime).getTime() > startDate.getTime() && new Date(p.fixTime).getTime() < new Date(t.endTime).getTime() && t.deviceId === p.deviceId)
|
|
80
|
+
updateTrip(t, routeInside)
|
|
81
|
+
t.startTime = startDate.toISOString()
|
|
82
|
+
t.startTimeIsOut = true
|
|
83
|
+
isPartialInside = true
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
if (tripStart.getTime() < startDate.getTime() &&
|
|
87
|
+
tripEnd.getTime() > startDate.getTime()) {
|
|
88
|
+
// Trip starts outside time period and ends inside time period
|
|
89
|
+
const routeInside = route.filter(p => new Date(p.fixTime).getTime() > new Date(startDate.toUTCString()).getTime() && new Date(p.fixTime).getTime() < new Date(t.endTime).getTime() && t.deviceId === p.deviceId)
|
|
90
|
+
updateTrip(t, routeInside)
|
|
91
|
+
t.startTime = startDate.toISOString()
|
|
92
|
+
t.startTimeIsOut = true
|
|
93
|
+
isPartialInside = true
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (tripStart.getTime() < endDate.getTime() &&
|
|
97
|
+
tripEnd.getTime() > endDate.getTime()) {
|
|
98
|
+
// Trip starts inside time period and ends outside time period
|
|
99
|
+
const routeInside = route.filter(p => new Date(p.fixTime).getTime() < endDate.getTime() && new Date(p.fixTime).getTime() > new Date(t.startTime).getTime() && t.deviceId === p.deviceId)
|
|
100
|
+
updateTrip(t, routeInside)
|
|
101
|
+
t.endTime = endDate.toISOString()
|
|
102
|
+
t.endTimeIsOut = true
|
|
103
|
+
isPartialInside = true
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return isPartialInside
|
|
109
|
+
}
|
|
114
110
|
|
|
115
|
-
function isInsideTimetable(t, userData){
|
|
116
|
-
|
|
117
|
-
|
|
111
|
+
function isInsideTimetable (t, userData) {
|
|
112
|
+
const tripStart = new Date(t.startTime)
|
|
113
|
+
const tripEnd = new Date(t.endTime)
|
|
118
114
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
if (weekDaySelected(tripStart, userData.weekDays)) {
|
|
116
|
+
const startDate = new Date(convertFromUTC(t.startTime, userData.user.attributes.timezone).toISOString().split('T')[0] + ' ' + userData.dayHours.startTime)
|
|
117
|
+
const endDate = new Date(convertFromUTC(t.startTime, userData.user.attributes.timezone).toISOString().split('T')[0] + ' ' + userData.dayHours.endTime)
|
|
122
118
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
119
|
+
// Trips inside time period
|
|
120
|
+
if (startDate.getTime() < endDate.getTime()) {
|
|
121
|
+
console.log((tripStart.getTime() > startDate.getTime() && tripEnd.getTime() < endDate.getTime()))
|
|
122
|
+
return tripStart.getTime() > startDate.getTime() && tripEnd.getTime() < endDate.getTime()
|
|
123
|
+
} else {
|
|
124
|
+
return (tripStart.getTime() < endDate.getTime() && tripEnd.getTime() < endDate.getTime()) ||
|
|
129
125
|
(tripStart.getTime() > startDate.getTime())
|
|
130
|
-
}
|
|
131
126
|
}
|
|
127
|
+
}
|
|
132
128
|
|
|
133
|
-
|
|
129
|
+
return false
|
|
134
130
|
}
|
|
135
131
|
|
|
136
|
-
function updateTrip(t, route){
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
132
|
+
function updateTrip (t, route) {
|
|
133
|
+
if (!route[route.length - 1]) { return }
|
|
134
|
+
const distance = route[route.length - 1].attributes.totalDistance - route[0].attributes.totalDistance
|
|
135
|
+
t.distance = distance
|
|
136
|
+
t.duration = new Date(route[route.length - 1].fixTime).getTime() - new Date(route[0].fixTime).getTime()
|
|
137
|
+
t.maxSpeed = route.reduce((a, b) => Math.max(a, b.speed), 0)
|
|
138
|
+
t.averageSpeed = distance > 0 ? ((route.reduce((a, b) => a + b.speed, 0)) / route.length) : 0
|
|
143
139
|
}
|
|
144
140
|
|
|
145
|
-
function calculateTrip(device, route) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
141
|
+
function calculateTrip (device, route) {
|
|
142
|
+
const startPos = route[0]
|
|
143
|
+
const endPos = route[route.length - 1]
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
deviceId: device.id,
|
|
147
|
+
deviceName: device.name,
|
|
148
|
+
distance: route.reduce((a, b) => a + b.attributes.distance, 0),
|
|
149
|
+
averageSpeed: Math.round((route.map(p => p.speed).reduce((a, b) => a + b, 0)) / route.length),
|
|
150
|
+
maxSpeed: route.map(p => p.speed).reduce(function (a, b) { return Math.max(a, b) }),
|
|
151
|
+
spentFuel: 0,
|
|
152
|
+
startOdometer: startPos.attributes.odometer || startPos.attributes.totalDistance,
|
|
153
|
+
endOdometer: endPos.attributes.odometer || endPos.attributes.totalDistance,
|
|
154
|
+
startPositionId: startPos.id,
|
|
155
|
+
endPositionId: endPos.id,
|
|
156
|
+
startLat: startPos.latitude,
|
|
157
|
+
startLon: startPos.longitude,
|
|
158
|
+
startTime: startPos.fixTime,
|
|
159
|
+
startAddress: startPos.address,
|
|
160
|
+
endLat: endPos.latitude,
|
|
161
|
+
endLon: endPos.longitude,
|
|
162
|
+
endTime: endPos.fixTime,
|
|
163
|
+
endAddress: endPos.address,
|
|
164
|
+
endTimeIsOut: true,
|
|
165
|
+
duration: new Date(endPos.fixTime).getTime() - new Date(startPos.fixTime).getTime(),
|
|
166
|
+
driverUniqueId: null,
|
|
167
|
+
driverName: null
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function getTripIdleTime (trip, idleEvents) {
|
|
172
|
+
const tripStartDate = new Date(trip.startTime)
|
|
173
|
+
const tripEndDate = new Date(trip.endTime)
|
|
174
|
+
const idleEventsInsideTrip = idleEvents.filter(i => tripStartDate < new Date(i.position.fixTime) && tripEndDate > new Date(i.position.fixTime))
|
|
175
|
+
return idleEventsInsideTrip.reduce((a, b) => a + b.idleTime, 0)
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
exports.checkTripsKms = checkTripsKms
|
|
@@ -177,3 +180,4 @@ exports.isInsideTimetable = isInsideTimetable
|
|
|
177
180
|
exports.isPartialInsideTimetable = isPartialInsideTimetable
|
|
178
181
|
exports.addNearestPOIs = addNearestPOIs
|
|
179
182
|
exports.calculateTrip = calculateTrip
|
|
183
|
+
exports.getTripIdleTime = getTripIdleTime
|
package/src/zone-report.js
CHANGED
|
@@ -53,26 +53,24 @@ async function createZoneReport (from, to, userData, traccar) {
|
|
|
53
53
|
xpert: devices.filter(d => d.attributes.xpert).length > 0
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
const sliced = automaticReports.sliceArray(devices,
|
|
56
|
+
const sliced = automaticReports.sliceArray(devices, 20)
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
const promises = []
|
|
58
|
+
let deviceCount = 0
|
|
60
59
|
for (const slice of sliced) {
|
|
61
|
-
|
|
60
|
+
const allInOne = await traccarHelper.getAllInOne(
|
|
62
61
|
traccar, from, to, slice, true, false, false, false,
|
|
63
|
-
deviceCount, devices.length, 20, 5
|
|
64
|
-
|
|
62
|
+
deviceCount, devices.length, 20, 5)
|
|
63
|
+
const routeData = allInOne.route
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
const data = getInAndOutEvents(slice, routeData, userData)
|
|
67
66
|
|
|
68
|
-
|
|
67
|
+
console.log('Geofence Enter/Exit Alerts:' + data.length)
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
if (data.length) {
|
|
70
|
+
allData.devices.push(...await processDevices(from, to, devices, userData.drivers, userData.geofences, data, traccar))
|
|
71
|
+
}
|
|
72
|
+
deviceCount = deviceCount + slice.length
|
|
74
73
|
}
|
|
75
|
-
await Promise.all(promises)
|
|
76
74
|
|
|
77
75
|
reportData.push(allData)
|
|
78
76
|
|