fleetmap-reports 1.0.261 → 1.0.265
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/.idea/workspace.xml +46 -4
- package/package.json +1 -1
- package/src/activity-report.js +227 -36
- package/src/kms-report.js +72 -0
- package/src/speeding-report.js +190 -192
- package/src/trip-report.js +53 -92
- package/src/util/driver.js +2 -3
- package/src/util/trips.js +53 -0
package/src/speeding-report.js
CHANGED
|
@@ -16,109 +16,135 @@ let traccarInstance
|
|
|
16
16
|
let userData
|
|
17
17
|
|
|
18
18
|
const fileName = 'SpeedingReport'
|
|
19
|
+
const eventTypes = ['deviceOverspeed']
|
|
19
20
|
|
|
20
21
|
async function createSpeedingReport(from, to, reportUserData, traccar) {
|
|
22
|
+
console.log('Create SpeedingReport')
|
|
21
23
|
traccarInstance = traccar
|
|
22
24
|
userData = reportUserData
|
|
23
|
-
console.log('createSpeedingReport', from, to, userData, traccar)
|
|
24
25
|
const reportData = []
|
|
25
26
|
|
|
26
|
-
if(userData.byDriver){
|
|
27
|
+
if (userData.byDriver) {
|
|
28
|
+
console.log("ByDriver")
|
|
27
29
|
const allData = await createSpeedingReportByDriver(from, to)
|
|
28
30
|
reportData.push(allData)
|
|
29
31
|
}
|
|
30
|
-
else if(userData.byGroup){
|
|
32
|
+
else if (userData.byGroup) {
|
|
31
33
|
console.log("ByGroup")
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
xpert: devices.filter(d => d.attributes.xpert).length > 0
|
|
40
|
-
}
|
|
34
|
+
const allData = await createSpeedingReportByGroup(from, to)
|
|
35
|
+
reportData.push(...allData)
|
|
36
|
+
} else {
|
|
37
|
+
console.log("ByDevice")
|
|
38
|
+
const allData = await createSpeedingReportByDevice(from, to, userData.devices)
|
|
39
|
+
reportData.push(allData)
|
|
40
|
+
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const routes = userData.useVehicleSpeedLimit ? [] : await traccar.getRoute(traccarInstance, from, to, devices)
|
|
42
|
+
return reportData
|
|
43
|
+
}
|
|
45
44
|
|
|
46
|
-
|
|
45
|
+
async function createSpeedingReportByGroup(from, to){
|
|
46
|
+
const reportData = []
|
|
47
|
+
for (const g of userData.groups) {
|
|
48
|
+
const devices = userData.devices.filter(d => d.groupId === g.id)
|
|
49
|
+
console.log(g.name + ' devices:' + devices.length)
|
|
50
|
+
if(devices.length > 0) {
|
|
51
|
+
const groupData = {
|
|
52
|
+
devices: [],
|
|
53
|
+
group: g,
|
|
54
|
+
xpert: devices.filter(d => d.attributes.xpert).length > 0
|
|
55
|
+
}
|
|
47
56
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
const events = await traccar.getEvents(traccarInstance, from, to, devices, eventTypes)
|
|
58
|
+
const routes = await traccar.getRoute(traccarInstance, from, to, devices)
|
|
59
|
+
|
|
60
|
+
devices.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
|
61
|
+
|
|
62
|
+
if (events.length > 0) {
|
|
63
|
+
groupData.devices = await processDevices(from, to, devices, events, routes)
|
|
64
|
+
reportData.push(groupData)
|
|
52
65
|
}
|
|
53
66
|
}
|
|
54
|
-
|
|
55
|
-
const withoutGroupData = await createSpeedingReportByDevice(from, to)
|
|
56
|
-
reportData.push(withoutGroupData)
|
|
57
|
-
} else {
|
|
58
|
-
const allData = await createSpeedingReportByDevice(from, to)
|
|
59
|
-
reportData.push(allData)
|
|
60
67
|
}
|
|
61
68
|
|
|
69
|
+
const groupIds = userData.groups.map(g => g.id)
|
|
70
|
+
const withoutGroupDevices = userData.devices.filter(d => !groupIds.includes(d.groupId))
|
|
71
|
+
|
|
72
|
+
const withoutGroupData = await createSpeedingReportByDevice(from, to, withoutGroupDevices)
|
|
73
|
+
reportData.push(withoutGroupData)
|
|
74
|
+
|
|
62
75
|
return reportData
|
|
63
76
|
}
|
|
64
77
|
|
|
65
|
-
async function createSpeedingReportByDevice(from, to) {
|
|
66
|
-
|
|
67
|
-
const withoutGroupDevices = userData.byGroup ? userData.devices.filter(d => !groupIds.includes(d.groupId)) : userData.devices
|
|
68
|
-
|
|
69
|
-
withoutGroupDevices.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
|
78
|
+
async function createSpeedingReportByDevice(from, to, devices) {
|
|
79
|
+
devices.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
|
70
80
|
|
|
71
|
-
const
|
|
72
|
-
const events =
|
|
73
|
-
|
|
81
|
+
const routes = await traccar.getRoute(traccarInstance, from, to, devices)
|
|
82
|
+
const events = []
|
|
83
|
+
if(!userData.useVehicleSpeedLimit && userData.customSpeed){
|
|
84
|
+
events.push(...await getCustomSpeedLimitEvents(devices, routes))
|
|
85
|
+
} else {
|
|
86
|
+
events.push(...await traccar.getEvents(traccarInstance, from, to, devices, eventTypes))
|
|
87
|
+
}
|
|
74
88
|
|
|
75
|
-
|
|
89
|
+
if(userData.roadSpeedLimits){
|
|
90
|
+
events.push(...await getHereEvents(devices, routes))
|
|
91
|
+
}
|
|
76
92
|
|
|
77
|
-
if(events.length
|
|
78
|
-
|
|
79
|
-
|
|
93
|
+
if(!events.length) {
|
|
94
|
+
//empty report
|
|
95
|
+
return { devices: [] }
|
|
80
96
|
}
|
|
81
97
|
|
|
82
|
-
return
|
|
98
|
+
return {
|
|
99
|
+
devices: await processDevices(from, to, devices, events, routes),
|
|
100
|
+
xpert: devices.filter(d => d && d.attributes && d.attributes.xpert).length > 0
|
|
101
|
+
}
|
|
83
102
|
}
|
|
84
103
|
|
|
85
104
|
async function createSpeedingReportByDriver(from, to) {
|
|
86
|
-
const
|
|
87
|
-
console.log('Devices with driver',
|
|
105
|
+
const devices = await drivers.devicesByDriver(traccarInstance, from, to, userData.drivers, userData.devices)
|
|
106
|
+
console.log('Devices with driver',devices.length)
|
|
107
|
+
|
|
108
|
+
if(!devices.length) {
|
|
109
|
+
//empty report
|
|
110
|
+
return { drivers: [] }
|
|
111
|
+
}
|
|
88
112
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
113
|
+
const routes = await traccar.getRoute(traccarInstance, from, to, devices)
|
|
114
|
+
const events = []
|
|
115
|
+
if(!userData.useVehicleSpeedLimit && userData.customSpeed){
|
|
116
|
+
events.push(...await getCustomSpeedLimitEvents(devices, routes))
|
|
117
|
+
} else {
|
|
118
|
+
events.push(...await traccar.getEvents(traccarInstance, from, to, devices, eventTypes))
|
|
93
119
|
}
|
|
94
120
|
|
|
95
|
-
|
|
96
|
-
|
|
121
|
+
if(userData.roadSpeedLimits){
|
|
122
|
+
events.push(...await getHereEvents(devices, routes))
|
|
97
123
|
}
|
|
98
|
-
console.log(eventsData.length)
|
|
99
124
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
125
|
+
if(!events.length) {
|
|
126
|
+
//empty report
|
|
127
|
+
return { drivers: [] }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {drivers: await processDrivers(from, to, events, routes)}
|
|
104
131
|
}
|
|
105
132
|
|
|
106
|
-
async function processDrivers(from, to,
|
|
107
|
-
console.log(data)
|
|
133
|
+
async function processDrivers(from, to, events, routes) {
|
|
108
134
|
const driversResult = []
|
|
109
|
-
const alertsWithPosition = data.filter(a => a.position)
|
|
110
|
-
|
|
111
|
-
userData.drivers.forEach(d => {
|
|
112
|
-
const alerts = alertsWithPosition.filter(e => e.position.attributes.driverUniqueId === d.uniqueId)
|
|
113
135
|
|
|
114
|
-
|
|
136
|
+
await findEventsPosition(from, to, userData.devices, events, routes)
|
|
115
137
|
|
|
138
|
+
userData.drivers.forEach(d => {
|
|
139
|
+
const driverEvents = events.filter(e => e.position && e.position.attributes.driverUniqueId === d.uniqueId)
|
|
140
|
+
if (driverEvents.length > 0) {
|
|
141
|
+
driverEvents.sort((a, b) => a.positionId - b.positionId)
|
|
116
142
|
driversResult.push({
|
|
117
143
|
driver: d,
|
|
118
144
|
from: from,
|
|
119
145
|
to: to,
|
|
120
|
-
alerts:
|
|
121
|
-
maxSpeed:
|
|
146
|
+
alerts: driverEvents,
|
|
147
|
+
maxSpeed: driverEvents.reduce((a, b) => {
|
|
122
148
|
return a > b.attributes.maxSpeed ? a : b.attributes.maxSpeed
|
|
123
149
|
}, 0),
|
|
124
150
|
})
|
|
@@ -130,117 +156,38 @@ async function processDrivers(from, to, data) {
|
|
|
130
156
|
|
|
131
157
|
async function processDevices(from, to, devices, events, routes) {
|
|
132
158
|
const devicesResult = []
|
|
159
|
+
await findEventsPosition(from, to, devices, events, routes)
|
|
133
160
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
to: to,
|
|
148
|
-
alerts: alertsWithPosition,
|
|
149
|
-
maxSpeed: alertsWithPosition.reduce((a, b) => {
|
|
150
|
-
return a > (b && b.attributes && b.attributes.maxSpeed) ? a : (b && b.attributes && b.attributes.maxSpeed)
|
|
151
|
-
}, 0),
|
|
152
|
-
})
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
} else {
|
|
156
|
-
for (const d of devices) {
|
|
157
|
-
const positions = routes.filter(t => t.deviceId === d.id)
|
|
158
|
-
const maxSpeed = userData.customSpeed / 1.85200
|
|
159
|
-
|
|
160
|
-
const alerts = []
|
|
161
|
-
for(let pIndex = 0; pIndex < positions.length ; pIndex++){
|
|
162
|
-
const position = positions[pIndex]
|
|
163
|
-
if(position.speed > maxSpeed){
|
|
164
|
-
const alert = {
|
|
165
|
-
position: position,
|
|
166
|
-
attributes: {
|
|
167
|
-
speedLimit: maxSpeed,
|
|
168
|
-
speed: position.speed
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
pIndex = calculateEventData(positions, pIndex, alert)
|
|
173
|
-
|
|
174
|
-
alerts.push(alert)
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
if(alerts.length > 0){
|
|
178
|
-
devicesResult.push({
|
|
179
|
-
device: d,
|
|
180
|
-
from: from,
|
|
181
|
-
to: to,
|
|
182
|
-
alerts: alerts,
|
|
183
|
-
maxSpeed: alerts.reduce((a, b) => {
|
|
184
|
-
return a > (b && b.attributes && b.attributes.maxSpeed) ? a : (b && b.attributes && b.attributes.maxSpeed)
|
|
185
|
-
}, 0),
|
|
186
|
-
})
|
|
187
|
-
}
|
|
161
|
+
for (const d of devices) {
|
|
162
|
+
const deviceEvents = events.filter(e => e.deviceId === d.id && e.position)
|
|
163
|
+
if (deviceEvents.length > 0) {
|
|
164
|
+
deviceEvents.sort((a, b) => a.positionId - b.positionId)
|
|
165
|
+
devicesResult.push({
|
|
166
|
+
device: d,
|
|
167
|
+
from: from,
|
|
168
|
+
to: to,
|
|
169
|
+
alerts: deviceEvents,
|
|
170
|
+
maxSpeed: deviceEvents.reduce((a, b) => {
|
|
171
|
+
return a > (b && b.attributes && b.attributes.maxSpeed) ? a : (b && b.attributes && b.attributes.maxSpeed)
|
|
172
|
+
}, 0),
|
|
173
|
+
})
|
|
188
174
|
}
|
|
189
175
|
}
|
|
190
176
|
|
|
191
177
|
return devicesResult
|
|
192
178
|
}
|
|
193
179
|
|
|
194
|
-
async function
|
|
195
|
-
|
|
196
|
-
const deviceIds = [...new Set(events.map(e => e.deviceId))]
|
|
197
|
-
const routeData = await traccar.getRoute(traccarInstance, from, to, deviceIds.map(id => { return { id: id } } ))
|
|
198
|
-
|
|
199
|
-
console.log(events)
|
|
200
|
-
|
|
180
|
+
async function findEventsPosition(from, to, devices, events, routes){
|
|
201
181
|
for (const d of devices) {
|
|
202
|
-
let
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const positions =
|
|
206
|
-
|
|
207
|
-
if (userData.roadSpeedLimits && positions.length) {
|
|
208
|
-
try {
|
|
209
|
-
const results = await here.routeMatch(positions)
|
|
210
|
-
const hereAlerts = results.map(r => {
|
|
211
|
-
const position = positions.find(p => new Date(p.fixTime).getTime() === r.timestamp)
|
|
212
|
-
return {
|
|
213
|
-
...r,
|
|
214
|
-
roadSpeedLimit: r.speedLimit,
|
|
215
|
-
deviceId: d.id,
|
|
216
|
-
position,
|
|
217
|
-
positionId: position && position.id,
|
|
218
|
-
attributes: {speedLimit: r.speedLimit, speed: r.currentSpeedKmh / 1.85200}
|
|
219
|
-
}
|
|
220
|
-
})
|
|
221
|
-
console.log('hereAlerts', hereAlerts)
|
|
222
|
-
const reduced = hereAlerts.reduce((acc, cur, idx, src) => {
|
|
223
|
-
if (idx === 1) {
|
|
224
|
-
return [cur]
|
|
225
|
-
}
|
|
226
|
-
if (cur.timestamp - src[idx - 1].timestamp > 300000 || cur.roadSpeedLimit !== src[idx - 1].roadSpeedLimit) {
|
|
227
|
-
return acc.concat(cur)
|
|
228
|
-
}
|
|
229
|
-
return acc
|
|
230
|
-
})
|
|
231
|
-
events.push(...reduced)
|
|
232
|
-
alerts = events.filter(t => t.deviceId === d.id)
|
|
233
|
-
} catch (e) {
|
|
234
|
-
console.error(e)
|
|
235
|
-
}
|
|
236
|
-
}
|
|
182
|
+
let deviceEvents = events.filter(e => e.deviceId === d.id && e.positionId)
|
|
183
|
+
if (deviceEvents.length > 0) {
|
|
184
|
+
deviceEvents.sort((a,b) => a.positionId-b.positionId )
|
|
185
|
+
const positions = routes.filter(p => p.deviceId === d.id)
|
|
237
186
|
|
|
238
|
-
for (const a of
|
|
187
|
+
for (const a of deviceEvents) {
|
|
239
188
|
let pIndex = positions.findIndex(p => p.id === a.positionId)
|
|
240
189
|
if (pIndex > 0) {
|
|
241
|
-
a.position = positions[pIndex]
|
|
242
190
|
let geofence = null
|
|
243
|
-
|
|
244
191
|
if (a.geofenceId) {
|
|
245
192
|
geofence = userData.geofences.find(g => g.id === a.geofenceId)
|
|
246
193
|
if (geofence) {
|
|
@@ -248,44 +195,87 @@ async function getEventsPosition(from, to, devices, events){
|
|
|
248
195
|
}
|
|
249
196
|
}
|
|
250
197
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
while (pIndex + 1 < positions.length) {
|
|
255
|
-
pIndex++
|
|
256
|
-
|
|
257
|
-
dist += distance(point([endEventPosition.longitude, endEventPosition.latitude]),
|
|
258
|
-
point([positions[pIndex].longitude, positions[pIndex].latitude]))
|
|
259
|
-
endEventPosition = positions[pIndex]
|
|
260
|
-
if (endEventPosition.speed <= a.attributes.speedLimit || (geofence && isOutsideGeofence(geofence, endEventPosition))) {
|
|
261
|
-
a.eventTime = new Date(endEventPosition.fixTime) - new Date(a.position.fixTime)
|
|
262
|
-
a.attributes.maxSpeed = maxSpeed
|
|
263
|
-
a.distance = dist
|
|
264
|
-
break
|
|
265
|
-
} else {
|
|
266
|
-
if (maxSpeed < endEventPosition.speed) {
|
|
267
|
-
maxSpeed = endEventPosition.speed
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
198
|
+
if(!a.position) {
|
|
199
|
+
a.position = positions[pIndex]
|
|
200
|
+
pIndex = calculateEventData(positions, pIndex, a, d, geofence)
|
|
271
201
|
|
|
272
|
-
|
|
273
|
-
const driver = userData.drivers.find(d => d.uniqueId === a.position.attributes.driverUniqueId)
|
|
274
|
-
a.driver = driver && driver.name
|
|
202
|
+
positions.slice(pIndex)
|
|
275
203
|
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
276
209
|
|
|
277
|
-
|
|
210
|
+
async function getCustomSpeedLimitEvents(devices, routes){
|
|
211
|
+
console.log('calculate custom speed limit events')
|
|
212
|
+
const events = []
|
|
213
|
+
const maxSpeed = userData.customSpeed / 1.85200
|
|
278
214
|
|
|
279
|
-
|
|
215
|
+
for (const d of devices) {
|
|
216
|
+
const positions = routes.filter(p => p.deviceId === d.id)
|
|
217
|
+
let eventIsActive = false
|
|
218
|
+
|
|
219
|
+
for (const position of positions) {
|
|
220
|
+
if(position.speed > maxSpeed && !eventIsActive){
|
|
221
|
+
const event = {
|
|
222
|
+
positionId: position && position.id,
|
|
223
|
+
deviceId: d.id,
|
|
224
|
+
attributes: {
|
|
225
|
+
speedLimit: maxSpeed,
|
|
226
|
+
speed: position.speed
|
|
227
|
+
}
|
|
280
228
|
}
|
|
229
|
+
events.push(event)
|
|
230
|
+
eventIsActive = true
|
|
231
|
+
} else if (position.speed <= maxSpeed) {
|
|
232
|
+
eventIsActive = false
|
|
281
233
|
}
|
|
282
234
|
}
|
|
283
235
|
}
|
|
236
|
+
|
|
237
|
+
return events
|
|
284
238
|
}
|
|
285
239
|
|
|
286
|
-
function
|
|
287
|
-
|
|
288
|
-
|
|
240
|
+
async function getHereEvents(devices, routes){
|
|
241
|
+
console.log('calculate custom speed limit events')
|
|
242
|
+
const events = []
|
|
243
|
+
for (const d of devices) {
|
|
244
|
+
const positions = routes.filter(p => p.deviceId === d.id)
|
|
245
|
+
try {
|
|
246
|
+
const results = await here.routeMatch(positions)
|
|
247
|
+
const hereAlerts = results.map(r => {
|
|
248
|
+
const position = positions.find(p => new Date(p.fixTime).getTime() === r.timestamp)
|
|
249
|
+
return {
|
|
250
|
+
...r,
|
|
251
|
+
roadSpeedLimit: r.speedLimit,
|
|
252
|
+
deviceId: d.id,
|
|
253
|
+
position,
|
|
254
|
+
positionId: position && position.id,
|
|
255
|
+
attributes: {speedLimit: r.speedLimit, speed: r.currentSpeedKmh / 1.85200}
|
|
256
|
+
}
|
|
257
|
+
})
|
|
258
|
+
console.log('hereAlerts', hereAlerts)
|
|
259
|
+
const reduced = hereAlerts.reduce((acc, cur, idx, src) => {
|
|
260
|
+
if (idx === 1) {
|
|
261
|
+
return [cur]
|
|
262
|
+
}
|
|
263
|
+
if (cur.timestamp - src[idx - 1].timestamp > 300000 || cur.roadSpeedLimit !== src[idx - 1].roadSpeedLimit) {
|
|
264
|
+
return acc.concat(cur)
|
|
265
|
+
}
|
|
266
|
+
return acc
|
|
267
|
+
})
|
|
268
|
+
events.push(...reduced)
|
|
269
|
+
} catch (e) {
|
|
270
|
+
//console.error(e)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return events
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function calculateEventData(positions, pIndex, alert, device, geofence){
|
|
277
|
+
let endEventPosition = alert.position
|
|
278
|
+
let maxSpeed = alert.position.speed
|
|
289
279
|
let dist = 0
|
|
290
280
|
while (pIndex + 1 < positions.length) {
|
|
291
281
|
pIndex++
|
|
@@ -293,10 +283,10 @@ function calculateEventData(positions, pIndex, a, geofence){
|
|
|
293
283
|
dist += distance(point([endEventPosition.longitude, endEventPosition.latitude]),
|
|
294
284
|
point([positions[pIndex].longitude, positions[pIndex].latitude]))
|
|
295
285
|
endEventPosition = positions[pIndex]
|
|
296
|
-
if (endEventPosition.speed <=
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
286
|
+
if (endEventPosition.speed <= alert.attributes.speedLimit || (geofence && isOutsideGeofence(geofence, endEventPosition))) {
|
|
287
|
+
alert.eventTime = new Date(endEventPosition.fixTime) - new Date(alert.position.fixTime)
|
|
288
|
+
alert.attributes.maxSpeed = maxSpeed
|
|
289
|
+
alert.distance = dist
|
|
300
290
|
break
|
|
301
291
|
} else {
|
|
302
292
|
if (maxSpeed < endEventPosition.speed) {
|
|
@@ -305,6 +295,14 @@ function calculateEventData(positions, pIndex, a, geofence){
|
|
|
305
295
|
}
|
|
306
296
|
}
|
|
307
297
|
|
|
298
|
+
|
|
299
|
+
if (alert.position.attributes.driverUniqueId) {
|
|
300
|
+
const driver = userData.drivers.find(d => d.uniqueId === alert.position.attributes.driverUniqueId)
|
|
301
|
+
alert.driver = driver && driver.name
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
alert.deviceName = device.name
|
|
305
|
+
|
|
308
306
|
return pIndex
|
|
309
307
|
}
|
|
310
308
|
|