From 1c7437d6de01fffbe3f69853717808b1790413fd Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Fri, 19 Aug 2016 10:45:33 +0500 Subject: - Added trips report - Refactored reports models - Added tests for ReportUtils - Added speed to test-generator.py - Other changes --- src/org/traccar/api/resource/ReportResource.java | 23 ++++ src/org/traccar/reports/ReportUtils.java | 46 +++++++ src/org/traccar/reports/Summary.java | 20 +-- src/org/traccar/reports/Trips.java | 160 +++++++++++++++++++++++ src/org/traccar/reports/model/BaseReport.java | 70 ++++++++++ src/org/traccar/reports/model/SummaryReport.java | 50 +------ src/org/traccar/reports/model/TripReport.java | 87 ++++++++++++ test/org/traccar/reports/ReportUtilsTest.java | 34 +++++ tools/test-generator.py | 10 +- web/app/Application.js | 4 +- web/app/AttributeFormatter.js | 9 ++ web/app/model/ReportSummary.js | 1 + web/app/model/ReportTrip.js | 55 ++++++++ web/app/store/ReportSummary.js | 1 + web/app/store/ReportTrips.js | 29 ++++ web/app/store/ReportTypes.js | 3 + web/app/view/ReportController.js | 63 ++++++++- web/l10n/en.json | 10 +- 18 files changed, 603 insertions(+), 72 deletions(-) create mode 100644 src/org/traccar/reports/Trips.java create mode 100644 src/org/traccar/reports/model/BaseReport.java create mode 100644 src/org/traccar/reports/model/TripReport.java create mode 100644 test/org/traccar/reports/ReportUtilsTest.java create mode 100644 web/app/model/ReportTrip.js create mode 100644 web/app/store/ReportTrips.js 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 deviceIds, @QueryParam("groupId") final List 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 deviceIds, @QueryParam("groupId") final List 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..03e23498a 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,46 @@ 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) { + BigDecimal distance = new BigDecimal("0.0"); + if (useOdometer && firstPosition.getAttributes().containsKey(Position.KEY_ODOMETER) + && lastPosition.getAttributes().containsKey(Position.KEY_ODOMETER)) { + distance = new BigDecimal(lastPosition.getAttributes().get(Position.KEY_ODOMETER).toString()) + .subtract(new BigDecimal(firstPosition.getAttributes().get(Position.KEY_ODOMETER).toString())) + .multiply(new BigDecimal("1000")); + } else if (firstPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE) + && lastPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)) { + distance = new BigDecimal(lastPosition.getAttributes().get(Position.KEY_TOTAL_DISTANCE).toString()) + .subtract(new BigDecimal(firstPosition.getAttributes().get(Position.KEY_TOTAL_DISTANCE) + .toString())); + } + return distance.setScale(2, RoundingMode.HALF_EVEN).doubleValue(); + } + + public static String calculateSpentFuel(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..068c47b8f --- /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 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.calculateSpentFuel(startTrip, endTrip)); + + return trip; + } + + private static Collection 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 result = new ArrayList<>(); + + ArrayList 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 deviceIds, Collection 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 deviceIds, Collection 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..bcbed29fe --- /dev/null +++ b/src/org/traccar/reports/model/BaseReport.java @@ -0,0 +1,70 @@ +/* + * 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; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +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 = new BigDecimal(averageSpeed.toString()).setScale(3, RoundingMode.HALF_EVEN).doubleValue(); + } + + 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..e7f762b28 --- /dev/null +++ b/test/org/traccar/reports/ReportUtilsTest.java @@ -0,0 +1,34 @@ +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); + Position endPosition = new Position(); + endPosition.set(Position.KEY_TOTAL_DISTANCE, 700); + 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.calculateSpentFuel(startPosition, endPosition), "-"); + startPosition.setProtocol("meitrack"); + startPosition.set(Position.KEY_FUEL, 0.07); + endPosition.set(Position.KEY_FUEL, 0.05); + Assert.assertEquals(ReportUtils.calculateSpentFuel(startPosition, endPosition), "0.02 %"); + startPosition.setProtocol("galileo"); + Assert.assertEquals(ReportUtils.calculateSpentFuel(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 @@ -24,6 +24,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 af4f2363e..a16322bca 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", @@ -138,10 +139,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 -- cgit v1.2.3 From 1ce8c1945e36acd56b7ceb87b900a0efa14e9ecd Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Sat, 20 Aug 2016 09:26:57 +0500 Subject: - Renamed calculateFuel - Used int in BigDecimal constructor - Added extra space --- src/org/traccar/reports/ReportUtils.java | 4 ++-- src/org/traccar/reports/Trips.java | 2 +- src/org/traccar/reports/model/BaseReport.java | 2 +- test/org/traccar/reports/ReportUtilsTest.java | 8 +++++--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/org/traccar/reports/ReportUtils.java b/src/org/traccar/reports/ReportUtils.java index 03e23498a..baa799e90 100644 --- a/src/org/traccar/reports/ReportUtils.java +++ b/src/org/traccar/reports/ReportUtils.java @@ -49,7 +49,7 @@ public final class ReportUtils { && lastPosition.getAttributes().containsKey(Position.KEY_ODOMETER)) { distance = new BigDecimal(lastPosition.getAttributes().get(Position.KEY_ODOMETER).toString()) .subtract(new BigDecimal(firstPosition.getAttributes().get(Position.KEY_ODOMETER).toString())) - .multiply(new BigDecimal("1000")); + .multiply(new BigDecimal(1000)); } else if (firstPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE) && lastPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)) { distance = new BigDecimal(lastPosition.getAttributes().get(Position.KEY_TOTAL_DISTANCE).toString()) @@ -59,7 +59,7 @@ public final class ReportUtils { return distance.setScale(2, RoundingMode.HALF_EVEN).doubleValue(); } - public static String calculateSpentFuel(Position firstPosition, Position lastPosition) { + public static String calculateFuel(Position firstPosition, Position lastPosition) { if (firstPosition.getAttributes().get(Position.KEY_FUEL) != null && lastPosition.getAttributes().get(Position.KEY_FUEL) != null) { diff --git a/src/org/traccar/reports/Trips.java b/src/org/traccar/reports/Trips.java index 068c47b8f..b661ffffa 100644 --- a/src/org/traccar/reports/Trips.java +++ b/src/org/traccar/reports/Trips.java @@ -65,7 +65,7 @@ public final class Trips { trip.setDuration(tripDuration); trip.setAverageSpeed(speedSum / (endIndex - startIndex)); trip.setMaxSpeed(speedMax); - trip.setSpentFuel(ReportUtils.calculateSpentFuel(startTrip, endTrip)); + trip.setSpentFuel(ReportUtils.calculateFuel(startTrip, endTrip)); return trip; } diff --git a/src/org/traccar/reports/model/BaseReport.java b/src/org/traccar/reports/model/BaseReport.java index bcbed29fe..8c5807c9b 100644 --- a/src/org/traccar/reports/model/BaseReport.java +++ b/src/org/traccar/reports/model/BaseReport.java @@ -57,7 +57,7 @@ public class BaseReport { this.averageSpeed = new BigDecimal(averageSpeed.toString()).setScale(3, RoundingMode.HALF_EVEN).doubleValue(); } - private double maxSpeed; //knots + private double maxSpeed; // knots public double getMaxSpeed() { return maxSpeed; } diff --git a/test/org/traccar/reports/ReportUtilsTest.java b/test/org/traccar/reports/ReportUtilsTest.java index e7f762b28..80b0f6287 100644 --- a/test/org/traccar/reports/ReportUtilsTest.java +++ b/test/org/traccar/reports/ReportUtilsTest.java @@ -22,13 +22,15 @@ public class ReportUtilsTest { public void testCalculateSpentFuel() { Position startPosition = new Position(); Position endPosition = new Position(); - Assert.assertEquals(ReportUtils.calculateSpentFuel(startPosition, endPosition), "-"); + 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.calculateSpentFuel(startPosition, endPosition), "0.02 %"); + Assert.assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), "0.02 %"); startPosition.setProtocol("galileo"); - Assert.assertEquals(ReportUtils.calculateSpentFuel(startPosition, endPosition), "0.02 %"); + Assert.assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), "0.02 %"); + startPosition.setProtocol("noran"); + Assert.assertEquals(ReportUtils.calculateFuel(startPosition, endPosition), "0.02 %"); } } -- cgit v1.2.3 From 804c56a76dc8406e4ff072289a4a9f97322dfe83 Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Sat, 20 Aug 2016 14:36:49 +0500 Subject: Removed rounding and BigDecimal for distance and averageSpeed --- src/org/traccar/reports/ReportUtils.java | 15 +++++++-------- src/org/traccar/reports/model/BaseReport.java | 5 +---- test/org/traccar/reports/ReportUtilsTest.java | 4 ++-- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/org/traccar/reports/ReportUtils.java b/src/org/traccar/reports/ReportUtils.java index baa799e90..34f04ac87 100644 --- a/src/org/traccar/reports/ReportUtils.java +++ b/src/org/traccar/reports/ReportUtils.java @@ -44,19 +44,18 @@ public final class ReportUtils { } public static double calculateDistance(Position firstPosition, Position lastPosition, boolean useOdometer) { - BigDecimal distance = new BigDecimal("0.0"); + double distance = 0.0; if (useOdometer && firstPosition.getAttributes().containsKey(Position.KEY_ODOMETER) && lastPosition.getAttributes().containsKey(Position.KEY_ODOMETER)) { - distance = new BigDecimal(lastPosition.getAttributes().get(Position.KEY_ODOMETER).toString()) - .subtract(new BigDecimal(firstPosition.getAttributes().get(Position.KEY_ODOMETER).toString())) - .multiply(new BigDecimal(1000)); + 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 = new BigDecimal(lastPosition.getAttributes().get(Position.KEY_TOTAL_DISTANCE).toString()) - .subtract(new BigDecimal(firstPosition.getAttributes().get(Position.KEY_TOTAL_DISTANCE) - .toString())); + distance = ((Number) lastPosition.getAttributes().get(Position.KEY_TOTAL_DISTANCE)).doubleValue() + - ((Number) firstPosition.getAttributes().get(Position.KEY_TOTAL_DISTANCE)).doubleValue(); } - return distance.setScale(2, RoundingMode.HALF_EVEN).doubleValue(); + return distance; } public static String calculateFuel(Position firstPosition, Position lastPosition) { diff --git a/src/org/traccar/reports/model/BaseReport.java b/src/org/traccar/reports/model/BaseReport.java index 8c5807c9b..246cdede0 100644 --- a/src/org/traccar/reports/model/BaseReport.java +++ b/src/org/traccar/reports/model/BaseReport.java @@ -16,9 +16,6 @@ */ package org.traccar.reports.model; -import java.math.BigDecimal; -import java.math.RoundingMode; - public class BaseReport { private long deviceId; @@ -54,7 +51,7 @@ public class BaseReport { return averageSpeed; } public void setAverageSpeed(Double averageSpeed) { - this.averageSpeed = new BigDecimal(averageSpeed.toString()).setScale(3, RoundingMode.HALF_EVEN).doubleValue(); + this.averageSpeed = averageSpeed; } private double maxSpeed; // knots diff --git a/test/org/traccar/reports/ReportUtilsTest.java b/test/org/traccar/reports/ReportUtilsTest.java index 80b0f6287..2e01505db 100644 --- a/test/org/traccar/reports/ReportUtilsTest.java +++ b/test/org/traccar/reports/ReportUtilsTest.java @@ -9,9 +9,9 @@ public class ReportUtilsTest { @Test public void testCalculateDistance() { Position startPosition = new Position(); - startPosition.set(Position.KEY_TOTAL_DISTANCE, 500); + startPosition.set(Position.KEY_TOTAL_DISTANCE, 500.0); Position endPosition = new Position(); - endPosition.set(Position.KEY_TOTAL_DISTANCE, 700); + 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); -- cgit v1.2.3