fleetmap-reports 1.0.393 → 1.0.400
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 +10 -5
- package/src/activity-report.js +26 -8
- package/src/index.test.js +342 -328
- package/src/refueling-report.js +149 -147
- package/src/speeding-report.js +112 -111
- package/src/tests/index.js +14 -16
- package/src/util/utils.js +151 -153
- package/src/zone-report.js +6 -5
package/src/refueling-report.js
CHANGED
|
@@ -1,177 +1,179 @@
|
|
|
1
|
-
const helpers = require(
|
|
2
|
-
const automaticReports = require(
|
|
3
|
-
const {devicesToProcess} = require(
|
|
1
|
+
const helpers = require('@turf/helpers')
|
|
2
|
+
const automaticReports = require('./automaticReports')
|
|
3
|
+
const { devicesToProcess } = require('./util/device')
|
|
4
|
+
const distance = require('@turf/distance')
|
|
4
5
|
|
|
5
6
|
const positionsToCheck = 15
|
|
6
7
|
|
|
7
|
-
async function createRefuelingReport(from, to, userData, traccar) {
|
|
8
|
-
|
|
8
|
+
async function createRefuelingReport (from, to, userData, traccar) {
|
|
9
|
+
console.log('Create RefuelingReport')
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
const reportData = []
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
const allData = {
|
|
14
|
+
devices: [],
|
|
15
|
+
from,
|
|
16
|
+
to
|
|
17
|
+
}
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
const devices = devicesToProcess(userData).filter(d => automaticReports.deviceWithFuelInfo(d))
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
console.log('Devices:' + devices.length)
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const arrayOfArrays = automaticReports.sliceArray(devices)
|
|
24
|
+
let data = []
|
|
25
|
+
for (const a of arrayOfArrays) {
|
|
26
|
+
const response = await traccar.reports.reportsRouteGet(from, to, a.map(d => d.id))
|
|
27
|
+
data = data.concat(response.data)
|
|
28
|
+
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
console.log('Locations:' + data.length)
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
if (data.length === 0) {
|
|
33
|
+
return reportData
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
allData.totalDevices = 0
|
|
37
|
+
allData.totalRefueling = 0
|
|
38
|
+
allData.totalRefuelingLiters = 0
|
|
39
|
+
|
|
40
|
+
for (const d of devices) {
|
|
41
|
+
const positions = data.filter(t => t.deviceId === d.id)
|
|
42
|
+
|
|
43
|
+
if (positions.length > 0) {
|
|
44
|
+
const refuelingPositions = await this.calculateRefuelingPositions(userData, d, { route: positions })
|
|
45
|
+
|
|
46
|
+
if (userData.drivers.length > 0) {
|
|
47
|
+
refuelingPositions.forEach(p => {
|
|
48
|
+
const geofence = findNearestPOI(p.position, userData.geofences)
|
|
49
|
+
if (geofence) {
|
|
50
|
+
p.position.geofenceName = geofence.name
|
|
51
|
+
}
|
|
52
|
+
const driver = userData.drivers.find(d => d.uniqueId === p.position.attributes.driverUniqueId)
|
|
53
|
+
if (driver) {
|
|
54
|
+
p.position.driverName = driver.name
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
}
|
|
34
58
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (userData.drivers.length > 0) {
|
|
46
|
-
refuelingPositions.forEach(p => {
|
|
47
|
-
const geofence = findNearestPOI(p.position, userData.geofences)
|
|
48
|
-
if(geofence) {
|
|
49
|
-
p.position.geofenceName = geofence.name
|
|
50
|
-
}
|
|
51
|
-
const driver = userData.drivers.find(d => d.uniqueId === p.position.attributes.driverUniqueId)
|
|
52
|
-
if (driver) {
|
|
53
|
-
p.position.driverName = driver.name
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (refuelingPositions.length > 0) {
|
|
59
|
-
allData.devices.push({
|
|
60
|
-
device: d,
|
|
61
|
-
refuelings: refuelingPositions,
|
|
62
|
-
})
|
|
63
|
-
allData.totalDevices = allData.totalDevices+1
|
|
64
|
-
allData.totalRefueling = allData.totalRefueling + refuelingPositions.length
|
|
65
|
-
allData.totalRefuelingLiters = allData.totalRefuelingLiters + refuelingPositions.reduce((a, b) => a + b.diff, 0)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
59
|
+
if (refuelingPositions.length > 0) {
|
|
60
|
+
allData.devices.push({
|
|
61
|
+
device: d,
|
|
62
|
+
refuelings: refuelingPositions
|
|
63
|
+
})
|
|
64
|
+
allData.totalDevices = allData.totalDevices + 1
|
|
65
|
+
allData.totalRefueling = allData.totalRefueling + refuelingPositions.length
|
|
66
|
+
allData.totalRefuelingLiters = allData.totalRefuelingLiters + refuelingPositions.reduce((a, b) => a + b.diff, 0)
|
|
67
|
+
}
|
|
68
68
|
}
|
|
69
|
+
}
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
reportData.push(allData)
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
return reportData
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
function findNearestPOI(position, geofences) {
|
|
76
|
-
|
|
76
|
+
function findNearestPOI (position, geofences) {
|
|
77
|
+
if (geofences.length === 0) { return null }
|
|
77
78
|
|
|
78
|
-
|
|
79
|
+
if (!position) { return null }
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
id: g.id,
|
|
86
|
-
distance: Math.round(coordsDistance(parseFloat(coord[1]), parseFloat(coord[0]), position.longitude, position.latitude))
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
id: g.id,
|
|
91
|
-
distance: Number.MAX_SAFE_INTEGER
|
|
92
|
-
}
|
|
93
|
-
}).filter(a => a.distance < 100).sort((a, b) => (a.distance > b.distance) ? 1 : -1)
|
|
81
|
+
const a = geofences.map(g => {
|
|
82
|
+
if (g.area && g.area.startsWith('CIRCLE (')) {
|
|
83
|
+
console.log('->' + g.area)
|
|
84
|
+
const str = g.area.substring('CIRCLE ('.length, g.area.indexOf(','))
|
|
94
85
|
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
const coord = str.trim().split(' ')
|
|
87
|
+
return {
|
|
88
|
+
id: g.id,
|
|
89
|
+
distance: Math.round(coordsDistance(parseFloat(coord[1]), parseFloat(coord[0]), position.longitude, position.latitude))
|
|
90
|
+
}
|
|
97
91
|
}
|
|
92
|
+
return {
|
|
93
|
+
id: g.id,
|
|
94
|
+
distance: Number.MAX_SAFE_INTEGER
|
|
95
|
+
}
|
|
96
|
+
}).filter(a => a.distance < 100).sort((a, b) => (a.distance > b.distance) ? 1 : -1)
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
function coordsDistance(lon1, lat1, lon2, lat2) {
|
|
103
|
-
const from = helpers.point([lon1, lat1])
|
|
104
|
-
const to = helpers.point([lon2, lat2])
|
|
98
|
+
if (a.length > 0) {
|
|
99
|
+
return a[0]
|
|
100
|
+
}
|
|
105
101
|
|
|
106
|
-
|
|
102
|
+
return null
|
|
107
103
|
}
|
|
108
104
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if(userData.withOdooServices && d.attributes.odooId) {
|
|
113
|
-
console.log(data.fuelServices)
|
|
114
|
-
data.fuelServices.forEach(s => {
|
|
115
|
-
const serviceDate = new Date(s.date)
|
|
116
|
-
serviceDate.setHours(23, 59, 59, 59)
|
|
105
|
+
function coordsDistance (lon1, lat1, lon2, lat2) {
|
|
106
|
+
const from = helpers.point([lon1, lat1])
|
|
107
|
+
const to = helpers.point([lon2, lat2])
|
|
117
108
|
|
|
118
|
-
|
|
109
|
+
return distance.default(from, to) * 1000
|
|
110
|
+
}
|
|
119
111
|
|
|
120
|
-
|
|
112
|
+
async function calculateRefuelingPositions (userData, d, data) {
|
|
113
|
+
const refuelingPositions = []
|
|
114
|
+
|
|
115
|
+
if (userData.withOdooServices && d.attributes.odooId) {
|
|
116
|
+
console.log(data.fuelServices)
|
|
117
|
+
data.fuelServices.forEach(s => {
|
|
118
|
+
const serviceDate = new Date(s.date)
|
|
119
|
+
serviceDate.setHours(23, 59, 59, 59)
|
|
120
|
+
|
|
121
|
+
const routeBeforeService = data.route.filter(r => new Date(r.fixTime).getTime() < serviceDate.getTime())
|
|
122
|
+
|
|
123
|
+
const quantity = s.notes.split(':')[1].replace(',', '.') || 0
|
|
124
|
+
|
|
125
|
+
if (routeBeforeService.length) {
|
|
126
|
+
refuelingPositions.push({ date: s.date, diff: Number.parseFloat(quantity), cost: s.amount })
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
} else {
|
|
130
|
+
let refuelingActivated = false
|
|
131
|
+
let currentValue = null
|
|
132
|
+
let positionsChecked = 0
|
|
133
|
+
let lastRefuelingIndex = 0
|
|
134
|
+
data.route.forEach(function (element, index, array) {
|
|
135
|
+
if (!element.attributes.ignition && refuelingActivated && !currentValue) {
|
|
136
|
+
// Ignition is off and Refueling is Active and there is no current value
|
|
137
|
+
|
|
138
|
+
// Get 5 position before the ignition off to calculate the current value of the fuel tank level
|
|
139
|
+
// lastRefuelingIndex is used to avoid re-analyzing positions prior to a refueling
|
|
140
|
+
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)
|
|
141
|
+
currentValue = Math.round(before.reduce((a, b) => a + b.attributes.fuel, 0) / before.length)
|
|
142
|
+
|
|
143
|
+
positionsChecked = 0
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (element.attributes.ignition && refuelingActivated && currentValue) {
|
|
147
|
+
// Ignition is on and Refueling is Active and there is a current value
|
|
148
|
+
// Calculate the value of the fuel tank level after turning the ignition back on
|
|
149
|
+
const after = (array.length > index + 5 ? array.slice(index, index + 1) : array.slice(index)).filter(b => b.attributes.ignition && b.attributes.fuel)
|
|
150
|
+
const newValue = Math.round(after.reduce((a, b) => a + b.attributes.fuel, 0) / after.length)
|
|
151
|
+
|
|
152
|
+
const diff = automaticReports.calculateFuelDiff(currentValue, newValue, d)
|
|
153
|
+
if (diff > 20) {
|
|
154
|
+
// New refueling detected
|
|
155
|
+
const value = Math.round(diff * d.attributes.fuel_tank_capacity / 100)
|
|
156
|
+
refuelingPositions.push({ position: element, date: element.fixTime, diff: value })
|
|
157
|
+
lastRefuelingIndex = index
|
|
158
|
+
positionsChecked = positionsToCheck + 1 // to reset values
|
|
159
|
+
}
|
|
121
160
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
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)
|
|
138
|
-
currentValue = Math.round(before.reduce((a, b) => a + b.attributes.fuel, 0) / before.length)
|
|
139
|
-
|
|
140
|
-
positionsChecked = 0
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (element.attributes.ignition && refuelingActivated && currentValue) {
|
|
144
|
-
//Ignition is on and Refueling is Active and there is a current value
|
|
145
|
-
//Calculate the value of the fuel tank level after turning the ignition back on
|
|
146
|
-
const after = (array.length > index + 5 ? array.slice(index, index + 1) : array.slice(index)).filter(b => b.attributes.ignition && b.attributes.fuel)
|
|
147
|
-
const newValue = Math.round(after.reduce((a, b) => a + b.attributes.fuel, 0) / after.length)
|
|
148
|
-
|
|
149
|
-
const diff = automaticReports.calculateFuelDiff(currentValue, newValue, d)
|
|
150
|
-
if (diff > 20) {
|
|
151
|
-
//New refueling detected
|
|
152
|
-
const value = Math.round(diff * d.attributes.fuel_tank_capacity / 100)
|
|
153
|
-
refuelingPositions.push({position: element, date: element.fixTime, diff: value})
|
|
154
|
-
lastRefuelingIndex = index
|
|
155
|
-
positionsChecked = positionsToCheck + 1 // to reset values
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (positionsChecked > positionsToCheck) {
|
|
159
|
-
refuelingActivated = false
|
|
160
|
-
currentValue = null
|
|
161
|
-
positionsChecked = 0
|
|
162
|
-
} else {
|
|
163
|
-
positionsChecked++
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (element.attributes.ignition && !refuelingActivated) {
|
|
168
|
-
refuelingActivated = true
|
|
169
|
-
}
|
|
170
|
-
})
|
|
171
|
-
}
|
|
172
|
-
return refuelingPositions
|
|
161
|
+
if (positionsChecked > positionsToCheck) {
|
|
162
|
+
refuelingActivated = false
|
|
163
|
+
currentValue = null
|
|
164
|
+
positionsChecked = 0
|
|
165
|
+
} else {
|
|
166
|
+
positionsChecked++
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (element.attributes.ignition && !refuelingActivated) {
|
|
171
|
+
refuelingActivated = true
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
return refuelingPositions
|
|
173
176
|
}
|
|
174
177
|
|
|
175
|
-
|
|
176
|
-
exports.createRefuelingReport = createRefuelingReport;
|
|
178
|
+
exports.createRefuelingReport = createRefuelingReport
|
|
177
179
|
exports.calculateRefuelingPositions = calculateRefuelingPositions
|