fleetmap-reports 1.0.288 → 1.0.289
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/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 +232 -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,150 @@
|
|
|
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) {
|
|
22
|
+
console.log('Create SpeedingReport')
|
|
18
23
|
traccarInstance = traccar
|
|
19
|
-
|
|
24
|
+
userData = reportUserData
|
|
20
25
|
const reportData = []
|
|
21
26
|
|
|
22
|
-
if(userData.byDriver){
|
|
23
|
-
|
|
27
|
+
if (userData.byDriver) {
|
|
28
|
+
console.log("ByDriver")
|
|
29
|
+
const allData = await createSpeedingReportByDriver(from, to)
|
|
24
30
|
reportData.push(allData)
|
|
25
31
|
}
|
|
26
|
-
else if(userData.byGroup){
|
|
32
|
+
else if (userData.byGroup) {
|
|
27
33
|
console.log("ByGroup")
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
xpert: devices.filter(d => d.attributes.xpert).length > 0
|
|
36
|
-
}
|
|
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
|
+
}
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
return reportData
|
|
43
|
+
}
|
|
39
44
|
|
|
40
|
-
|
|
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
|
+
}
|
|
41
56
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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)
|
|
46
65
|
}
|
|
47
66
|
}
|
|
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
67
|
}
|
|
55
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
|
+
|
|
56
75
|
return reportData
|
|
57
76
|
}
|
|
58
77
|
|
|
59
|
-
async function createSpeedingReportByDevice(from, to,
|
|
60
|
-
|
|
61
|
-
const withoutGroupDevices = userData.byGroup ? userData.devices.filter(d => !groupIds.includes(d.groupId)) : userData.devices
|
|
78
|
+
async function createSpeedingReportByDevice(from, to, devices) {
|
|
79
|
+
devices.sort((a, b) => (a.name > b.name) ? 1 : -1)
|
|
62
80
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
+
}
|
|
66
88
|
|
|
67
|
-
|
|
89
|
+
if(userData.roadSpeedLimits){
|
|
90
|
+
events.push(...await getHereEvents(devices, routes, userData.maxSpeedThreshold))
|
|
91
|
+
}
|
|
68
92
|
|
|
69
|
-
if(
|
|
70
|
-
|
|
71
|
-
|
|
93
|
+
if(!events.length) {
|
|
94
|
+
//empty report
|
|
95
|
+
return { devices: [] }
|
|
72
96
|
}
|
|
73
97
|
|
|
74
|
-
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
|
+
}
|
|
75
102
|
}
|
|
76
103
|
|
|
77
|
-
async function createSpeedingReportByDriver(from, to
|
|
78
|
-
const
|
|
79
|
-
console.log(
|
|
104
|
+
async function createSpeedingReportByDriver(from, to) {
|
|
105
|
+
const devices = await drivers.devicesByDriver(traccarInstance, from, to, userData.drivers, userData.devices)
|
|
106
|
+
console.log('Devices with driver',devices.length)
|
|
80
107
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const devices = userData.devices.filter(d => deviceIds.includes(d.id))
|
|
85
|
-
eventsData = await getReportData(from, to, devices)
|
|
108
|
+
if(!devices.length) {
|
|
109
|
+
//empty report
|
|
110
|
+
return { drivers: [] }
|
|
86
111
|
}
|
|
87
112
|
|
|
88
|
-
const
|
|
89
|
-
|
|
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))
|
|
90
119
|
}
|
|
91
120
|
|
|
92
|
-
|
|
121
|
+
if(userData.roadSpeedLimits){
|
|
122
|
+
events.push(...await getHereEvents(devices, routes, userData.maxSpeedThreshold))
|
|
123
|
+
}
|
|
93
124
|
|
|
94
|
-
|
|
125
|
+
if(!events.length) {
|
|
126
|
+
//empty report
|
|
127
|
+
return { drivers: [] }
|
|
128
|
+
}
|
|
95
129
|
|
|
96
|
-
|
|
97
|
-
console.log(allData.drivers.length)
|
|
98
|
-
return allData
|
|
130
|
+
return {drivers: await processDrivers(from, to, events, routes)}
|
|
99
131
|
}
|
|
100
132
|
|
|
101
|
-
async function processDrivers(from, to,
|
|
102
|
-
console.log(data)
|
|
133
|
+
async function processDrivers(from, to, events, routes) {
|
|
103
134
|
const driversResult = []
|
|
104
|
-
const alertsWithPosition = data.filter(a => a.position)
|
|
105
135
|
|
|
106
|
-
|
|
107
|
-
const alerts = alertsWithPosition.filter(e => e.position.attributes.driverUniqueId === d.uniqueId)
|
|
108
|
-
|
|
109
|
-
if (alerts.length > 0) {
|
|
136
|
+
await findEventsPosition(from, to, userData.devices, events, routes)
|
|
110
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)
|
|
111
142
|
driversResult.push({
|
|
112
143
|
driver: d,
|
|
113
144
|
from: from,
|
|
114
145
|
to: to,
|
|
115
|
-
alerts:
|
|
116
|
-
maxSpeed:
|
|
146
|
+
alerts: driverEvents,
|
|
147
|
+
maxSpeed: driverEvents.reduce((a, b) => {
|
|
117
148
|
return a > b.attributes.maxSpeed ? a : b.attributes.maxSpeed
|
|
118
149
|
}, 0),
|
|
119
150
|
})
|
|
@@ -123,40 +154,20 @@ async function processDrivers(from, to, drivers, data) {
|
|
|
123
154
|
return driversResult
|
|
124
155
|
}
|
|
125
156
|
|
|
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) {
|
|
157
|
+
async function processDevices(from, to, devices, events, routes) {
|
|
145
158
|
const devicesResult = []
|
|
146
|
-
|
|
147
|
-
await getEventsPosition(from , to, devices, geofences, drivers, data, roadSpeedLimits)
|
|
159
|
+
await findEventsPosition(from, to, devices, events, routes)
|
|
148
160
|
|
|
149
161
|
for (const d of devices) {
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const alertsWithPosition = alerts.filter(a => a.position).sort((a,b) => a.positionId - b.positionId)
|
|
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)
|
|
154
165
|
devicesResult.push({
|
|
155
166
|
device: d,
|
|
156
167
|
from: from,
|
|
157
168
|
to: to,
|
|
158
|
-
alerts:
|
|
159
|
-
maxSpeed:
|
|
169
|
+
alerts: deviceEvents,
|
|
170
|
+
maxSpeed: deviceEvents.reduce((a, b) => {
|
|
160
171
|
return a > (b && b.attributes && b.attributes.maxSpeed) ? a : (b && b.attributes && b.attributes.maxSpeed)
|
|
161
172
|
}, 0),
|
|
162
173
|
})
|
|
@@ -166,93 +177,129 @@ async function processDevices(from, to, devices, geofences, drivers, data, roadS
|
|
|
166
177
|
return devicesResult
|
|
167
178
|
}
|
|
168
179
|
|
|
169
|
-
async function
|
|
180
|
+
async function findEventsPosition(from, to, devices, events, routes){
|
|
170
181
|
for (const d of devices) {
|
|
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)
|
|
171
186
|
|
|
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) {
|
|
187
|
+
for (const a of deviceEvents) {
|
|
211
188
|
let pIndex = positions.findIndex(p => p.id === a.positionId)
|
|
212
189
|
if (pIndex > 0) {
|
|
213
|
-
a.position = positions[pIndex]
|
|
214
190
|
let geofence = null
|
|
215
|
-
|
|
216
191
|
if (a.geofenceId) {
|
|
217
|
-
geofence = geofences.find(g => g.id === a.geofenceId)
|
|
192
|
+
geofence = userData.geofences.find(g => g.id === a.geofenceId)
|
|
218
193
|
if (geofence) {
|
|
219
194
|
a.geofenceName = geofence.name + ' (' + Math.round(a.attributes.speedLimit * 1.85200) + ' Km/h)'
|
|
220
195
|
}
|
|
221
196
|
}
|
|
222
197
|
|
|
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
|
-
}
|
|
198
|
+
a.position = positions[pIndex]
|
|
199
|
+
pIndex = calculateEventData(positions, pIndex, a, d, geofence)
|
|
200
|
+
positions.slice(pIndex)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function getCustomSpeedLimitEvents(devices, routes){
|
|
208
|
+
console.log('custom speed limit events')
|
|
209
|
+
const events = []
|
|
210
|
+
const maxSpeed = userData.customSpeed / 1.85200
|
|
243
211
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
212
|
+
for (const d of devices) {
|
|
213
|
+
const positions = routes.filter(p => p.deviceId === d.id)
|
|
214
|
+
let eventIsActive = false
|
|
215
|
+
|
|
216
|
+
for (const position of positions) {
|
|
217
|
+
if(position.speed > maxSpeed && !eventIsActive){
|
|
218
|
+
const event = {
|
|
219
|
+
positionId: position && position.id,
|
|
220
|
+
deviceId: d.id,
|
|
221
|
+
attributes: {
|
|
222
|
+
speedLimit: maxSpeed,
|
|
223
|
+
speed: position.speed
|
|
247
224
|
}
|
|
225
|
+
}
|
|
226
|
+
events.push(event)
|
|
227
|
+
eventIsActive = true
|
|
228
|
+
} else if (position.speed <= maxSpeed) {
|
|
229
|
+
eventIsActive = false
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
248
233
|
|
|
249
|
-
|
|
234
|
+
return events
|
|
235
|
+
}
|
|
250
236
|
|
|
251
|
-
|
|
237
|
+
async function getHereEvents(devices, routes, threshold){
|
|
238
|
+
console.log('here speed limit events')
|
|
239
|
+
const events = []
|
|
240
|
+
for (const d of devices) {
|
|
241
|
+
const positions = routes.filter(p => p.deviceId === d.id)
|
|
242
|
+
try {
|
|
243
|
+
const results = await here.routeMatch(positions)
|
|
244
|
+
const hereAlerts = results.filter(r => r.currentSpeedKmh > (parseInt(r.speedLimit) + threshold)).map(r => {
|
|
245
|
+
const position = positions.find(p => new Date(p.fixTime).getTime() === r.timestamp)
|
|
246
|
+
return {
|
|
247
|
+
...r,
|
|
248
|
+
roadSpeedLimit: r.speedLimit,
|
|
249
|
+
deviceId: d.id,
|
|
250
|
+
position,
|
|
251
|
+
positionId: position && position.id,
|
|
252
|
+
attributes: {speedLimit: r.speedLimit, speed: r.currentSpeedKmh / 1.85200}
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
const reduced = hereAlerts.reduce((acc, cur, idx, src) => {
|
|
256
|
+
if (idx === 1) {
|
|
257
|
+
return [cur]
|
|
252
258
|
}
|
|
259
|
+
if (cur.timestamp - src[idx - 1].timestamp > 300000 || cur.roadSpeedLimit !== src[idx - 1].roadSpeedLimit) {
|
|
260
|
+
return acc.concat(cur)
|
|
261
|
+
}
|
|
262
|
+
return acc
|
|
263
|
+
})
|
|
264
|
+
events.push(...reduced)
|
|
265
|
+
} catch (e) {
|
|
266
|
+
console.error(e)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return events
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function calculateEventData(positions, pIndex, alert, device, geofence){
|
|
273
|
+
let endEventPosition = alert.position
|
|
274
|
+
let maxSpeed = alert.position.speed
|
|
275
|
+
let dist = 0
|
|
276
|
+
while (pIndex + 1 < positions.length) {
|
|
277
|
+
pIndex++
|
|
278
|
+
|
|
279
|
+
dist += distance(point([endEventPosition.longitude, endEventPosition.latitude]),
|
|
280
|
+
point([positions[pIndex].longitude, positions[pIndex].latitude]))
|
|
281
|
+
endEventPosition = positions[pIndex]
|
|
282
|
+
if (endEventPosition.speed <= alert.attributes.speedLimit || (geofence && isOutsideGeofence(geofence, endEventPosition))) {
|
|
283
|
+
alert.eventTime = new Date(endEventPosition.fixTime) - new Date(alert.position.fixTime)
|
|
284
|
+
alert.attributes.maxSpeed = maxSpeed
|
|
285
|
+
alert.distance = dist
|
|
286
|
+
break
|
|
287
|
+
} else {
|
|
288
|
+
if (maxSpeed < endEventPosition.speed) {
|
|
289
|
+
maxSpeed = endEventPosition.speed
|
|
253
290
|
}
|
|
254
291
|
}
|
|
255
292
|
}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
if (alert.position.attributes.driverUniqueId) {
|
|
296
|
+
const driver = userData.drivers.find(d => d.uniqueId === alert.position.attributes.driverUniqueId)
|
|
297
|
+
alert.driver = driver && driver.name
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
alert.deviceName = device.name
|
|
301
|
+
|
|
302
|
+
return pIndex
|
|
256
303
|
}
|
|
257
304
|
|
|
258
305
|
function isOutsideGeofence(geofence, endEventPosition) {
|
|
@@ -261,10 +308,11 @@ function isOutsideGeofence(geofence, endEventPosition) {
|
|
|
261
308
|
return turf.inside(point, v)
|
|
262
309
|
}
|
|
263
310
|
|
|
264
|
-
function exportSpeedingReportToPDF(userData, reportData) {
|
|
311
|
+
async function exportSpeedingReportToPDF(userData, reportData) {
|
|
265
312
|
console.log('Export to PDF')
|
|
266
313
|
|
|
267
314
|
const lang = userData.user.attributes.lang
|
|
315
|
+
const timezone = userData.user.attributes.timezone
|
|
268
316
|
const translations = messages[lang] ? messages[lang] : messages['en-GB']
|
|
269
317
|
|
|
270
318
|
const overspeedData = userData.byDriver ? reportData.drivers : reportData.devices
|
|
@@ -278,7 +326,7 @@ function exportSpeedingReportToPDF(userData, reportData) {
|
|
|
278
326
|
translations.report.duration,
|
|
279
327
|
]
|
|
280
328
|
|
|
281
|
-
if(userData.
|
|
329
|
+
if(userData.roadSpeedLimits) {
|
|
282
330
|
headers.splice(2, 0, translations.report.roadSpeedLimit)
|
|
283
331
|
}
|
|
284
332
|
|
|
@@ -291,8 +339,7 @@ function exportSpeedingReportToPDF(userData, reportData) {
|
|
|
291
339
|
if (overspeedData) {
|
|
292
340
|
let first = true
|
|
293
341
|
const doc = new jsPDF.jsPDF('l');
|
|
294
|
-
doc.
|
|
295
|
-
doc.text(translations.report.titleSpeedingReport, 15, 15 )
|
|
342
|
+
await headerFromUser(doc, translations.report.titleSpeedingReport, userData.user)
|
|
296
343
|
|
|
297
344
|
overspeedData.forEach(d => {
|
|
298
345
|
try {
|
|
@@ -308,13 +355,13 @@ function exportSpeedingReportToPDF(userData, reportData) {
|
|
|
308
355
|
first = false
|
|
309
356
|
space = 10
|
|
310
357
|
}
|
|
311
|
-
doc.setFontSize(
|
|
358
|
+
doc.setFontSize(13)
|
|
312
359
|
doc.text(name, 20, space+20 )
|
|
313
|
-
doc.setFontSize(
|
|
360
|
+
doc.setFontSize(11)
|
|
314
361
|
doc.text(group ? translations.report.group + ': ' + group.name : '', 150, space+20 )
|
|
315
|
-
doc.text(
|
|
362
|
+
doc.text(convertToLocaleString(d.from, lang, timezone) + ' - ' + convertToLocaleString(d.to, lang, timezone), 20, space+25 )
|
|
316
363
|
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)
|
|
364
|
+
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
365
|
}
|
|
319
366
|
d.alerts.map(a => {
|
|
320
367
|
const temp = [
|
|
@@ -327,7 +374,7 @@ function exportSpeedingReportToPDF(userData, reportData) {
|
|
|
327
374
|
userData.byDriver ? a.deviceName : a.driver
|
|
328
375
|
]
|
|
329
376
|
|
|
330
|
-
if(userData.
|
|
377
|
+
if(userData.roadSpeedLimits){
|
|
331
378
|
temp.splice(2, 0, a.roadSpeedLimit)
|
|
332
379
|
}
|
|
333
380
|
|
|
@@ -343,26 +390,34 @@ function exportSpeedingReportToPDF(userData, reportData) {
|
|
|
343
390
|
convertMS(d.alerts.reduce((a, b) => a + b.eventTime, 0), true)
|
|
344
391
|
]
|
|
345
392
|
|
|
346
|
-
if(userData.
|
|
393
|
+
if(userData.roadSpeedLimits){
|
|
347
394
|
footValues.splice(2, 0, '')
|
|
348
395
|
}
|
|
349
396
|
|
|
397
|
+
doc.addFileToVFS("Amiri-Regular.ttf", AmiriRegular());
|
|
398
|
+
doc.addFont("Amiri-Regular.ttf", "Amiri", "normal");
|
|
399
|
+
|
|
400
|
+
const style = getStyle(getUserPartner(userData.user))
|
|
350
401
|
doc.autoTable({
|
|
351
402
|
head: [headers],
|
|
352
403
|
body: data,
|
|
353
404
|
foot: [footValues],
|
|
354
405
|
showFoot: 'lastPage',
|
|
355
406
|
headStyles: {
|
|
356
|
-
fillColor:
|
|
357
|
-
textColor:
|
|
407
|
+
fillColor: style.pdfHeaderColor,
|
|
408
|
+
textColor: style.pdfHeaderTextColor,
|
|
409
|
+
fontSize: 10
|
|
358
410
|
},
|
|
359
411
|
bodyStyles: {
|
|
360
|
-
fillColor:
|
|
361
|
-
textColor:
|
|
412
|
+
fillColor: style.pdfBodyColor,
|
|
413
|
+
textColor: style.pdfBodyTextColor,
|
|
414
|
+
fontSize: 8,
|
|
415
|
+
font: timezone === 'Asia/Qatar' ? "Amiri" : ''
|
|
362
416
|
},
|
|
363
417
|
footStyles: {
|
|
364
|
-
fillColor:
|
|
365
|
-
textColor:
|
|
418
|
+
fillColor: style.pdfFooterColor,
|
|
419
|
+
textColor: style.pdfFooterTextColor,
|
|
420
|
+
fontSize: 9
|
|
366
421
|
},
|
|
367
422
|
startY: space+35 });
|
|
368
423
|
} catch (e) {
|
|
@@ -398,7 +453,7 @@ function exportSpeedingReportToExcel(userData, reportData) {
|
|
|
398
453
|
userData.byDriver ? {label: translations.report.vehicle, value:'name'} : {label: translations.report.driver, value:'driver'}
|
|
399
454
|
]
|
|
400
455
|
|
|
401
|
-
if (userData.
|
|
456
|
+
if (userData.roadSpeedLimits){
|
|
402
457
|
headers.splice(3, 0, {label: translations.report.roadSpeedLimit, value:'roadSpeedLimit'})
|
|
403
458
|
}
|
|
404
459
|
|
|
@@ -434,10 +489,7 @@ function deviceName(device){
|
|
|
434
489
|
}
|
|
435
490
|
|
|
436
491
|
function getAlertDate(row, user) {
|
|
437
|
-
return
|
|
438
|
-
timeZone: user.attributes.timezone,
|
|
439
|
-
hour12: false
|
|
440
|
-
})
|
|
492
|
+
return convertToLocaleString(row.position.fixTime, user.attributes.lang, user.attributes.timezone)
|
|
441
493
|
}
|
|
442
494
|
|
|
443
495
|
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
|
+
|