aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar/reports
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2019-03-31 22:35:39 -0700
committerAnton Tananaev <anton.tananaev@gmail.com>2019-03-31 22:35:39 -0700
commit59416923dcb3a756eaf532cc4259f2f6625c0762 (patch)
tree9082dae6616deac8fda432b7bfd80e4a52b6d9dc /src/main/java/org/traccar/reports
parent79a129dd6327d932133d6b9a50190d3f4927bff9 (diff)
downloadtrackermap-server-59416923dcb3a756eaf532cc4259f2f6625c0762.tar.gz
trackermap-server-59416923dcb3a756eaf532cc4259f2f6625c0762.tar.bz2
trackermap-server-59416923dcb3a756eaf532cc4259f2f6625c0762.zip
Convert project to gradle
Diffstat (limited to 'src/main/java/org/traccar/reports')
-rw-r--r--src/main/java/org/traccar/reports/Events.java133
-rw-r--r--src/main/java/org/traccar/reports/ReportUtils.java378
-rw-r--r--src/main/java/org/traccar/reports/Route.java85
-rw-r--r--src/main/java/org/traccar/reports/Stops.java102
-rw-r--r--src/main/java/org/traccar/reports/Summary.java117
-rw-r--r--src/main/java/org/traccar/reports/Trips.java100
-rw-r--r--src/main/java/org/traccar/reports/model/BaseReport.java106
-rw-r--r--src/main/java/org/traccar/reports/model/DeviceReport.java55
-rw-r--r--src/main/java/org/traccar/reports/model/StopReport.java106
-rw-r--r--src/main/java/org/traccar/reports/model/SummaryReport.java34
-rw-r--r--src/main/java/org/traccar/reports/model/TripReport.java152
-rw-r--r--src/main/java/org/traccar/reports/model/TripsConfig.java105
12 files changed, 1473 insertions, 0 deletions
diff --git a/src/main/java/org/traccar/reports/Events.java b/src/main/java/org/traccar/reports/Events.java
new file mode 100644
index 000000000..66d9e708d
--- /dev/null
+++ b/src/main/java/org/traccar/reports/Events.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.poi.ss.util.WorkbookUtil;
+import org.traccar.Context;
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.model.Geofence;
+import org.traccar.model.Group;
+import org.traccar.model.Maintenance;
+import org.traccar.reports.model.DeviceReport;
+
+public final class Events {
+
+ private Events() {
+ }
+
+ public static Collection<Event> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Collection<String> types, Date from, Date to) throws SQLException {
+ ReportUtils.checkPeriodLimit(from, to);
+ ArrayList<Event> result = new ArrayList<>();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ Collection<Event> events = Context.getDataManager().getEvents(deviceId, from, to);
+ boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS);
+ for (Event event : events) {
+ if (all || types.contains(event.getType())) {
+ long geofenceId = event.getGeofenceId();
+ long maintenanceId = event.getMaintenanceId();
+ if ((geofenceId == 0 || Context.getGeofenceManager().checkItemPermission(userId, geofenceId))
+ && (maintenanceId == 0
+ || Context.getMaintenancesManager().checkItemPermission(userId, maintenanceId))) {
+ result.add(event);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public static void getExcel(OutputStream outputStream,
+ long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Collection<String> types, Date from, Date to) throws SQLException, IOException {
+ ReportUtils.checkPeriodLimit(from, to);
+ ArrayList<DeviceReport> devicesEvents = new ArrayList<>();
+ ArrayList<String> sheetNames = new ArrayList<>();
+ HashMap<Long, String> geofenceNames = new HashMap<>();
+ HashMap<Long, String> maintenanceNames = new HashMap<>();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ Collection<Event> events = Context.getDataManager().getEvents(deviceId, from, to);
+ boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS);
+ for (Iterator<Event> iterator = events.iterator(); iterator.hasNext();) {
+ Event event = iterator.next();
+ if (all || types.contains(event.getType())) {
+ long geofenceId = event.getGeofenceId();
+ long maintenanceId = event.getMaintenanceId();
+ if (geofenceId != 0) {
+ if (Context.getGeofenceManager().checkItemPermission(userId, geofenceId)) {
+ Geofence geofence = Context.getGeofenceManager().getById(geofenceId);
+ if (geofence != null) {
+ geofenceNames.put(geofenceId, geofence.getName());
+ }
+ } else {
+ iterator.remove();
+ }
+ } else if (maintenanceId != 0) {
+ if (Context.getMaintenancesManager().checkItemPermission(userId, maintenanceId)) {
+ Maintenance maintenance = Context.getMaintenancesManager().getById(maintenanceId);
+ if (maintenance != null) {
+ maintenanceNames.put(maintenanceId, maintenance.getName());
+ }
+ } else {
+ iterator.remove();
+ }
+ }
+ } else {
+ iterator.remove();
+ }
+ }
+ DeviceReport deviceEvents = new DeviceReport();
+ Device device = Context.getIdentityManager().getById(deviceId);
+ deviceEvents.setDeviceName(device.getName());
+ sheetNames.add(WorkbookUtil.createSafeSheetName(deviceEvents.getDeviceName()));
+ if (device.getGroupId() != 0) {
+ Group group = Context.getGroupsManager().getById(device.getGroupId());
+ if (group != null) {
+ deviceEvents.setGroupName(group.getName());
+ }
+ }
+ deviceEvents.setObjects(events);
+ devicesEvents.add(deviceEvents);
+ }
+ String templatePath = Context.getConfig().getString("report.templatesPath",
+ "templates/export/");
+ try (InputStream inputStream = new FileInputStream(templatePath + "/events.xlsx")) {
+ org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
+ jxlsContext.putVar("devices", devicesEvents);
+ jxlsContext.putVar("sheetNames", sheetNames);
+ jxlsContext.putVar("geofenceNames", geofenceNames);
+ jxlsContext.putVar("maintenanceNames", maintenanceNames);
+ jxlsContext.putVar("from", from);
+ jxlsContext.putVar("to", to);
+ ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
+ }
+ }
+}
diff --git a/src/main/java/org/traccar/reports/ReportUtils.java b/src/main/java/org/traccar/reports/ReportUtils.java
new file mode 100644
index 000000000..3a631e0d9
--- /dev/null
+++ b/src/main/java/org/traccar/reports/ReportUtils.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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 org.apache.velocity.tools.generic.DateTool;
+import org.apache.velocity.tools.generic.NumberTool;
+import org.jxls.area.Area;
+import org.jxls.builder.xls.XlsCommentAreaBuilder;
+import org.jxls.common.CellRef;
+import org.jxls.formula.StandardFormulaProcessor;
+import org.jxls.transform.Transformer;
+import org.jxls.transform.poi.PoiTransformer;
+import org.jxls.util.TransformerFactory;
+import org.traccar.Context;
+import org.traccar.database.DeviceManager;
+import org.traccar.database.IdentityManager;
+import org.traccar.handler.events.MotionEventHandler;
+import org.traccar.model.DeviceState;
+import org.traccar.model.Driver;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.reports.model.BaseReport;
+import org.traccar.reports.model.StopReport;
+import org.traccar.reports.model.TripReport;
+import org.traccar.reports.model.TripsConfig;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+public final class ReportUtils {
+
+ private ReportUtils() {
+ }
+
+ public static void checkPeriodLimit(Date from, Date to) {
+ long limit = Context.getConfig().getLong("report.periodLimit") * 1000;
+ if (limit > 0 && to.getTime() - from.getTime() > limit) {
+ throw new IllegalArgumentException("Time period exceeds the limit");
+ }
+ }
+
+ public static String getDistanceUnit(long userId) {
+ return (String) Context.getPermissionsManager().lookupAttribute(userId, "distanceUnit", "km");
+ }
+
+ public static String getSpeedUnit(long userId) {
+ return (String) Context.getPermissionsManager().lookupAttribute(userId, "speedUnit", "kn");
+ }
+
+ public static String getVolumeUnit(long userId) {
+ return (String) Context.getPermissionsManager().lookupAttribute(userId, "volumeUnit", "ltr");
+ }
+
+ public static TimeZone getTimezone(long userId) {
+ String timezone = (String) Context.getPermissionsManager().lookupAttribute(userId, "timezone", null);
+ return timezone != null ? TimeZone.getTimeZone(timezone) : TimeZone.getDefault();
+ }
+
+ public static Collection<Long> getDeviceList(Collection<Long> deviceIds, Collection<Long> groupIds) {
+ Collection<Long> result = new ArrayList<>();
+ result.addAll(deviceIds);
+ for (long groupId : groupIds) {
+ result.addAll(Context.getPermissionsManager().getGroupDevices(groupId));
+ }
+ 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;
+ double firstOdometer = firstPosition.getDouble(Position.KEY_ODOMETER);
+ double lastOdometer = lastPosition.getDouble(Position.KEY_ODOMETER);
+
+ if (useOdometer && (firstOdometer != 0.0 || lastOdometer != 0.0)) {
+ distance = lastOdometer - firstOdometer;
+ } else if (firstPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)
+ && lastPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)) {
+ distance = lastPosition.getDouble(Position.KEY_TOTAL_DISTANCE)
+ - firstPosition.getDouble(Position.KEY_TOTAL_DISTANCE);
+ }
+ return distance;
+ }
+
+ public static double calculateFuel(Position firstPosition, Position lastPosition) {
+
+ if (firstPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null
+ && lastPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null) {
+
+ BigDecimal value = new BigDecimal(firstPosition.getDouble(Position.KEY_FUEL_LEVEL)
+ - lastPosition.getDouble(Position.KEY_FUEL_LEVEL));
+ return value.setScale(1, RoundingMode.HALF_EVEN).doubleValue();
+ }
+ return 0;
+ }
+
+ public static String findDriver(Position firstPosition, Position lastPosition) {
+ if (firstPosition.getAttributes().containsKey(Position.KEY_DRIVER_UNIQUE_ID)) {
+ return firstPosition.getString(Position.KEY_DRIVER_UNIQUE_ID);
+ } else if (lastPosition.getAttributes().containsKey(Position.KEY_DRIVER_UNIQUE_ID)) {
+ return lastPosition.getString(Position.KEY_DRIVER_UNIQUE_ID);
+ }
+ return null;
+ }
+
+ public static String findDriverName(String driverUniqueId) {
+ if (driverUniqueId != null && Context.getDriversManager() != null) {
+ Driver driver = Context.getDriversManager().getDriverByUniqueId(driverUniqueId);
+ if (driver != null) {
+ return driver.getName();
+ }
+ }
+ return null;
+ }
+
+ public static org.jxls.common.Context initializeContext(long userId) {
+ org.jxls.common.Context jxlsContext = PoiTransformer.createInitialContext();
+ jxlsContext.putVar("distanceUnit", getDistanceUnit(userId));
+ jxlsContext.putVar("speedUnit", getSpeedUnit(userId));
+ jxlsContext.putVar("volumeUnit", getVolumeUnit(userId));
+ jxlsContext.putVar("webUrl", Context.getVelocityEngine().getProperty("web.url"));
+ jxlsContext.putVar("dateTool", new DateTool());
+ jxlsContext.putVar("numberTool", new NumberTool());
+ jxlsContext.putVar("timezone", getTimezone(userId));
+ jxlsContext.putVar("locale", Locale.getDefault());
+ jxlsContext.putVar("bracketsRegex", "[\\{\\}\"]");
+ return jxlsContext;
+ }
+
+ public static void processTemplateWithSheets(
+ InputStream templateStream, OutputStream targetStream,
+ org.jxls.common.Context jxlsContext) throws IOException {
+
+ Transformer transformer = TransformerFactory.createTransformer(templateStream, targetStream);
+ List<Area> xlsAreas = new XlsCommentAreaBuilder(transformer).build();
+ for (Area xlsArea : xlsAreas) {
+ xlsArea.applyAt(new CellRef(xlsArea.getStartCellRef().getCellName()), jxlsContext);
+ xlsArea.setFormulaProcessor(new StandardFormulaProcessor());
+ xlsArea.processFormulas();
+ }
+ transformer.deleteSheet(xlsAreas.get(0).getStartCellRef().getSheetName());
+ transformer.write();
+ }
+
+ private static TripReport calculateTrip(
+ ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
+
+ 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() - startTrip.getFixTime().getTime();
+ long deviceId = startTrip.getDeviceId();
+ trip.setDeviceId(deviceId);
+ trip.setDeviceName(Context.getIdentityManager().getById(deviceId).getName());
+
+ trip.setStartPositionId(startTrip.getId());
+ trip.setStartLat(startTrip.getLatitude());
+ trip.setStartLon(startTrip.getLongitude());
+ trip.setStartTime(startTrip.getFixTime());
+ String startAddress = startTrip.getAddress();
+ if (startAddress == null && Context.getGeocoder() != null
+ && Context.getConfig().getBoolean("geocoder.onRequest")) {
+ startAddress = Context.getGeocoder().getAddress(startTrip.getLatitude(), startTrip.getLongitude(), null);
+ }
+ trip.setStartAddress(startAddress);
+
+ trip.setEndPositionId(endTrip.getId());
+ trip.setEndLat(endTrip.getLatitude());
+ trip.setEndLon(endTrip.getLongitude());
+ trip.setEndTime(endTrip.getFixTime());
+ String endAddress = endTrip.getAddress();
+ if (endAddress == null && Context.getGeocoder() != null
+ && Context.getConfig().getBoolean("geocoder.onRequest")) {
+ endAddress = Context.getGeocoder().getAddress(endTrip.getLatitude(), endTrip.getLongitude(), null);
+ }
+ trip.setEndAddress(endAddress);
+
+ trip.setDistance(calculateDistance(startTrip, endTrip, !ignoreOdometer));
+ trip.setDuration(tripDuration);
+ trip.setAverageSpeed(speedSum / (endIndex - startIndex));
+ trip.setMaxSpeed(speedMax);
+ trip.setSpentFuel(calculateFuel(startTrip, endTrip));
+
+ trip.setDriverUniqueId(findDriver(startTrip, endTrip));
+ trip.setDriverName(findDriverName(trip.getDriverUniqueId()));
+
+ if (!ignoreOdometer
+ && startTrip.getDouble(Position.KEY_ODOMETER) != 0
+ && endTrip.getDouble(Position.KEY_ODOMETER) != 0) {
+ trip.setStartOdometer(startTrip.getDouble(Position.KEY_ODOMETER));
+ trip.setEndOdometer(endTrip.getDouble(Position.KEY_ODOMETER));
+ } else {
+ trip.setStartOdometer(startTrip.getDouble(Position.KEY_TOTAL_DISTANCE));
+ trip.setEndOdometer(endTrip.getDouble(Position.KEY_TOTAL_DISTANCE));
+ }
+
+ return trip;
+ }
+
+ private static StopReport calculateStop(
+ ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
+
+ Position startStop = positions.get(startIndex);
+ Position endStop = positions.get(endIndex);
+
+ StopReport stop = new StopReport();
+
+ long deviceId = startStop.getDeviceId();
+ stop.setDeviceId(deviceId);
+ stop.setDeviceName(Context.getIdentityManager().getById(deviceId).getName());
+
+ stop.setPositionId(startStop.getId());
+ stop.setLatitude(startStop.getLatitude());
+ stop.setLongitude(startStop.getLongitude());
+ stop.setStartTime(startStop.getFixTime());
+ String address = startStop.getAddress();
+ if (address == null && Context.getGeocoder() != null
+ && Context.getConfig().getBoolean("geocoder.onRequest")) {
+ address = Context.getGeocoder().getAddress(stop.getLatitude(), stop.getLongitude(), null);
+ }
+ stop.setAddress(address);
+
+ stop.setEndTime(endStop.getFixTime());
+
+ long stopDuration = endStop.getFixTime().getTime() - startStop.getFixTime().getTime();
+ stop.setDuration(stopDuration);
+ stop.setSpentFuel(calculateFuel(startStop, endStop));
+
+ long engineHours = 0;
+ if (startStop.getAttributes().containsKey(Position.KEY_HOURS)
+ && endStop.getAttributes().containsKey(Position.KEY_HOURS)) {
+ engineHours = endStop.getLong(Position.KEY_HOURS) - startStop.getLong(Position.KEY_HOURS);
+ } else if (Context.getConfig().getBoolean("processing.engineHours.enable")) {
+ // Temporary fallback for old data, to be removed in May 2019
+ for (int i = startIndex + 1; i <= endIndex; i++) {
+ if (positions.get(i).getBoolean(Position.KEY_IGNITION)
+ && positions.get(i - 1).getBoolean(Position.KEY_IGNITION)) {
+ engineHours += positions.get(i).getFixTime().getTime()
+ - positions.get(i - 1).getFixTime().getTime();
+ }
+ }
+ }
+ stop.setEngineHours(engineHours);
+
+ if (!ignoreOdometer
+ && startStop.getDouble(Position.KEY_ODOMETER) != 0
+ && endStop.getDouble(Position.KEY_ODOMETER) != 0) {
+ stop.setStartOdometer(startStop.getDouble(Position.KEY_ODOMETER));
+ stop.setEndOdometer(endStop.getDouble(Position.KEY_ODOMETER));
+ } else {
+ stop.setStartOdometer(startStop.getDouble(Position.KEY_TOTAL_DISTANCE));
+ stop.setEndOdometer(endStop.getDouble(Position.KEY_TOTAL_DISTANCE));
+ }
+
+ return stop;
+
+ }
+
+ private static <T extends BaseReport> T calculateTripOrStop(
+ ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer, Class<T> reportClass) {
+
+ if (reportClass.equals(TripReport.class)) {
+ return (T) calculateTrip(positions, startIndex, endIndex, ignoreOdometer);
+ } else {
+ return (T) calculateStop(positions, startIndex, endIndex, ignoreOdometer);
+ }
+ }
+
+ private static boolean isMoving(ArrayList<Position> positions, int index, TripsConfig tripsConfig) {
+ if (tripsConfig.getMinimalNoDataDuration() > 0) {
+ boolean beforeGap = index < positions.size() - 1
+ && positions.get(index + 1).getFixTime().getTime() - positions.get(index).getFixTime().getTime()
+ >= tripsConfig.getMinimalNoDataDuration();
+ boolean afterGap = index > 0
+ && positions.get(index).getFixTime().getTime() - positions.get(index - 1).getFixTime().getTime()
+ >= tripsConfig.getMinimalNoDataDuration();
+ if (beforeGap || afterGap) {
+ return false;
+ }
+ }
+ if (positions.get(index).getAttributes().containsKey(Position.KEY_MOTION)
+ && positions.get(index).getAttributes().get(Position.KEY_MOTION) instanceof Boolean) {
+ return positions.get(index).getBoolean(Position.KEY_MOTION);
+ } else {
+ return positions.get(index).getSpeed() > tripsConfig.getSpeedThreshold();
+ }
+ }
+
+ public static <T extends BaseReport> Collection<T> detectTripsAndStops(
+ IdentityManager identityManager, DeviceManager deviceManager,
+ Collection<Position> positionCollection,
+ TripsConfig tripsConfig, boolean ignoreOdometer, Class<T> reportClass) {
+
+ Collection<T> result = new ArrayList<>();
+
+ ArrayList<Position> positions = new ArrayList<>(positionCollection);
+ if (!positions.isEmpty()) {
+ boolean trips = reportClass.equals(TripReport.class);
+ MotionEventHandler motionHandler = new MotionEventHandler(identityManager, deviceManager, tripsConfig);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setMotionState(isMoving(positions, 0, tripsConfig));
+ int startEventIndex = trips == deviceState.getMotionState() ? 0 : -1;
+ int startNoEventIndex = -1;
+ for (int i = 0; i < positions.size(); i++) {
+ Map<Event, Position> event = motionHandler.updateMotionState(deviceState, positions.get(i),
+ isMoving(positions, i, tripsConfig));
+ if (startEventIndex == -1
+ && (trips != deviceState.getMotionState() && deviceState.getMotionPosition() != null
+ || trips == deviceState.getMotionState() && event != null)) {
+ startEventIndex = i;
+ startNoEventIndex = -1;
+ } else if (trips != deviceState.getMotionState() && startEventIndex != -1
+ && deviceState.getMotionPosition() == null && event == null) {
+ startEventIndex = -1;
+ }
+ if (startNoEventIndex == -1
+ && (trips == deviceState.getMotionState() && deviceState.getMotionPosition() != null
+ || trips != deviceState.getMotionState() && event != null)) {
+ startNoEventIndex = i;
+ } else if (startNoEventIndex != -1 && deviceState.getMotionPosition() == null && event == null) {
+ startNoEventIndex = -1;
+ }
+ if (startEventIndex != -1 && startNoEventIndex != -1 && event != null
+ && trips != deviceState.getMotionState()) {
+ result.add(calculateTripOrStop(positions, startEventIndex, startNoEventIndex,
+ ignoreOdometer, reportClass));
+ startEventIndex = -1;
+ }
+ }
+ if (startEventIndex != -1 && (startNoEventIndex != -1 || !trips)) {
+ result.add(calculateTripOrStop(positions, startEventIndex,
+ startNoEventIndex != -1 ? startNoEventIndex : positions.size() - 1,
+ ignoreOdometer, reportClass));
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/traccar/reports/Route.java b/src/main/java/org/traccar/reports/Route.java
new file mode 100644
index 000000000..6adb00aae
--- /dev/null
+++ b/src/main/java/org/traccar/reports/Route.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import org.apache.poi.ss.util.WorkbookUtil;
+import org.traccar.Context;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.model.Position;
+import org.traccar.reports.model.DeviceReport;
+
+public final class Route {
+
+ private Route() {
+ }
+
+ public static Collection<Position> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws SQLException {
+ ReportUtils.checkPeriodLimit(from, to);
+ ArrayList<Position> result = new ArrayList<>();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ result.addAll(Context.getDataManager().getPositions(deviceId, from, to));
+ }
+ return result;
+ }
+
+ public static void getExcel(OutputStream outputStream,
+ long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws SQLException, IOException {
+ ReportUtils.checkPeriodLimit(from, to);
+ ArrayList<DeviceReport> devicesRoutes = new ArrayList<>();
+ ArrayList<String> sheetNames = new ArrayList<>();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ Collection<Position> positions = Context.getDataManager()
+ .getPositions(deviceId, from, to);
+ DeviceReport deviceRoutes = new DeviceReport();
+ Device device = Context.getIdentityManager().getById(deviceId);
+ deviceRoutes.setDeviceName(device.getName());
+ sheetNames.add(WorkbookUtil.createSafeSheetName(deviceRoutes.getDeviceName()));
+ if (device.getGroupId() != 0) {
+ Group group = Context.getGroupsManager().getById(device.getGroupId());
+ if (group != null) {
+ deviceRoutes.setGroupName(group.getName());
+ }
+ }
+ deviceRoutes.setObjects(positions);
+ devicesRoutes.add(deviceRoutes);
+ }
+ String templatePath = Context.getConfig().getString("report.templatesPath",
+ "templates/export/");
+ try (InputStream inputStream = new FileInputStream(templatePath + "/route.xlsx")) {
+ org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
+ jxlsContext.putVar("devices", devicesRoutes);
+ jxlsContext.putVar("sheetNames", sheetNames);
+ jxlsContext.putVar("from", from);
+ jxlsContext.putVar("to", to);
+ ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
+ }
+ }
+}
diff --git a/src/main/java/org/traccar/reports/Stops.java b/src/main/java/org/traccar/reports/Stops.java
new file mode 100644
index 000000000..98c9cef00
--- /dev/null
+++ b/src/main/java/org/traccar/reports/Stops.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import org.apache.poi.ss.util.WorkbookUtil;
+import org.traccar.Context;
+import org.traccar.Main;
+import org.traccar.database.DeviceManager;
+import org.traccar.database.IdentityManager;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.reports.model.DeviceReport;
+import org.traccar.reports.model.StopReport;
+
+public final class Stops {
+
+ private Stops() {
+ }
+
+ private static Collection<StopReport> detectStops(long deviceId, Date from, Date to) throws SQLException {
+ boolean ignoreOdometer = Context.getDeviceManager()
+ .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
+
+ IdentityManager identityManager = Main.getInjector().getInstance(IdentityManager.class);
+ DeviceManager deviceManager = Main.getInjector().getInstance(DeviceManager.class);
+
+ return ReportUtils.detectTripsAndStops(
+ identityManager, deviceManager, Context.getDataManager().getPositions(deviceId, from, to),
+ Context.getTripsConfig(), ignoreOdometer, StopReport.class);
+ }
+
+ public static Collection<StopReport> getObjects(
+ long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws SQLException {
+ ReportUtils.checkPeriodLimit(from, to);
+ ArrayList<StopReport> result = new ArrayList<>();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ result.addAll(detectStops(deviceId, from, to));
+ }
+ return result;
+ }
+
+ public static void getExcel(
+ OutputStream outputStream, long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws SQLException, IOException {
+ ReportUtils.checkPeriodLimit(from, to);
+ ArrayList<DeviceReport> devicesStops = new ArrayList<>();
+ ArrayList<String> sheetNames = new ArrayList<>();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ Collection<StopReport> stops = detectStops(deviceId, from, to);
+ DeviceReport deviceStops = new DeviceReport();
+ Device device = Context.getIdentityManager().getById(deviceId);
+ deviceStops.setDeviceName(device.getName());
+ sheetNames.add(WorkbookUtil.createSafeSheetName(deviceStops.getDeviceName()));
+ if (device.getGroupId() != 0) {
+ Group group = Context.getGroupsManager().getById(device.getGroupId());
+ if (group != null) {
+ deviceStops.setGroupName(group.getName());
+ }
+ }
+ deviceStops.setObjects(stops);
+ devicesStops.add(deviceStops);
+ }
+ String templatePath = Context.getConfig().getString("report.templatesPath",
+ "templates/export/");
+ try (InputStream inputStream = new FileInputStream(templatePath + "/stops.xlsx")) {
+ org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
+ jxlsContext.putVar("devices", devicesStops);
+ jxlsContext.putVar("sheetNames", sheetNames);
+ jxlsContext.putVar("from", from);
+ jxlsContext.putVar("to", to);
+ ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/reports/Summary.java b/src/main/java/org/traccar/reports/Summary.java
new file mode 100644
index 000000000..9810424d8
--- /dev/null
+++ b/src/main/java/org/traccar/reports/Summary.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import org.jxls.util.JxlsHelper;
+import org.traccar.Context;
+import org.traccar.model.Position;
+import org.traccar.reports.model.SummaryReport;
+
+public final class Summary {
+
+ private Summary() {
+ }
+
+ private static SummaryReport calculateSummaryResult(long deviceId, Date from, Date to) throws SQLException {
+ SummaryReport result = new SummaryReport();
+ result.setDeviceId(deviceId);
+ result.setDeviceName(Context.getIdentityManager().getById(deviceId).getName());
+ Collection<Position> positions = Context.getDataManager().getPositions(deviceId, from, to);
+ if (positions != null && !positions.isEmpty()) {
+ Position firstPosition = null;
+ Position previousPosition = null;
+ double speedSum = 0;
+ boolean engineHoursEnabled = Context.getConfig().getBoolean("processing.engineHours.enable");
+ for (Position position : positions) {
+ if (firstPosition == null) {
+ firstPosition = position;
+ }
+ if (engineHoursEnabled && previousPosition != null
+ && position.getBoolean(Position.KEY_IGNITION)
+ && previousPosition.getBoolean(Position.KEY_IGNITION)) {
+ // Temporary fallback for old data, to be removed in May 2019
+ result.addEngineHours(position.getFixTime().getTime()
+ - previousPosition.getFixTime().getTime());
+ }
+ previousPosition = position;
+ speedSum += position.getSpeed();
+ result.setMaxSpeed(position.getSpeed());
+ }
+ boolean ignoreOdometer = Context.getDeviceManager()
+ .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
+ result.setDistance(ReportUtils.calculateDistance(firstPosition, previousPosition, !ignoreOdometer));
+ result.setAverageSpeed(speedSum / positions.size());
+ result.setSpentFuel(ReportUtils.calculateFuel(firstPosition, previousPosition));
+
+ if (engineHoursEnabled
+ && firstPosition.getAttributes().containsKey(Position.KEY_HOURS)
+ && previousPosition.getAttributes().containsKey(Position.KEY_HOURS)) {
+ result.setEngineHours(
+ previousPosition.getLong(Position.KEY_HOURS) - firstPosition.getLong(Position.KEY_HOURS));
+ }
+
+ if (!ignoreOdometer
+ && firstPosition.getDouble(Position.KEY_ODOMETER) != 0
+ && previousPosition.getDouble(Position.KEY_ODOMETER) != 0) {
+ result.setStartOdometer(firstPosition.getDouble(Position.KEY_ODOMETER));
+ result.setEndOdometer(previousPosition.getDouble(Position.KEY_ODOMETER));
+ } else {
+ result.setStartOdometer(firstPosition.getDouble(Position.KEY_TOTAL_DISTANCE));
+ result.setEndOdometer(previousPosition.getDouble(Position.KEY_TOTAL_DISTANCE));
+ }
+
+ }
+ return result;
+ }
+
+ public static Collection<SummaryReport> getObjects(long userId, Collection<Long> deviceIds,
+ Collection<Long> groupIds, Date from, Date to) throws SQLException {
+ ReportUtils.checkPeriodLimit(from, to);
+ ArrayList<SummaryReport> result = new ArrayList<>();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ result.add(calculateSummaryResult(deviceId, from, to));
+ }
+ return result;
+ }
+
+ public static void getExcel(OutputStream outputStream,
+ long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws SQLException, IOException {
+ ReportUtils.checkPeriodLimit(from, to);
+ Collection<SummaryReport> summaries = getObjects(userId, deviceIds, groupIds, from, to);
+ String templatePath = Context.getConfig().getString("report.templatesPath",
+ "templates/export/");
+ try (InputStream inputStream = new FileInputStream(templatePath + "/summary.xlsx")) {
+ org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
+ jxlsContext.putVar("summaries", summaries);
+ jxlsContext.putVar("from", from);
+ jxlsContext.putVar("to", to);
+ JxlsHelper.getInstance().setUseFastFormulaProcessor(false)
+ .processTemplate(inputStream, outputStream, jxlsContext);
+ }
+ }
+}
diff --git a/src/main/java/org/traccar/reports/Trips.java b/src/main/java/org/traccar/reports/Trips.java
new file mode 100644
index 000000000..3cda65553
--- /dev/null
+++ b/src/main/java/org/traccar/reports/Trips.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import org.apache.poi.ss.util.WorkbookUtil;
+import org.traccar.Context;
+import org.traccar.Main;
+import org.traccar.database.DeviceManager;
+import org.traccar.database.IdentityManager;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.reports.model.DeviceReport;
+import org.traccar.reports.model.TripReport;
+
+public final class Trips {
+
+ private Trips() {
+ }
+
+ private static Collection<TripReport> detectTrips(long deviceId, Date from, Date to) throws SQLException {
+ boolean ignoreOdometer = Context.getDeviceManager()
+ .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
+
+ IdentityManager identityManager = Main.getInjector().getInstance(IdentityManager.class);
+ DeviceManager deviceManager = Main.getInjector().getInstance(DeviceManager.class);
+
+ return ReportUtils.detectTripsAndStops(
+ identityManager, deviceManager, Context.getDataManager().getPositions(deviceId, from, to),
+ Context.getTripsConfig(), ignoreOdometer, TripReport.class);
+ }
+
+ public static Collection<TripReport> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws SQLException {
+ ReportUtils.checkPeriodLimit(from, to);
+ ArrayList<TripReport> result = new ArrayList<>();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ result.addAll(detectTrips(deviceId, from, to));
+ }
+ return result;
+ }
+
+ public static void getExcel(OutputStream outputStream,
+ long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ Date from, Date to) throws SQLException, IOException {
+ ReportUtils.checkPeriodLimit(from, to);
+ ArrayList<DeviceReport> devicesTrips = new ArrayList<>();
+ ArrayList<String> sheetNames = new ArrayList<>();
+ for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
+ Context.getPermissionsManager().checkDevice(userId, deviceId);
+ Collection<TripReport> trips = detectTrips(deviceId, from, to);
+ DeviceReport deviceTrips = new DeviceReport();
+ Device device = Context.getIdentityManager().getById(deviceId);
+ deviceTrips.setDeviceName(device.getName());
+ sheetNames.add(WorkbookUtil.createSafeSheetName(deviceTrips.getDeviceName()));
+ if (device.getGroupId() != 0) {
+ Group group = Context.getGroupsManager().getById(device.getGroupId());
+ if (group != null) {
+ deviceTrips.setGroupName(group.getName());
+ }
+ }
+ deviceTrips.setObjects(trips);
+ devicesTrips.add(deviceTrips);
+ }
+ String templatePath = Context.getConfig().getString("report.templatesPath",
+ "templates/export/");
+ try (InputStream inputStream = new FileInputStream(templatePath + "/trips.xlsx")) {
+ org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
+ jxlsContext.putVar("devices", devicesTrips);
+ jxlsContext.putVar("sheetNames", sheetNames);
+ jxlsContext.putVar("from", from);
+ jxlsContext.putVar("to", to);
+ ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/reports/model/BaseReport.java b/src/main/java/org/traccar/reports/model/BaseReport.java
new file mode 100644
index 000000000..9f2d1188c
--- /dev/null
+++ b/src/main/java/org/traccar/reports/model/BaseReport.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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;
+
+ 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 double spentFuel;
+
+ public double getSpentFuel() {
+ return spentFuel;
+ }
+
+ public void setSpentFuel(double spentFuel) {
+ this.spentFuel = spentFuel;
+ }
+
+ private double startOdometer;
+
+ public double getStartOdometer() {
+ return startOdometer;
+ }
+
+ public void setStartOdometer(double startOdometer) {
+ this.startOdometer = startOdometer;
+ }
+ private double endOdometer;
+
+ public double getEndOdometer() {
+ return endOdometer;
+ }
+
+ public void setEndOdometer(double endOdometer) {
+ this.endOdometer = endOdometer;
+ }
+
+}
diff --git a/src/main/java/org/traccar/reports/model/DeviceReport.java b/src/main/java/org/traccar/reports/model/DeviceReport.java
new file mode 100644
index 000000000..932753d15
--- /dev/null
+++ b/src/main/java/org/traccar/reports/model/DeviceReport.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class DeviceReport {
+
+ private String deviceName;
+
+ public String getDeviceName() {
+ return deviceName;
+ }
+
+ public void setDeviceName(String deviceName) {
+ this.deviceName = deviceName;
+ }
+
+ private String groupName = "";
+
+ public String getGroupName() {
+ return groupName;
+ }
+
+ public void setGroupName(String groupName) {
+ this.groupName = groupName;
+ }
+
+ private List<?> objects;
+
+ public Collection<?> getObjects() {
+ return objects;
+ }
+
+ public void setObjects(Collection<?> objects) {
+ this.objects = new ArrayList<>(objects);
+ }
+
+}
diff --git a/src/main/java/org/traccar/reports/model/StopReport.java b/src/main/java/org/traccar/reports/model/StopReport.java
new file mode 100644
index 000000000..245292b63
--- /dev/null
+++ b/src/main/java/org/traccar/reports/model/StopReport.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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.util.Date;
+
+public class StopReport extends BaseReport {
+
+ private long positionId;
+
+ public long getPositionId() {
+ return positionId;
+ }
+
+ public void setPositionId(long positionId) {
+ this.positionId = positionId;
+ }
+
+ private double latitude;
+
+ public double getLatitude() {
+ return latitude;
+ }
+
+ public void setLatitude(double latitude) {
+ this.latitude = latitude;
+ }
+
+ private double longitude;
+
+ public double getLongitude() {
+ return longitude;
+ }
+
+ public void setLongitude(double longitude) {
+ this.longitude = longitude;
+ }
+
+ private Date startTime;
+
+ public Date getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(Date startTime) {
+ this.startTime = startTime;
+ }
+
+ private Date endTime;
+
+ public Date getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(Date endTime) {
+ this.endTime = endTime;
+ }
+
+ private String address;
+
+ public String getAddress() {
+ return address;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ private long duration;
+
+ public long getDuration() {
+ return duration;
+ }
+
+ public void setDuration(long duration) {
+ this.duration = duration;
+ }
+
+ private long engineHours; // milliseconds
+
+ public long getEngineHours() {
+ return engineHours;
+ }
+
+ public void setEngineHours(long engineHours) {
+ this.engineHours = engineHours;
+ }
+
+ public void addEngineHours(long engineHours) {
+ this.engineHours += engineHours;
+ }
+}
diff --git a/src/main/java/org/traccar/reports/model/SummaryReport.java b/src/main/java/org/traccar/reports/model/SummaryReport.java
new file mode 100644
index 000000000..6f9e9459f
--- /dev/null
+++ b/src/main/java/org/traccar/reports/model/SummaryReport.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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 SummaryReport extends BaseReport {
+
+ private long engineHours; // milliseconds
+
+ public long getEngineHours() {
+ return engineHours;
+ }
+
+ public void setEngineHours(long engineHours) {
+ this.engineHours = engineHours;
+ }
+
+ public void addEngineHours(long engineHours) {
+ this.engineHours += engineHours;
+ }
+}
diff --git a/src/main/java/org/traccar/reports/model/TripReport.java b/src/main/java/org/traccar/reports/model/TripReport.java
new file mode 100644
index 000000000..3140f3019
--- /dev/null
+++ b/src/main/java/org/traccar/reports/model/TripReport.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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.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 double startLat;
+
+ public double getStartLat() {
+ return startLat;
+ }
+
+ public void setStartLat(double startLat) {
+ this.startLat = startLat;
+ }
+
+ private double startLon;
+
+ public double getStartLon() {
+ return startLon;
+ }
+
+ public void setStartLon(double startLon) {
+ this.startLon = startLon;
+ }
+
+ private double endLat;
+
+ public double getEndLat() {
+ return endLat;
+ }
+
+ public void setEndLat(double endLat) {
+ this.endLat = endLat;
+ }
+
+ private double endLon;
+
+ public double getEndLon() {
+ return endLon;
+ }
+
+ public void setEndLon(double endLon) {
+ this.endLon = endLon;
+ }
+
+ private Date startTime;
+
+ public Date getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(Date startTime) {
+ this.startTime = startTime;
+ }
+
+ private String startAddress;
+
+ public String getStartAddress() {
+ return startAddress;
+ }
+
+ public void setStartAddress(String address) {
+ this.startAddress = address;
+ }
+
+ private Date endTime;
+
+ public Date getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(Date endTime) {
+ this.endTime = endTime;
+ }
+
+ 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 driverUniqueId;
+
+ public String getDriverUniqueId() {
+ return driverUniqueId;
+ }
+
+ public void setDriverUniqueId(String driverUniqueId) {
+ this.driverUniqueId = driverUniqueId;
+ }
+
+ private String driverName;
+
+ public String getDriverName() {
+ return driverName;
+ }
+
+ public void setDriverName(String driverName) {
+ this.driverName = driverName;
+ }
+}
diff --git a/src/main/java/org/traccar/reports/model/TripsConfig.java b/src/main/java/org/traccar/reports/model/TripsConfig.java
new file mode 100644
index 000000000..0f0c615d3
--- /dev/null
+++ b/src/main/java/org/traccar/reports/model/TripsConfig.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
+ *
+ * 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 TripsConfig {
+
+ public TripsConfig() {
+ }
+
+ public TripsConfig(double minimalTripDistance, long minimalTripDuration, long minimalParkingDuration,
+ long minimalNoDataDuration, boolean useIgnition, boolean processInvalidPositions, double speedThreshold) {
+ this.minimalTripDistance = minimalTripDistance;
+ this.minimalTripDuration = minimalTripDuration;
+ this.minimalParkingDuration = minimalParkingDuration;
+ this.minimalNoDataDuration = minimalNoDataDuration;
+ this.useIgnition = useIgnition;
+ this.processInvalidPositions = processInvalidPositions;
+ this.speedThreshold = speedThreshold;
+ }
+
+ private double minimalTripDistance;
+
+ public double getMinimalTripDistance() {
+ return minimalTripDistance;
+ }
+
+ public void setMinimalTripDistance(double minimalTripDistance) {
+ this.minimalTripDistance = minimalTripDistance;
+ }
+
+ private long minimalTripDuration;
+
+ public long getMinimalTripDuration() {
+ return minimalTripDuration;
+ }
+
+ public void setMinimalTripDuration(long minimalTripDuration) {
+ this.minimalTripDuration = minimalTripDuration;
+ }
+
+ private long minimalParkingDuration;
+
+ public long getMinimalParkingDuration() {
+ return minimalParkingDuration;
+ }
+
+ public void setMinimalParkingDuration(long minimalParkingDuration) {
+ this.minimalParkingDuration = minimalParkingDuration;
+ }
+
+ private long minimalNoDataDuration;
+
+ public long getMinimalNoDataDuration() {
+ return minimalNoDataDuration;
+ }
+
+ public void setMinimalNoDataDuration(long minimalNoDataDuration) {
+ this.minimalNoDataDuration = minimalNoDataDuration;
+ }
+
+ private boolean useIgnition;
+
+ public boolean getUseIgnition() {
+ return useIgnition;
+ }
+
+ public void setUseIgnition(boolean useIgnition) {
+ this.useIgnition = useIgnition;
+ }
+
+ private boolean processInvalidPositions;
+
+ public boolean getProcessInvalidPositions() {
+ return processInvalidPositions;
+ }
+
+ public void setProcessInvalidPositions(boolean processInvalidPositions) {
+ this.processInvalidPositions = processInvalidPositions;
+ }
+
+ private double speedThreshold;
+
+ public double getSpeedThreshold() {
+ return speedThreshold;
+ }
+
+ public void setSpeedThreshold(double speedThreshold) {
+ this.speedThreshold = speedThreshold;
+ }
+
+}