fleetmap-reports 1.0.288 → 1.0.292
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/aws.xml +17 -0
- package/.idea/codeStyles/Project.xml +7 -0
- package/{fleetmap-reports.iml → .idea/fleetmap-reports.iml} +2 -1
- package/.idea/jsLibraryMappings.xml +1 -1
- package/.idea/modules.xml +1 -1
- package/.idea/vcs.xml +1 -1
- package/lang/enGB.js +12 -2
- package/lang/esCL.js +10 -1
- package/lang/index.js +4 -1
- package/lang/ptBR.js +11 -1
- package/lang/ptPT.js +11 -1
- package/package.json +2 -1
- package/src/activity-report.js +542 -206
- package/src/automaticReports.js +1 -1
- package/src/events-report.js +25 -21
- package/src/here.js +1 -1
- package/src/idle-report.js +285 -0
- package/src/index.js +12 -2
- package/src/index.test.js +18 -25
- package/src/kms-report.js +409 -103
- package/src/location-report.js +22 -18
- package/src/refueling-report.js +11 -1
- package/src/reportStyle.js +26 -0
- package/src/speeding-report.js +239 -180
- package/src/tests/index.js +22 -0
- package/src/trip-report.js +152 -159
- package/src/util/driver.js +5 -13
- package/src/util/pdfDocument.js +58 -0
- package/src/util/traccar.js +72 -0
- package/src/util/trips.js +76 -0
- package/src/util/utils.js +67 -1
- package/src/zone-report.js +22 -23
package/src/refueling-report.js
CHANGED
|
@@ -109,21 +109,31 @@ function calculateRefuelingPositions(d, positions) {
|
|
|
109
109
|
let refuelingActivated = false
|
|
110
110
|
let currentValue = null
|
|
111
111
|
let positionsChecked = 0
|
|
112
|
+
let lastRefuelingIndex = 0
|
|
112
113
|
positions.forEach(function(element, index, array){
|
|
113
114
|
if(!element.attributes.ignition && refuelingActivated && !currentValue){
|
|
114
|
-
|
|
115
|
+
//Ignition is off and Refueling is Active and there is no current value
|
|
116
|
+
|
|
117
|
+
//Get 5 position before the ignition off to calculate the current value of the fuel tank level
|
|
118
|
+
//lastRefuelingIndex is used to avoid re-analyzing positions prior to a refueling
|
|
119
|
+
const before = (index > 5 ? array.slice((index-6 < lastRefuelingIndex ? lastRefuelingIndex : index-6), index-1) : array.slice(0, index-1)).filter(b => b.attributes.ignition && b.attributes.fuel)
|
|
115
120
|
currentValue = Math.round(before.reduce((a, b) => a + b.attributes.fuel, 0) / before.length )
|
|
121
|
+
|
|
116
122
|
positionsChecked = 0
|
|
117
123
|
}
|
|
118
124
|
|
|
119
125
|
if(element.attributes.ignition && refuelingActivated && currentValue) {
|
|
126
|
+
//Ignition is on and Refueling is Active and there is a current value
|
|
127
|
+
//Calculate the value of the fuel tank level after turning the ignition back on
|
|
120
128
|
const after = (array.length > index + 5 ? array.slice(index, index+1) : array.slice(index)).filter(b => b.attributes.ignition && b.attributes.fuel)
|
|
121
129
|
const newValue = Math.round(after.reduce((a, b) => a + b.attributes.fuel, 0) / after.length )
|
|
122
130
|
|
|
123
131
|
const diff = automaticReports.calculateFuelDiff(currentValue, newValue, d)
|
|
124
132
|
if (diff > 20) {
|
|
133
|
+
//New refueling detected
|
|
125
134
|
const value = Math.round(diff * d.attributes.fuel_tank_capacity / 100)
|
|
126
135
|
refuelingPositions.push({position: element, diff: value})
|
|
136
|
+
lastRefuelingIndex = index
|
|
127
137
|
positionsChecked = 6 // to reset values
|
|
128
138
|
}
|
|
129
139
|
|
package/src/reportStyle.js
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
function getStyle(partnerData) {
|
|
2
|
+
const reportStyleData = {
|
|
3
|
+
pdfHeaderColor: [105, 105, 105],
|
|
4
|
+
pdfHeaderTextColor: [256, 256, 256],
|
|
5
|
+
pdfBodyColor: [256, 256, 256],
|
|
6
|
+
pdfBodyTextColor: [30, 30, 30],
|
|
7
|
+
pdfFooterColor: [210, 210, 210],
|
|
8
|
+
pdfFooterTextColor: [30, 30, 30],
|
|
9
|
+
imgHeight: 0,
|
|
10
|
+
imgWidth: 0,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if(partnerData && partnerData.reports){
|
|
14
|
+
reportStyleData.pdfHeaderColor = partnerData.reports.mainColor || [105, 105, 105]
|
|
15
|
+
reportStyleData.pdfFooterColor = partnerData.reports.mainColor || [210, 210, 210]
|
|
16
|
+
reportStyleData.pdfFooterTextColor = partnerData.reports.mainColor ? [256, 256, 256] : [30, 30, 30]
|
|
17
|
+
reportStyleData.imgHeight = partnerData.reports.logoHeight || 0
|
|
18
|
+
reportStyleData.imgWidth = partnerData.reports.logoWidth || 0
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return reportStyleData
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
exports.getStyle = getStyle
|
|
1
25
|
|
|
2
26
|
exports.pdfHeaderColor = [105, 105, 105]
|
|
3
27
|
exports.pdfHeaderTextColor = [256, 256, 256]
|
|
@@ -6,3 +30,5 @@ exports.pdfBodyTextColor = [30, 30, 30]
|
|
|
6
30
|
exports.pdfFooterColor = [210, 210, 210]
|
|
7
31
|
exports.pdfFooterTextColor = [30, 30, 30]
|
|
8
32
|
|
|
33
|
+
|
|
34
|
+
|
package/src/speeding-report.js
CHANGED
|
@@ -1,119 +1,149 @@
|
|
|
1
|
-
const automaticReports = require("./automaticReports")
|
|
2
1
|
const messages = require('../lang')
|
|
3
2
|
const jsPDF = require('jspdf')
|
|
4
|
-
const {convertMS} = require("./util/utils")
|
|
3
|
+
const {convertMS, convertToLocaleString} = require("./util/utils")
|
|
4
|
+
const {headerFromUser, AmiriRegular} = require("./util/pdfDocument")
|
|
5
5
|
require('jspdf-autotable')
|
|
6
6
|
const { parse } = require('wkt')
|
|
7
7
|
const turf = require('turf')
|
|
8
|
-
const
|
|
8
|
+
const {getStyle} = require("./reportStyle")
|
|
9
9
|
const drivers = require("./util/driver")
|
|
10
10
|
const {distance, point} = require('turf')
|
|
11
11
|
const here = require('./here')
|
|
12
|
+
const {getUserPartner} = require("fleetmap-partners");
|
|
13
|
+
const traccar = require("./util/traccar");
|
|
12
14
|
|
|
13
15
|
let traccarInstance
|
|
16
|
+
let userData
|
|
14
17
|
|
|
15
18
|
const fileName = 'SpeedingReport'
|
|
19
|
+
const eventTypes = ['deviceOverspeed']
|
|
16
20
|
|
|
17
|
-
async function createSpeedingReport(from, to,
|
|
21
|
+
async function createSpeedingReport(from, to, reportUserData, traccar) {
|
|
18
22
|
traccarInstance = traccar
|
|
19
|
-
|
|
23
|
+
userData = reportUserData
|
|
20
24
|
const reportData = []
|
|
21
25
|
|
|
22
|
-
if(userData.byDriver){
|
|
23
|
-
|
|
26
|
+
if (userData.byDriver) {
|
|
27
|
+
console.log("ByDriver")
|
|
28
|
+
const allData = await createSpeedingReportByDriver(from, to)
|
|
24
29
|
reportData.push(allData)
|
|
25
30
|
}
|
|
26
|
-
else if(userData.byGroup){
|
|
31
|
+
else if (userData.byGroup) {
|
|
27
32
|
console.log("ByGroup")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
xpert: devices.filter(d => d.attributes.xpert).length > 0
|
|
36
|
-
}
|
|
33
|
+
const allData = await createSpeedingReportByGroup(from, to)
|
|
34
|
+
reportData.push(...allData)
|
|
35
|
+
} else {
|
|
36
|
+
console.log("createSpeedingReportByDevice")
|
|
37
|
+
const allData = await createSpeedingReportByDevice(from, to, userData.devices)
|
|
38
|
+
reportData.push(allData)
|
|
39
|
+
}
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
return reportData
|
|
42
|
+
}
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
async function createSpeedingReportByGroup(from, to){
|
|
45
|
+
const reportData = []
|
|
46
|
+
for (const g of userData.groups) {
|
|
47
|
+
const devices = userData.devices.filter(d => d.groupId === g.id)
|
|
48
|
+
console.log(g.name + ' devices:' + devices.length)
|
|
49
|
+
if(devices.length > 0) {
|
|
50
|
+
const groupData = {
|
|
51
|
+
devices: [],
|
|
52
|
+
group: g,
|
|
53
|
+
xpert: devices.filter(d => d.attributes.xpert).length > 0
|
|
54
|
+
}
|
|
41
55
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
const events = await traccar.getEvents(traccarInstance, from, to, devices, eventTypes)
|
|
57
|
+
const routes = await traccar.getRoute(traccarInstance, from, to, devices)
|
|
58
|
+
|
|
59
|
+
devices.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
|
60
|
+
|
|
61
|
+
if (events.length > 0) {
|
|
62
|
+
groupData.devices = await processDevices(from, to, devices, events, routes)
|
|
63
|
+
reportData.push(groupData)
|
|
46
64
|
}
|
|
47
65
|
}
|
|
48
|
-
|
|
49
|
-
const withoutGroupData = await createSpeedingReportByDevice(from, to, userData)
|
|
50
|
-
reportData.push(withoutGroupData)
|
|
51
|
-
} else {
|
|
52
|
-
const allData = await createSpeedingReportByDevice(from, to, userData, roadSpeedLimits)
|
|
53
|
-
reportData.push(allData)
|
|
54
66
|
}
|
|
55
67
|
|
|
68
|
+
const groupIds = userData.groups.map(g => g.id)
|
|
69
|
+
const withoutGroupDevices = userData.devices.filter(d => !groupIds.includes(d.groupId))
|
|
70
|
+
|
|
71
|
+
const withoutGroupData = await createSpeedingReportByDevice(from, to, withoutGroupDevices)
|
|
72
|
+
reportData.push(withoutGroupData)
|
|
73
|
+
|
|
56
74
|
return reportData
|
|
57
75
|
}
|
|
58
76
|
|
|
59
|
-
async function createSpeedingReportByDevice(from, to,
|
|
60
|
-
|
|
61
|
-
const withoutGroupDevices = userData.byGroup ? userData.devices.filter(d => !groupIds.includes(d.groupId)) : userData.devices
|
|
62
|
-
|
|
63
|
-
withoutGroupDevices.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
|
77
|
+
async function createSpeedingReportByDevice(from, to, devices) {
|
|
78
|
+
devices.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
|
64
79
|
|
|
65
|
-
const
|
|
80
|
+
const routes = await traccar.getRoute(traccarInstance, from, to, devices)
|
|
81
|
+
const events = []
|
|
82
|
+
if(!userData.useVehicleSpeedLimit && userData.customSpeed){
|
|
83
|
+
events.push(...await getCustomSpeedLimitEvents(devices, routes))
|
|
84
|
+
} else {
|
|
85
|
+
events.push(...await traccar.getEvents(traccarInstance, from, to, devices, eventTypes))
|
|
86
|
+
}
|
|
66
87
|
|
|
67
|
-
|
|
88
|
+
if(userData.roadSpeedLimits){
|
|
89
|
+
events.push(...await getHereEvents(devices, routes, userData.maxSpeedThreshold))
|
|
90
|
+
}
|
|
68
91
|
|
|
69
|
-
if(
|
|
70
|
-
|
|
71
|
-
|
|
92
|
+
if(!events.length) {
|
|
93
|
+
//empty report
|
|
94
|
+
return { devices: [] }
|
|
72
95
|
}
|
|
73
96
|
|
|
74
|
-
return
|
|
97
|
+
return {
|
|
98
|
+
devices: await processDevices(from, to, devices, events, routes),
|
|
99
|
+
xpert: devices.filter(d => d && d.attributes && d.attributes.xpert).length > 0
|
|
100
|
+
}
|
|
75
101
|
}
|
|
76
102
|
|
|
77
|
-
async function createSpeedingReportByDriver(from, to
|
|
78
|
-
const
|
|
79
|
-
console.log(
|
|
103
|
+
async function createSpeedingReportByDriver(from, to) {
|
|
104
|
+
const devices = await drivers.devicesByDriver(traccarInstance, from, to, userData.drivers, userData.devices)
|
|
105
|
+
console.log('Devices with driver',devices.length)
|
|
80
106
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const devices = userData.devices.filter(d => deviceIds.includes(d.id))
|
|
85
|
-
eventsData = await getReportData(from, to, devices)
|
|
107
|
+
if(!devices.length) {
|
|
108
|
+
//empty report
|
|
109
|
+
return { drivers: [] }
|
|
86
110
|
}
|
|
87
111
|
|
|
88
|
-
const
|
|
89
|
-
|
|
112
|
+
const routes = await traccar.getRoute(traccarInstance, from, to, devices)
|
|
113
|
+
const events = []
|
|
114
|
+
if(!userData.useVehicleSpeedLimit && userData.customSpeed){
|
|
115
|
+
events.push(...await getCustomSpeedLimitEvents(devices, routes))
|
|
116
|
+
} else {
|
|
117
|
+
events.push(...await traccar.getEvents(traccarInstance, from, to, devices, eventTypes))
|
|
90
118
|
}
|
|
91
119
|
|
|
92
|
-
|
|
120
|
+
if(userData.roadSpeedLimits){
|
|
121
|
+
events.push(...await getHereEvents(devices, routes, userData.maxSpeedThreshold))
|
|
122
|
+
}
|
|
93
123
|
|
|
94
|
-
|
|
124
|
+
if(!events.length) {
|
|
125
|
+
//empty report
|
|
126
|
+
return { drivers: [] }
|
|
127
|
+
}
|
|
95
128
|
|
|
96
|
-
|
|
97
|
-
console.log(allData.drivers.length)
|
|
98
|
-
return allData
|
|
129
|
+
return {drivers: await processDrivers(from, to, events, routes)}
|
|
99
130
|
}
|
|
100
131
|
|
|
101
|
-
async function processDrivers(from, to,
|
|
102
|
-
console.log(data)
|
|
132
|
+
async function processDrivers(from, to, events, routes) {
|
|
103
133
|
const driversResult = []
|
|
104
|
-
const alertsWithPosition = data.filter(a => a.position)
|
|
105
|
-
|
|
106
|
-
drivers.forEach(d => {
|
|
107
|
-
const alerts = alertsWithPosition.filter(e => e.position.attributes.driverUniqueId === d.uniqueId)
|
|
108
134
|
|
|
109
|
-
|
|
135
|
+
await findEventsPosition(from, to, userData.devices, events, routes)
|
|
110
136
|
|
|
137
|
+
userData.drivers.forEach(d => {
|
|
138
|
+
const driverEvents = events.filter(e => e.position && e.position.attributes.driverUniqueId === d.uniqueId)
|
|
139
|
+
if (driverEvents.length > 0) {
|
|
140
|
+
driverEvents.sort((a, b) => a.positionId - b.positionId)
|
|
111
141
|
driversResult.push({
|
|
112
142
|
driver: d,
|
|
113
143
|
from: from,
|
|
114
144
|
to: to,
|
|
115
|
-
alerts:
|
|
116
|
-
maxSpeed:
|
|
145
|
+
alerts: driverEvents,
|
|
146
|
+
maxSpeed: driverEvents.reduce((a, b) => {
|
|
117
147
|
return a > b.attributes.maxSpeed ? a : b.attributes.maxSpeed
|
|
118
148
|
}, 0),
|
|
119
149
|
})
|
|
@@ -123,40 +153,20 @@ async function processDrivers(from, to, drivers, data) {
|
|
|
123
153
|
return driversResult
|
|
124
154
|
}
|
|
125
155
|
|
|
126
|
-
async function
|
|
127
|
-
const types = ['deviceOverspeed']
|
|
128
|
-
let data = []
|
|
129
|
-
const arrayOfArrays = automaticReports.sliceArray(devices)
|
|
130
|
-
for (const a of arrayOfArrays) {
|
|
131
|
-
try {
|
|
132
|
-
const devices = a.map(d => d.id)
|
|
133
|
-
console.log('reportsEventsGet', from, to, devices, null, types)
|
|
134
|
-
const response = await traccarInstance.reports.reportsEventsGet(from, to, devices, null, types)
|
|
135
|
-
data = data.concat(response.data)
|
|
136
|
-
} catch (e) {
|
|
137
|
-
console.error(e)
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
console.log('Alerts:', data.length)
|
|
141
|
-
return data
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async function processDevices(from, to, devices, geofences, drivers, data, roadSpeedLimits) {
|
|
156
|
+
async function processDevices(from, to, devices, events, routes) {
|
|
145
157
|
const devicesResult = []
|
|
146
|
-
|
|
147
|
-
await getEventsPosition(from , to, devices, geofences, drivers, data, roadSpeedLimits)
|
|
158
|
+
await findEventsPosition(from, to, devices, events, routes)
|
|
148
159
|
|
|
149
160
|
for (const d of devices) {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const alertsWithPosition = alerts.filter(a => a.position).sort((a,b) => a.positionId - b.positionId)
|
|
161
|
+
const deviceEvents = events.filter(e => e.deviceId === d.id && e.position)
|
|
162
|
+
if (deviceEvents.length > 0) {
|
|
163
|
+
deviceEvents.sort((a, b) => a.positionId - b.positionId)
|
|
154
164
|
devicesResult.push({
|
|
155
165
|
device: d,
|
|
156
166
|
from: from,
|
|
157
167
|
to: to,
|
|
158
|
-
alerts:
|
|
159
|
-
maxSpeed:
|
|
168
|
+
alerts: deviceEvents,
|
|
169
|
+
maxSpeed: deviceEvents.reduce((a, b) => {
|
|
160
170
|
return a > (b && b.attributes && b.attributes.maxSpeed) ? a : (b && b.attributes && b.attributes.maxSpeed)
|
|
161
171
|
}, 0),
|
|
162
172
|
})
|
|
@@ -166,93 +176,137 @@ async function processDevices(from, to, devices, geofences, drivers, data, roadS
|
|
|
166
176
|
return devicesResult
|
|
167
177
|
}
|
|
168
178
|
|
|
169
|
-
async function
|
|
179
|
+
async function findEventsPosition(from, to, devices, events, routes){
|
|
170
180
|
for (const d of devices) {
|
|
181
|
+
let deviceEvents = events.filter(e => e.deviceId === d.id && e.positionId)
|
|
182
|
+
if (deviceEvents.length > 0) {
|
|
183
|
+
deviceEvents.sort((a,b) => a.positionId-b.positionId )
|
|
184
|
+
const positions = routes.filter(p => p.deviceId === d.id)
|
|
171
185
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (alerts.length > 0 || roadSpeedLimits) {
|
|
175
|
-
const response = await traccarInstance.reports.reportsRouteGet(from, to, [d.id])
|
|
176
|
-
const positions = response.data
|
|
177
|
-
|
|
178
|
-
if (roadSpeedLimits && positions.length) {
|
|
179
|
-
try {
|
|
180
|
-
await new Promise(res => setTimeout(res, 1000))
|
|
181
|
-
const results = await here.routeMatch(positions)
|
|
182
|
-
const hereAlerts = results.map(r => {
|
|
183
|
-
const position = positions.find(p => new Date(p.fixTime).getTime() === r.timestamp)
|
|
184
|
-
return {
|
|
185
|
-
...r,
|
|
186
|
-
roadSpeedLimit: r.speedLimit,
|
|
187
|
-
deviceId: d.id,
|
|
188
|
-
position,
|
|
189
|
-
positionId: position && position.id,
|
|
190
|
-
attributes: {speedLimit: r.speedLimit, speed: r.currentSpeedKmh / 1.85200}
|
|
191
|
-
}
|
|
192
|
-
})
|
|
193
|
-
console.log('hereAlerts', hereAlerts)
|
|
194
|
-
const reduced = hereAlerts.reduce((acc, cur, idx, src) => {
|
|
195
|
-
if (idx === 1) {
|
|
196
|
-
return [cur]
|
|
197
|
-
}
|
|
198
|
-
if (cur.timestamp - src[idx - 1].timestamp > 300000 || cur.roadSpeedLimit !== src[idx - 1].roadSpeedLimit) {
|
|
199
|
-
return acc.concat(cur)
|
|
200
|
-
}
|
|
201
|
-
return acc
|
|
202
|
-
})
|
|
203
|
-
data.push(...reduced)
|
|
204
|
-
alerts = data.filter(t => t.deviceId === d.id)
|
|
205
|
-
} catch (e) {
|
|
206
|
-
console.error(e)
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
for (const a of alerts) {
|
|
186
|
+
for (const a of deviceEvents) {
|
|
211
187
|
let pIndex = positions.findIndex(p => p.id === a.positionId)
|
|
212
188
|
if (pIndex > 0) {
|
|
213
|
-
a.position = positions[pIndex]
|
|
214
189
|
let geofence = null
|
|
215
|
-
|
|
216
190
|
if (a.geofenceId) {
|
|
217
|
-
geofence = geofences.find(g => g.id === a.geofenceId)
|
|
191
|
+
geofence = userData.geofences.find(g => g.id === a.geofenceId)
|
|
218
192
|
if (geofence) {
|
|
219
193
|
a.geofenceName = geofence.name + ' (' + Math.round(a.attributes.speedLimit * 1.85200) + ' Km/h)'
|
|
220
194
|
}
|
|
221
195
|
}
|
|
222
196
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
break
|
|
237
|
-
} else {
|
|
238
|
-
if (maxSpeed < endEventPosition.speed) {
|
|
239
|
-
maxSpeed = endEventPosition.speed
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
197
|
+
a.position = positions[pIndex]
|
|
198
|
+
pIndex = calculateEventData(positions, pIndex, a, d, geofence)
|
|
199
|
+
positions.slice(pIndex)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function getCustomSpeedLimitEvents(devices, routes){
|
|
207
|
+
console.log('custom speed limit events')
|
|
208
|
+
const events = []
|
|
209
|
+
const maxSpeed = userData.customSpeed / 1.85200
|
|
243
210
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
211
|
+
for (const d of devices) {
|
|
212
|
+
const positions = routes.filter(p => p.deviceId === d.id)
|
|
213
|
+
let eventIsActive = false
|
|
214
|
+
|
|
215
|
+
for (const position of positions) {
|
|
216
|
+
if(position.speed > maxSpeed && !eventIsActive){
|
|
217
|
+
const event = {
|
|
218
|
+
positionId: position && position.id,
|
|
219
|
+
deviceId: d.id,
|
|
220
|
+
attributes: {
|
|
221
|
+
speedLimit: maxSpeed,
|
|
222
|
+
speed: position.speed
|
|
247
223
|
}
|
|
224
|
+
}
|
|
225
|
+
events.push(event)
|
|
226
|
+
eventIsActive = true
|
|
227
|
+
} else if (position.speed <= maxSpeed) {
|
|
228
|
+
eventIsActive = false
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
248
232
|
|
|
249
|
-
|
|
233
|
+
return events
|
|
234
|
+
}
|
|
250
235
|
|
|
251
|
-
|
|
236
|
+
async function getHereEvents(devices, routes, threshold){
|
|
237
|
+
console.log('here speed limit events')
|
|
238
|
+
const events = []
|
|
239
|
+
for (const d of devices) {
|
|
240
|
+
const positions = routes.filter(p => p.deviceId === d.id)
|
|
241
|
+
if (!positions.length) {
|
|
242
|
+
console.log('no positions on device', d.name)
|
|
243
|
+
continue
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
const results = await here.routeMatch(positions)
|
|
247
|
+
const hereAlerts = results.filter(r => r.currentSpeedKmh > (parseInt(r.speedLimit) + threshold)).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}
|
|
252
256
|
}
|
|
257
|
+
})
|
|
258
|
+
if (!hereAlerts.length) {
|
|
259
|
+
console.log('empty array after filter on device', d.name)
|
|
260
|
+
continue
|
|
253
261
|
}
|
|
262
|
+
const reduced = hereAlerts.reduce((acc, cur, idx, src) => {
|
|
263
|
+
if (idx === 1) {
|
|
264
|
+
return [cur]
|
|
265
|
+
}
|
|
266
|
+
if (cur.timestamp - src[idx - 1].timestamp > 300000 || cur.roadSpeedLimit !== src[idx - 1].roadSpeedLimit) {
|
|
267
|
+
return acc.concat(cur)
|
|
268
|
+
}
|
|
269
|
+
return acc
|
|
270
|
+
})
|
|
271
|
+
events.push(...reduced)
|
|
272
|
+
} catch (e) {
|
|
273
|
+
console.error(e)
|
|
254
274
|
}
|
|
255
275
|
}
|
|
276
|
+
return events
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function calculateEventData(positions, pIndex, alert, device, geofence){
|
|
280
|
+
let endEventPosition = alert.position
|
|
281
|
+
let maxSpeed = alert.position.speed
|
|
282
|
+
let dist = 0
|
|
283
|
+
while (pIndex + 1 < positions.length) {
|
|
284
|
+
pIndex++
|
|
285
|
+
|
|
286
|
+
dist += distance(point([endEventPosition.longitude, endEventPosition.latitude]),
|
|
287
|
+
point([positions[pIndex].longitude, positions[pIndex].latitude]))
|
|
288
|
+
endEventPosition = positions[pIndex]
|
|
289
|
+
if (endEventPosition.speed <= alert.attributes.speedLimit || (geofence && isOutsideGeofence(geofence, endEventPosition))) {
|
|
290
|
+
alert.eventTime = new Date(endEventPosition.fixTime) - new Date(alert.position.fixTime)
|
|
291
|
+
alert.attributes.maxSpeed = maxSpeed
|
|
292
|
+
alert.distance = dist
|
|
293
|
+
break
|
|
294
|
+
} else {
|
|
295
|
+
if (maxSpeed < endEventPosition.speed) {
|
|
296
|
+
maxSpeed = endEventPosition.speed
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
if (alert.position.attributes.driverUniqueId) {
|
|
303
|
+
const driver = userData.drivers.find(d => d.uniqueId === alert.position.attributes.driverUniqueId)
|
|
304
|
+
alert.driver = driver && driver.name
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
alert.deviceName = device.name
|
|
308
|
+
|
|
309
|
+
return pIndex
|
|
256
310
|
}
|
|
257
311
|
|
|
258
312
|
function isOutsideGeofence(geofence, endEventPosition) {
|
|
@@ -261,10 +315,11 @@ function isOutsideGeofence(geofence, endEventPosition) {
|
|
|
261
315
|
return turf.inside(point, v)
|
|
262
316
|
}
|
|
263
317
|
|
|
264
|
-
function exportSpeedingReportToPDF(userData, reportData) {
|
|
318
|
+
async function exportSpeedingReportToPDF(userData, reportData) {
|
|
265
319
|
console.log('Export to PDF')
|
|
266
320
|
|
|
267
321
|
const lang = userData.user.attributes.lang
|
|
322
|
+
const timezone = userData.user.attributes.timezone
|
|
268
323
|
const translations = messages[lang] ? messages[lang] : messages['en-GB']
|
|
269
324
|
|
|
270
325
|
const overspeedData = userData.byDriver ? reportData.drivers : reportData.devices
|
|
@@ -278,7 +333,7 @@ function exportSpeedingReportToPDF(userData, reportData) {
|
|
|
278
333
|
translations.report.duration,
|
|
279
334
|
]
|
|
280
335
|
|
|
281
|
-
if(userData.
|
|
336
|
+
if(userData.roadSpeedLimits) {
|
|
282
337
|
headers.splice(2, 0, translations.report.roadSpeedLimit)
|
|
283
338
|
}
|
|
284
339
|
|
|
@@ -291,8 +346,7 @@ function exportSpeedingReportToPDF(userData, reportData) {
|
|
|
291
346
|
if (overspeedData) {
|
|
292
347
|
let first = true
|
|
293
348
|
const doc = new jsPDF.jsPDF('l');
|
|
294
|
-
doc.
|
|
295
|
-
doc.text(translations.report.titleSpeedingReport, 15, 15 )
|
|
349
|
+
await headerFromUser(doc, translations.report.titleSpeedingReport, userData.user)
|
|
296
350
|
|
|
297
351
|
overspeedData.forEach(d => {
|
|
298
352
|
try {
|
|
@@ -308,13 +362,13 @@ function exportSpeedingReportToPDF(userData, reportData) {
|
|
|
308
362
|
first = false
|
|
309
363
|
space = 10
|
|
310
364
|
}
|
|
311
|
-
doc.setFontSize(
|
|
365
|
+
doc.setFontSize(13)
|
|
312
366
|
doc.text(name, 20, space+20 )
|
|
313
|
-
doc.setFontSize(
|
|
367
|
+
doc.setFontSize(11)
|
|
314
368
|
doc.text(group ? translations.report.group + ': ' + group.name : '', 150, space+20 )
|
|
315
|
-
doc.text(
|
|
369
|
+
doc.text(convertToLocaleString(d.from, lang, timezone) + ' - ' + convertToLocaleString(d.to, lang, timezone), 20, space+25 )
|
|
316
370
|
if (d.alerts[0] && !userData.byDriver) {
|
|
317
|
-
doc.text(translations.report.speedLimit + ": " + Math.round(d.alerts[0].attributes.speedLimit * 1.85200) + ' Km/h', 20, space + 30)
|
|
371
|
+
doc.text(translations.report.speedLimit + ": " + ((userData.useVehicleSpeedLimit || !userData.customSpeed) ? Math.round(d.alerts[0].attributes.speedLimit * 1.85200) : userData.customSpeed) + ' Km/h', 20, space + 30)
|
|
318
372
|
}
|
|
319
373
|
d.alerts.map(a => {
|
|
320
374
|
const temp = [
|
|
@@ -327,7 +381,7 @@ function exportSpeedingReportToPDF(userData, reportData) {
|
|
|
327
381
|
userData.byDriver ? a.deviceName : a.driver
|
|
328
382
|
]
|
|
329
383
|
|
|
330
|
-
if(userData.
|
|
384
|
+
if(userData.roadSpeedLimits){
|
|
331
385
|
temp.splice(2, 0, a.roadSpeedLimit)
|
|
332
386
|
}
|
|
333
387
|
|
|
@@ -343,26 +397,34 @@ function exportSpeedingReportToPDF(userData, reportData) {
|
|
|
343
397
|
convertMS(d.alerts.reduce((a, b) => a + b.eventTime, 0), true)
|
|
344
398
|
]
|
|
345
399
|
|
|
346
|
-
if(userData.
|
|
400
|
+
if(userData.roadSpeedLimits){
|
|
347
401
|
footValues.splice(2, 0, '')
|
|
348
402
|
}
|
|
349
403
|
|
|
404
|
+
doc.addFileToVFS("Amiri-Regular.ttf", AmiriRegular());
|
|
405
|
+
doc.addFont("Amiri-Regular.ttf", "Amiri", "normal");
|
|
406
|
+
|
|
407
|
+
const style = getStyle(getUserPartner(userData.user))
|
|
350
408
|
doc.autoTable({
|
|
351
409
|
head: [headers],
|
|
352
410
|
body: data,
|
|
353
411
|
foot: [footValues],
|
|
354
412
|
showFoot: 'lastPage',
|
|
355
413
|
headStyles: {
|
|
356
|
-
fillColor:
|
|
357
|
-
textColor:
|
|
414
|
+
fillColor: style.pdfHeaderColor,
|
|
415
|
+
textColor: style.pdfHeaderTextColor,
|
|
416
|
+
fontSize: 10
|
|
358
417
|
},
|
|
359
418
|
bodyStyles: {
|
|
360
|
-
fillColor:
|
|
361
|
-
textColor:
|
|
419
|
+
fillColor: style.pdfBodyColor,
|
|
420
|
+
textColor: style.pdfBodyTextColor,
|
|
421
|
+
fontSize: 8,
|
|
422
|
+
font: timezone === 'Asia/Qatar' ? "Amiri" : ''
|
|
362
423
|
},
|
|
363
424
|
footStyles: {
|
|
364
|
-
fillColor:
|
|
365
|
-
textColor:
|
|
425
|
+
fillColor: style.pdfFooterColor,
|
|
426
|
+
textColor: style.pdfFooterTextColor,
|
|
427
|
+
fontSize: 9
|
|
366
428
|
},
|
|
367
429
|
startY: space+35 });
|
|
368
430
|
} catch (e) {
|
|
@@ -398,7 +460,7 @@ function exportSpeedingReportToExcel(userData, reportData) {
|
|
|
398
460
|
userData.byDriver ? {label: translations.report.vehicle, value:'name'} : {label: translations.report.driver, value:'driver'}
|
|
399
461
|
]
|
|
400
462
|
|
|
401
|
-
if (userData.
|
|
463
|
+
if (userData.roadSpeedLimits){
|
|
402
464
|
headers.splice(3, 0, {label: translations.report.roadSpeedLimit, value:'roadSpeedLimit'})
|
|
403
465
|
}
|
|
404
466
|
|
|
@@ -434,10 +496,7 @@ function deviceName(device){
|
|
|
434
496
|
}
|
|
435
497
|
|
|
436
498
|
function getAlertDate(row, user) {
|
|
437
|
-
return
|
|
438
|
-
timeZone: user.attributes.timezone,
|
|
439
|
-
hour12: false
|
|
440
|
-
})
|
|
499
|
+
return convertToLocaleString(row.position.fixTime, user.attributes.lang, user.attributes.timezone)
|
|
441
500
|
}
|
|
442
501
|
|
|
443
502
|
function getMaxSpeed(data) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const {SessionApi} = require("traccar-api");
|
|
2
|
+
const axios = require('axios')
|
|
3
|
+
const axiosCookieJarSupport = require('axios-cookiejar-support').default;
|
|
4
|
+
const tough = require('tough-cookie');
|
|
5
|
+
const Index = require('../index');
|
|
6
|
+
const cookieJar = new tough.CookieJar();
|
|
7
|
+
const traccarConfig = {
|
|
8
|
+
basePath: 'https://api.pinme.io/api',
|
|
9
|
+
baseOptions: {
|
|
10
|
+
withCredentials: true,
|
|
11
|
+
jar: cookieJar
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
axiosCookieJarSupport(axios)
|
|
16
|
+
|
|
17
|
+
exports.getReports = async() => {
|
|
18
|
+
await new SessionApi(traccarConfig, null, axios).sessionPost(process.env.email, process.env.password)
|
|
19
|
+
return new Index(traccarConfig, axios)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|