aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/org/traccar/api/resource/ReportResource.java23
-rw-r--r--src/org/traccar/reports/ReportUtils.java45
-rw-r--r--src/org/traccar/reports/Summary.java20
-rw-r--r--src/org/traccar/reports/Trips.java160
-rw-r--r--src/org/traccar/reports/model/BaseReport.java67
-rw-r--r--src/org/traccar/reports/model/SummaryReport.java50
-rw-r--r--src/org/traccar/reports/model/TripReport.java87
-rw-r--r--test/org/traccar/reports/ReportUtilsTest.java36
-rwxr-xr-xtools/test-generator.py10
-rw-r--r--web/app/Application.js4
-rw-r--r--web/app/AttributeFormatter.js9
-rw-r--r--web/app/model/ReportSummary.js1
-rw-r--r--web/app/model/ReportTrip.js55
-rw-r--r--web/app/store/ReportSummary.js1
-rw-r--r--web/app/store/ReportTrips.js29
-rw-r--r--web/app/store/ReportTypes.js3
-rw-r--r--web/app/view/ReportController.js63
-rw-r--r--web/l10n/en.json10
18 files changed, 601 insertions, 72 deletions
diff --git a/src/org/traccar/api/resource/ReportResource.java b/src/org/traccar/api/resource/ReportResource.java
index e87d6401c..0dd0452ff 100644
--- a/src/org/traccar/api/resource/ReportResource.java
+++ b/src/org/traccar/api/resource/ReportResource.java
@@ -15,6 +15,7 @@ import javax.ws.rs.core.Response;
import org.traccar.api.BaseResource;
import org.traccar.reports.Events;
import org.traccar.reports.Summary;
+import org.traccar.reports.Trips;
import org.traccar.reports.Route;
import org.traccar.web.JsonConverter;
@@ -93,4 +94,26 @@ public class ReportResource extends BaseResource {
.build();
}
+ @Path("trips")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getTripsJson(
+ @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
+ @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
+ return Response.ok(Trips.getJson(getUserId(), deviceIds, groupIds,
+ JsonConverter.parseDate(from), JsonConverter.parseDate(to))).build();
+ }
+
+ @Path("trips")
+ @GET
+ @Produces(TEXT_CSV)
+ public Response getTripsCsv(
+ @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
+ @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
+ return Response.ok(Trips.getCsv(getUserId(), deviceIds, groupIds,
+ JsonConverter.parseDate(from), JsonConverter.parseDate(to)))
+ .header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE)
+ .build();
+ }
+
}
diff --git a/src/org/traccar/reports/ReportUtils.java b/src/org/traccar/reports/ReportUtils.java
index 9f6d34860..34f04ac87 100644
--- a/src/org/traccar/reports/ReportUtils.java
+++ b/src/org/traccar/reports/ReportUtils.java
@@ -16,10 +16,14 @@
*/
package org.traccar.reports;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import org.traccar.Context;
+import org.traccar.helper.Log;
+import org.traccar.model.Position;
public final class ReportUtils {
@@ -35,4 +39,45 @@ public final class ReportUtils {
return result;
}
+ public static double calculateDistance(Position firstPosition, Position lastPosition) {
+ return calculateDistance(firstPosition, lastPosition, true);
+ }
+
+ public static double calculateDistance(Position firstPosition, Position lastPosition, boolean useOdometer) {
+ double distance = 0.0;
+ if (useOdometer && firstPosition.getAttributes().containsKey(Position.KEY_ODOMETER)
+ && lastPosition.getAttributes().containsKey(Position.KEY_ODOMETER)) {
+ distance = (((Number) lastPosition.getAttributes().get(Position.KEY_ODOMETER)).doubleValue()
+ - ((Number) firstPosition.getAttributes().get(Position.KEY_ODOMETER)).doubleValue())
+ * 1000;
+ } else if (firstPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)
+ && lastPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)) {
+ distance = ((Number) lastPosition.getAttributes().get(Position.KEY_TOTAL_DISTANCE)).doubleValue()
+ - ((Number) firstPosition.getAttributes().get(Position.KEY_TOTAL_DISTANCE)).doubleValue();
+ }
+ return distance;
+ }
+
+ public static String calculateFuel(Position firstPosition, Position lastPosition) {
+
+ if (firstPosition.getAttributes().get(Position.KEY_FUEL) != null
+ && lastPosition.getAttributes().get(Position.KEY_FUEL) != null) {
+ try {
+ switch (firstPosition.getProtocol()) {
+ case "meitrack":
+ case "galileo":
+ case "noran":
+ return new BigDecimal(firstPosition.getAttributes().get(Position.KEY_FUEL).toString())
+ .subtract(new BigDecimal(lastPosition.getAttributes().get(Position.KEY_FUEL).toString()))
+ .setScale(2, RoundingMode.HALF_EVEN).toString() + " %";
+ default:
+ break;
+ }
+ } catch (Exception error) {
+ Log.warning(error);
+ }
+ }
+ return "-";
+ }
+
}
diff --git a/src/org/traccar/reports/Summary.java b/src/org/traccar/reports/Summary.java
index 402e6d432..44fb1dd4c 100644
--- a/src/org/traccar/reports/Summary.java
+++ b/src/org/traccar/reports/Summary.java
@@ -16,8 +16,6 @@
*/
package org.traccar.reports;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Date;
@@ -62,22 +60,8 @@ public final class Summary {
speedSum += position.getSpeed();
result.setMaxSpeed(position.getSpeed());
}
- if (firstPosition.getAttributes().containsKey(Position.KEY_ODOMETER)
- && previousPosition.getAttributes().containsKey(Position.KEY_ODOMETER)) {
- result.setDistance((Integer.parseInt(previousPosition.getAttributes().get(Position.KEY_ODOMETER)
- .toString())
- - Integer.parseInt(firstPosition.getAttributes().get(Position.KEY_ODOMETER).toString()))
- * 1000);
- } else if (firstPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)
- && previousPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)) {
- result.setDistance(((Number) previousPosition.getAttributes().get(Position.KEY_TOTAL_DISTANCE))
- .doubleValue()
- - ((Number) firstPosition.getAttributes().get(Position.KEY_TOTAL_DISTANCE)).doubleValue());
- }
- result.setDistance(new BigDecimal(result.getDistance())
- .setScale(2, RoundingMode.HALF_EVEN).doubleValue());
- result.setAverageSpeed(new BigDecimal(speedSum / positions.size())
- .setScale(3, RoundingMode.HALF_EVEN).doubleValue());
+ result.setDistance(ReportUtils.calculateDistance(firstPosition, previousPosition));
+ result.setAverageSpeed(speedSum / positions.size());
}
return result;
}
diff --git a/src/org/traccar/reports/Trips.java b/src/org/traccar/reports/Trips.java
new file mode 100644
index 000000000..b661ffffa
--- /dev/null
+++ b/src/org/traccar/reports/Trips.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2016 Andrey Kunitsyn (abyss@fox5.ru)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import javax.json.Json;
+import javax.json.JsonArrayBuilder;
+
+import org.traccar.Context;
+import org.traccar.model.Position;
+import org.traccar.reports.model.TripReport;
+import org.traccar.web.CsvBuilder;
+import org.traccar.web.JsonConverter;
+
+public final class Trips {
+
+ private Trips() {
+ }
+
+ private static TripReport calculateTrip(ArrayList<Position> positions, int startIndex, int endIndex) {
+ Position startTrip = positions.get(startIndex);
+ Position endTrip = positions.get(endIndex);
+
+ double speedMax = 0.0;
+ double speedSum = 0.0;
+ for (int i = startIndex; i <= endIndex; i++) {
+ double speed = positions.get(i).getSpeed();
+ speedSum += speed;
+ if (speed > speedMax) {
+ speedMax = speed;
+ }
+ }
+
+ TripReport trip = new TripReport();
+ long tripDuration = endTrip.getFixTime().getTime() - positions.get(startIndex).getFixTime().getTime();
+ long deviceId = startTrip.getDeviceId();
+ trip.setDeviceId(deviceId);
+ String deviceName = Context.getDeviceManager().getDeviceById(deviceId).getName();
+ trip.setDeviceName(deviceName);
+ trip.setStartPositionId(startTrip.getId());
+ trip.setStartTime(startTrip.getFixTime());
+ trip.setStartAddress(startTrip.getAddress());
+ trip.setEndPositionId(endTrip.getId());
+ trip.setEndTime(endTrip.getFixTime());
+ trip.setEndAddress(endTrip.getAddress());
+ trip.setDistance(ReportUtils.calculateDistance(startTrip, endTrip));
+ trip.setDuration(tripDuration);
+ trip.setAverageSpeed(speedSum / (endIndex - startIndex));
+ trip.setMaxSpeed(speedMax);
+ trip.setSpentFuel(ReportUtils.calculateFuel(startTrip, endTrip));
+
+ return trip;
+ }
+
+ private static Collection<TripReport> detectTrips(long deviceId, Date from, Date to) throws SQLException {
+ double speedThreshold = Context.getConfig().getDouble("event.motion.speedThreshold", 0.01);
+ long minimalTripDuration = Context.getConfig().getLong("report.trip.minimalTripDuration", 300) * 1000;
+ double minimalTripDistance = Context.getConfig().getLong("report.trip.minimalTripDistance", 500);
+ long minimalParkingDuration = Context.getConfig().getLong("report.trip.minimalParkingDuration", 300) * 1000;
+ Collection<TripReport> result = new ArrayList<>();
+
+ ArrayList<Position> positions = new ArrayList<>(Context.getDataManager().getPositions(deviceId, from, to));
+ if (positions != null && !positions.isEmpty()) {
+ int previousStartParkingIndex = 0;
+ int startParkingIndex = -1;
+ int previousEndParkingIndex = 0;
+ int endParkingIndex = 0;
+
+ boolean isMoving = false;
+ boolean isLast = false;
+ boolean skipped = false;
+ boolean tripFiltered = false;
+
+ for (int i = 0; i < positions.size(); i++) {
+ isMoving = positions.get(i).getSpeed() > speedThreshold;
+ isLast = i == positions.size() - 1;
+
+ if ((isMoving || isLast) && startParkingIndex != -1) {
+ if (!skipped || previousEndParkingIndex == 0) {
+ previousEndParkingIndex = endParkingIndex;
+ }
+ endParkingIndex = i;
+ }
+ if (!isMoving && startParkingIndex == -1) {
+ long tripDuration = positions.get(i).getFixTime().getTime()
+ - positions.get(endParkingIndex).getFixTime().getTime();
+ double tripDistance = ReportUtils.calculateDistance(positions.get(endParkingIndex),
+ positions.get(i), false);
+ tripFiltered = tripDuration < minimalTripDuration && tripDistance < minimalTripDistance;
+ if (tripFiltered) {
+ startParkingIndex = previousStartParkingIndex;
+ endParkingIndex = previousEndParkingIndex;
+ tripFiltered = false;
+ } else {
+ previousStartParkingIndex = i;
+ startParkingIndex = i;
+ }
+
+ }
+ if (startParkingIndex != -1 && (endParkingIndex > startParkingIndex || isLast)) {
+ long parkingDuration = positions.get(endParkingIndex).getFixTime().getTime()
+ - positions.get(startParkingIndex).getFixTime().getTime();
+ if ((parkingDuration >= minimalParkingDuration || isLast)
+ && previousEndParkingIndex < startParkingIndex) {
+ if (!tripFiltered) {
+ result.add(calculateTrip(positions, previousEndParkingIndex, startParkingIndex));
+ }
+ previousEndParkingIndex = endParkingIndex;
+ skipped = false;
+ } else {
+ skipped = true;
+ }
+ startParkingIndex = -1;
+ }
+ }
+ }
+ return result;
+ }
+
+ public static String getJson(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws SQLException {
+ JsonArrayBuilder json = Json.createArrayBuilder();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ for (TripReport tripReport : detectTrips(deviceId, from, to)) {
+ json.add(JsonConverter.objectToJson(tripReport));
+ }
+ }
+ return json.build().toString();
+ }
+
+ public static String getCsv(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws SQLException {
+ CsvBuilder csv = new CsvBuilder();
+ csv.addHeaderLine(new TripReport());
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ csv.addArray(detectTrips(deviceId, from, to));
+ }
+ return csv.build();
+ }
+}
diff --git a/src/org/traccar/reports/model/BaseReport.java b/src/org/traccar/reports/model/BaseReport.java
new file mode 100644
index 000000000..246cdede0
--- /dev/null
+++ b/src/org/traccar/reports/model/BaseReport.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2016 Andrey Kunitsyn (abyss@fox5.ru)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.reports.model;
+
+public class BaseReport {
+
+ private long deviceId;
+ public long getDeviceId() {
+ return deviceId;
+ }
+ public void setDeviceId(long deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ private String deviceName;
+ public String getDeviceName() {
+ return deviceName;
+ }
+ public void setDeviceName(String deviceName) {
+ this.deviceName = deviceName;
+ }
+
+ private double distance; // meters
+ public double getDistance() {
+ return distance;
+ }
+ public void setDistance(double distance) {
+ this.distance = distance;
+ }
+
+ public void addDistance(double distance) {
+ this.distance += distance;
+ }
+
+ private double averageSpeed; // knots
+ public double getAverageSpeed() {
+ return averageSpeed;
+ }
+ public void setAverageSpeed(Double averageSpeed) {
+ this.averageSpeed = averageSpeed;
+ }
+
+ private double maxSpeed; // knots
+ public double getMaxSpeed() {
+ return maxSpeed;
+ }
+ public void setMaxSpeed(double maxSpeed) {
+ if (maxSpeed > this.maxSpeed) {
+ this.maxSpeed = maxSpeed;
+ }
+ }
+
+}
diff --git a/src/org/traccar/reports/model/SummaryReport.java b/src/org/traccar/reports/model/SummaryReport.java
index 271d0c7c2..1e6655904 100644
--- a/src/org/traccar/reports/model/SummaryReport.java
+++ b/src/org/traccar/reports/model/SummaryReport.java
@@ -16,55 +16,9 @@
*/
package org.traccar.reports.model;
-public class SummaryReport {
+public class SummaryReport extends BaseReport {
- private long deviceId;
- public long getDeviceId() {
- return deviceId;
- }
- public void setDeviceId(long deviceId) {
- this.deviceId = deviceId;
- }
-
- private String deviceName;
- public String getDeviceName() {
- return deviceName;
- }
- public void setDeviceName(String deviceName) {
- this.deviceName = deviceName;
- }
-
- private double distance;
- public double getDistance() {
- return distance;
- }
- public void setDistance(double distance) {
- this.distance = distance;
- }
-
- public void addDistance(double distance) {
- this.distance += distance;
- }
-
- private double averageSpeed;
- public double getAverageSpeed() {
- return averageSpeed;
- }
- public void setAverageSpeed(double averageSpeed) {
- this.averageSpeed = averageSpeed;
- }
-
- private double maxSpeed;
- public double getMaxSpeed() {
- return maxSpeed;
- }
- public void setMaxSpeed(double maxSpeed) {
- if (maxSpeed > this.maxSpeed) {
- this.maxSpeed = maxSpeed;
- }
- }
-
- private long engineHours;
+ private long engineHours; // milliseconds
public long getEngineHours() {
return engineHours;
}
diff --git a/src/org/traccar/reports/model/TripReport.java b/src/org/traccar/reports/model/TripReport.java
new file mode 100644
index 000000000..3a77b02ca
--- /dev/null
+++ b/src/org/traccar/reports/model/TripReport.java
@@ -0,0 +1,87 @@
+package org.traccar.reports.model;
+
+import java.util.Date;
+
+public class TripReport extends BaseReport {
+
+ private long startPositionId;
+ public long getStartPositionId() {
+ return startPositionId;
+ }
+ public void setStartPositionId(long startPositionId) {
+ this.startPositionId = startPositionId;
+ }
+
+ private long endPositionId;
+ public long getEndPositionId() {
+ return endPositionId;
+ }
+ public void setEndPositionId(long endPositionId) {
+ this.endPositionId = endPositionId;
+ }
+
+ private Date startTime;
+ public Date getStartTime() {
+ if (startTime != null) {
+ return new Date(startTime.getTime());
+ } else {
+ return null;
+ }
+ }
+ public void setStartTime(Date startTime) {
+ if (startTime != null) {
+ this.startTime = new Date(startTime.getTime());
+ } else {
+ this.startTime = null;
+ }
+ }
+
+ private String startAddress;
+ public String getStartAddress() {
+ return startAddress;
+ }
+ public void setStartAddress(String address) {
+ this.startAddress = address;
+ }
+
+ private Date endTime;
+ public Date getEndTime() {
+ if (endTime != null) {
+ return new Date(endTime.getTime());
+ } else {
+ return null;
+ }
+ }
+ public void setEndTime(Date endTime) {
+ if (endTime != null) {
+ this.endTime = new Date(endTime.getTime());
+ } else {
+ this.endTime = null;
+ }
+ }
+
+ private String endAddress;
+ public String getEndAddress() {
+ return endAddress;
+ }
+ public void setEndAddress(String address) {
+ this.endAddress = address;
+ }
+
+ private long duration;
+ public long getDuration() {
+ return duration;
+ }
+ public void setDuration(long duration) {
+ this.duration = duration;
+ }
+
+ private String spentFuel;
+ public String getSpentFuel() {
+ return spentFuel;
+ }
+ public void setSpentFuel(String spentFuel) {
+ this.spentFuel = spentFuel;
+ }
+
+}
diff --git a/test/org/traccar/reports/ReportUtilsTest.java b/test/org/traccar/reports/ReportUtilsTest.java
new file mode 100644
index 000000000..2e01505db
--- /dev/null
+++ b/test/org/traccar/reports/ReportUtilsTest.java
@@ -0,0 +1,36 @@
+package org.traccar.reports;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.traccar.model.Position;
+
+public class ReportUtilsTest {
+
+ @Test
+ public void testCalculateDistance() {
+ Position startPosition = new Position();
+ startPosition.set(Position.KEY_TOTAL_DISTANCE, 500.0);
+ Position endPosition = new Position();
+ endPosition.set(Position.KEY_TOTAL_DISTANCE, 700.0);
+ Assert.assertEquals(ReportUtils.calculateDistance(startPosition, endPosition), 200.0, 10);
+ startPosition.set(Position.KEY_ODOMETER, 50000);
+ endPosition.set(Position.KEY_ODOMETER, 50001);
+ Assert.assertEquals(ReportUtils.calculateDistance(startPosition, endPosition), 1000.0, 10);
+ }
+
+ @Test
+ public void testCalculateSpentFuel() {
+ Position startPosition = new Position();
+ Position endPosition = new Position();
+ Assert.assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), "-");
+ startPosition.setProtocol("meitrack");
+ startPosition.set(Position.KEY_FUEL, 0.07);
+ endPosition.set(Position.KEY_FUEL, 0.05);
+ Assert.assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), "0.02 %");
+ startPosition.setProtocol("galileo");
+ Assert.assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), "0.02 %");
+ startPosition.setProtocol("noran");
+ Assert.assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), "0.02 %");
+ }
+
+}
diff --git a/tools/test-generator.py b/tools/test-generator.py
index 0b8e4113c..a23e2576e 100755
--- a/tools/test-generator.py
+++ b/tools/test-generator.py
@@ -10,6 +10,7 @@ id = '123456789012345'
server = 'localhost:5055'
period = 1
step = 0.001
+device_speed = 40
waypoints = [
(40.722412, -74.006288),
@@ -32,12 +33,13 @@ for i in range(0, len(waypoints)):
lon = lon1 + (lon2 - lon1) * j / count
points.append((lat, lon))
-def send(conn, lat, lon, course, alarm, ignition):
+def send(conn, lat, lon, course, alarm, ignition, speed):
params = (('id', id), ('timestamp', int(time.time())), ('lat', lat), ('lon', lon), ('bearing', course))
if alarm:
params = params + (('alarm', 'sos'),)
if ignition:
params = params + (('ignition', 'true'),)
+ params = params + (('speed', speed),)
conn.request('GET', '?' + urllib.urlencode(params))
conn.getresponse().read()
@@ -59,6 +61,10 @@ while True:
(lat2, lon2) = points[(index + 1) % len(points)]
alarm = ((index % 10) == 0)
ignition = ((index % len(points)) != 0)
- send(conn, lat1, lon1, course(lat1, lon1, lat2, lon2), alarm, ignition)
+ if (index % len(points)) != 0:
+ speed = device_speed
+ else:
+ speed = 0
+ send(conn, lat1, lon1, course(lat1, lon1, lat2, lon2), alarm, ignition, speed)
time.sleep(period)
index += 1
diff --git a/web/app/Application.js b/web/app/Application.js
index 22102f74b..da28070e6 100644
--- a/web/app/Application.js
+++ b/web/app/Application.js
@@ -34,7 +34,8 @@ Ext.define('Traccar.Application', {
'Event',
'Geofence',
'Notification',
- 'ReportSummary'
+ 'ReportSummary',
+ 'ReportTrip'
],
stores: [
@@ -60,6 +61,7 @@ Ext.define('Traccar.Application', {
'GeofenceTypes',
'ReportRoute',
'ReportEvents',
+ 'ReportTrips',
'ReportSummary',
'ReportTypes'
],
diff --git a/web/app/AttributeFormatter.js b/web/app/AttributeFormatter.js
index dcf50c580..1fff07bcf 100644
--- a/web/app/AttributeFormatter.js
+++ b/web/app/AttributeFormatter.js
@@ -39,6 +39,13 @@ Ext.define('Traccar.AttributeFormatter', {
return (hours + ' ' + Strings.sharedHourAbbreviation);
},
+ durationFormatter: function (value) {
+ var hours, minutes;
+ hours = Math.floor(value / 3600000);
+ minutes = Math.round((value % 3600000) / 60000);
+ return (hours + ' ' + Strings.sharedHourAbbreviation + ' ' + minutes + ' ' + Strings.sharedMinuteAbbreviation);
+ },
+
defaultFormatter: function (value) {
if (typeof value === 'number') {
return Number(value.toFixed(Traccar.Style.numberPrecision));
@@ -65,6 +72,8 @@ Ext.define('Traccar.AttributeFormatter', {
return this.distanceFormatter;
} else if (key === 'hours') {
return this.hoursFormatter;
+ } else if (key === 'duration') {
+ return this.durationFormatter;
} else {
return this.defaultFormatter;
}
diff --git a/web/app/model/ReportSummary.js b/web/app/model/ReportSummary.js
index 39f0c498c..430d00b9b 100644
--- a/web/app/model/ReportSummary.js
+++ b/web/app/model/ReportSummary.js
@@ -1,5 +1,6 @@
/*
* Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2016 Andrey Kunitsyn (abyss@fox5.ru)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/web/app/model/ReportTrip.js b/web/app/model/ReportTrip.js
new file mode 100644
index 000000000..cbd03d77a
--- /dev/null
+++ b/web/app/model/ReportTrip.js
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2016 Andrey Kunitsyn (abyss@fox5.ru)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+Ext.define('Traccar.model.ReportTrip', {
+ extend: 'Ext.data.Model',
+ identifier: 'negative',
+
+ fields: [{
+ name: 'deviceId',
+ type: 'int'
+ }, {
+ name: 'deviceName',
+ type: 'string'
+ }, {
+ name: 'maxSpeed',
+ type: 'float'
+ }, {
+ name: 'averageSpeed',
+ type: 'float'
+ }, {
+ name: 'distance',
+ type: 'float'
+ }, {
+ name: 'duration',
+ type: 'int'
+ }, {
+ name: 'startTime',
+ type: 'date',
+ dateFormat: 'c'
+ }, {
+ name: 'startAddress',
+ type: 'string'
+ }, {
+ name: 'endTime',
+ type: 'date',
+ dateFormat: 'c'
+ }, {
+ name: 'endAddress',
+ type: 'string'
+ }]
+});
diff --git a/web/app/store/ReportSummary.js b/web/app/store/ReportSummary.js
index 35dad736b..7c9a4fca2 100644
--- a/web/app/store/ReportSummary.js
+++ b/web/app/store/ReportSummary.js
@@ -1,5 +1,6 @@
/*
* Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2016 Andrey Kunitsyn (abyss@fox5.ru)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/web/app/store/ReportTrips.js b/web/app/store/ReportTrips.js
new file mode 100644
index 000000000..e0d86aa48
--- /dev/null
+++ b/web/app/store/ReportTrips.js
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2016 Andrey Kunitsyn (abyss@fox5.ru)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+Ext.define('Traccar.store.ReportTrips', {
+ extend: 'Ext.data.Store',
+ model: 'Traccar.model.ReportTrip',
+
+ proxy: {
+ type: 'rest',
+ url: 'api/reports/trips',
+ headers: {
+ 'Accept': 'application/json'
+ }
+ }
+});
diff --git a/web/app/store/ReportTypes.js b/web/app/store/ReportTypes.js
index d544a2fda..09ef61db6 100644
--- a/web/app/store/ReportTypes.js
+++ b/web/app/store/ReportTypes.js
@@ -25,6 +25,9 @@ Ext.define('Traccar.store.ReportTypes', {
key: 'events',
name: Strings.reportEvents
}, {
+ key: 'trips',
+ name: Strings.reportTrips
+ }, {
key: 'summary',
name: Strings.reportSummary
}]
diff --git a/web/app/view/ReportController.js b/web/app/view/ReportController.js
index 61e278993..aeaa1849e 100644
--- a/web/app/view/ReportController.js
+++ b/web/app/view/ReportController.js
@@ -1,5 +1,6 @@
/*
* Copyright 2015 - 2016 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2016 Andrey Kunitsyn (abyss@fox5.ru)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -114,7 +115,7 @@ Ext.define('Traccar.view.ReportController', {
window.navigator.msSaveBlob(blob, filename);
} else {
url = window.URL || window.webkitURL;
- downloadUrl = URL.createObjectURL(blob);
+ downloadUrl = url.createObjectURL(blob);
if (filename) {
elementA = document.createElement("a");
elementA.href = downloadUrl;
@@ -131,7 +132,7 @@ Ext.define('Traccar.view.ReportController', {
},
onTypeChange: function (combobox, newValue, oldValue) {
- var routeColumns, eventsColumns, summaryColumns;
+ var routeColumns, eventsColumns, summaryColumns, tripsColumns;
if (oldValue !== null) {
this.onClearClick();
}
@@ -234,12 +235,70 @@ Ext.define('Traccar.view.ReportController', {
renderer: Traccar.AttributeFormatter.getFormatter('hours')
}];
+ tripsColumns = [{
+ text: Strings.reportDeviceName,
+ dataIndex: 'deviceId',
+ flex: 1,
+ renderer: function (value) {
+ return Ext.getStore('Devices').findRecord('id', value).get('name');
+ }
+ }, {
+ text: Strings.reportStartTime,
+ dataIndex: 'startTime',
+ flex: 1,
+ xtype: 'datecolumn',
+ renderer: Traccar.AttributeFormatter.getFormatter('startTime')
+ }, {
+ text: Strings.reportStartAddress,
+ dataIndex: 'startAddress',
+ flex: 1,
+ renderer: Traccar.AttributeFormatter.getFormatter('address')
+ }, {
+ text: Strings.reportEndTime,
+ dataIndex: 'endTime',
+ flex: 1,
+ xtype: 'datecolumn',
+ renderer: Traccar.AttributeFormatter.getFormatter('endTime')
+ }, {
+ text: Strings.reportEndAddress,
+ dataIndex: 'endAddress',
+ flex: 1,
+ renderer: Traccar.AttributeFormatter.getFormatter('address')
+ }, {
+ text: Strings.sharedDistance,
+ dataIndex: 'distance',
+ flex: 1,
+ renderer: Traccar.AttributeFormatter.getFormatter('distance')
+ }, {
+ text: Strings.reportAverageSpeed,
+ dataIndex: 'averageSpeed',
+ flex: 1,
+ renderer: Traccar.AttributeFormatter.getFormatter('speed')
+ }, {
+ text: Strings.reportMaximumSpeed,
+ dataIndex: 'maxSpeed',
+ flex: 1,
+ renderer: Traccar.AttributeFormatter.getFormatter('speed')
+ }, {
+ text: Strings.reportDuration,
+ dataIndex: 'duration',
+ flex: 1,
+ renderer: Traccar.AttributeFormatter.getFormatter('duration')
+ }, {
+ text: Strings.reportSpentFuel,
+ dataIndex: 'spentFuel',
+ flex: 1,
+ renderer: Traccar.AttributeFormatter.getFormatter('spentFuel')
+ }];
+
if (newValue === 'route') {
this.getView().reconfigure('ReportRoute', routeColumns);
} else if (newValue === 'events') {
this.getView().reconfigure('ReportEvents', eventsColumns);
} else if (newValue === 'summary') {
this.getView().reconfigure('ReportSummary', summaryColumns);
+ } else if (newValue === 'trips') {
+ this.getView().reconfigure('ReportTrips', tripsColumns);
}
}
diff --git a/web/l10n/en.json b/web/l10n/en.json
index 5daa7e2ab..e51c5445c 100644
--- a/web/l10n/en.json
+++ b/web/l10n/en.json
@@ -27,6 +27,7 @@
"sharedType": "Type",
"sharedDistance": "Distance",
"sharedHourAbbreviation": "h",
+ "sharedMinuteAbbreviation": "m",
"errorTitle": "Error",
"errorUnknown": "Unknown error",
"errorConnection": "Connection error",
@@ -143,10 +144,17 @@
"notificationMail": "Send via Mail",
"reportRoute": "Route",
"reportEvents": "Events",
+ "reportTrips": "Trips",
"reportSummary": "Summary",
"reportCsv": "CSV",
"reportDeviceName": "Device Name",
"reportAverageSpeed": "Average Speed",
"reportMaximumSpeed": "Maximum Speed",
- "reportEngineHours": "Engine Hours"
+ "reportEngineHours": "Engine Hours",
+ "reportDuration": "Duration",
+ "reportStartTime": "Start Time",
+ "reportStartAddress": "Start Address",
+ "reportEndTime": "End Time",
+ "reportEndAddress": "End Address",
+ "reportSpentFuel": "Spent Fuel"
} \ No newline at end of file