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 ++++++++++++ 7 files changed, 390 insertions(+), 66 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 (limited to 'src') 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; + } + +} -- cgit v1.2.3