diff options
-rw-r--r-- | src/org/traccar/BasePipelineFactory.java | 9 | ||||
-rw-r--r-- | src/org/traccar/CoordinatesHandler.java | 51 | ||||
-rw-r--r-- | src/org/traccar/api/BaseResource.java | 1 | ||||
-rw-r--r-- | src/org/traccar/api/resource/ReportResource.java | 85 | ||||
-rw-r--r-- | src/org/traccar/database/PermissionsManager.java | 16 | ||||
-rw-r--r-- | src/org/traccar/reports/Events.java | 45 | ||||
-rw-r--r-- | src/org/traccar/reports/ReportUtils.java | 22 | ||||
-rw-r--r-- | src/org/traccar/reports/Route.java | 41 | ||||
-rw-r--r-- | src/org/traccar/reports/Summary.java | 66 | ||||
-rw-r--r-- | src/org/traccar/reports/model/SummaryReport.java | 42 | ||||
-rw-r--r-- | src/org/traccar/web/CsvBuilder.java | 133 | ||||
-rw-r--r-- | src/org/traccar/web/WebServer.java | 3 |
12 files changed, 511 insertions, 3 deletions
diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java index 31845290f..2b5028f33 100644 --- a/src/org/traccar/BasePipelineFactory.java +++ b/src/org/traccar/BasePipelineFactory.java @@ -44,6 +44,7 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory { private int timeout; private FilterHandler filterHandler; + private CoordinatesHandler coordinatesHandler; private DistanceHandler distanceHandler; private ReverseGeocoderHandler reverseGeocoderHandler; private LocationProviderHandler locationProviderHandler; @@ -115,6 +116,10 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory { filterHandler = new FilterHandler(); } + if (Context.getConfig().getBoolean("coordinates.filter")) { + coordinatesHandler = new CoordinatesHandler(); + } + if (Context.getReverseGeocoder() != null) { reverseGeocoderHandler = new ReverseGeocoderHandler( Context.getReverseGeocoder(), Context.getConfig().getBoolean("geocoder.processInvalidPositions")); @@ -188,6 +193,10 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory { pipeline.addLast("filter", filterHandler); } + if (coordinatesHandler != null) { + pipeline.addLast("coordinatesHandler", coordinatesHandler); + } + if (Context.getDataManager() != null) { pipeline.addLast("dataHandler", new DefaultDataHandler()); } diff --git a/src/org/traccar/CoordinatesHandler.java b/src/org/traccar/CoordinatesHandler.java new file mode 100644 index 000000000..2dcb3c632 --- /dev/null +++ b/src/org/traccar/CoordinatesHandler.java @@ -0,0 +1,51 @@ +/* + * Copyright 2016 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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; + +import org.traccar.helper.DistanceCalculator; +import org.traccar.model.Position; + +public class CoordinatesHandler extends BaseDataHandler { + + private final int coordinatesError; + + public CoordinatesHandler() { + Config config = Context.getConfig(); + coordinatesError = config.getInteger("coordinates.error", 50); + } + + private Position getLastPosition(long deviceId) { + if (Context.getIdentityManager() != null) { + return Context.getIdentityManager().getLastPosition(deviceId); + } + return null; + } + + @Override + protected Position handlePosition(Position position) { + Position last = getLastPosition(position.getDeviceId()); + if (last != null) { + double distance = DistanceCalculator.distance( + position.getLatitude(), position.getLongitude(), last.getLatitude(), last.getLongitude()); + if (distance < coordinatesError) { + position.setLatitude(last.getLatitude()); + position.setLongitude(last.getLongitude()); + } + } + return position; + } + +} diff --git a/src/org/traccar/api/BaseResource.java b/src/org/traccar/api/BaseResource.java index 97f5c074d..567b9735a 100644 --- a/src/org/traccar/api/BaseResource.java +++ b/src/org/traccar/api/BaseResource.java @@ -29,5 +29,4 @@ public class BaseResource { } return 0; } - } diff --git a/src/org/traccar/api/resource/ReportResource.java b/src/org/traccar/api/resource/ReportResource.java new file mode 100644 index 000000000..8682070b1 --- /dev/null +++ b/src/org/traccar/api/resource/ReportResource.java @@ -0,0 +1,85 @@ +package org.traccar.api.resource; + +import java.sql.SQLException; +import java.util.List; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +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.Route; +import org.traccar.web.JsonConverter; + +@Path("reports") +@Consumes("application/json") +public class ReportResource extends BaseResource { + + @Path("route") + @GET + @Produces("application/json") + public Response getRouteJson( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + return Response.ok(Route.getJson(getUserId(), deviceIds, groupIds, + JsonConverter.parseDate(from), JsonConverter.parseDate(to))).build(); + } + + @Path("route") + @GET + @Produces("application/ms-excel") + public Response getRouteCsv( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + return Response.ok(Route.getCsv(getUserId(), deviceIds, groupIds, + JsonConverter.parseDate(from), JsonConverter.parseDate(to))).build(); + } + + @Path("events") + @GET + @Produces("application/json") + public Response getEventsJson( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("type") final List<String> types, + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + return Response.ok(Events.getJson(getUserId(), deviceIds, groupIds, types, + JsonConverter.parseDate(from), JsonConverter.parseDate(to))).build(); + } + + @Path("events") + @GET + @Produces("application/ms-excel") + public Response getEventsCsv( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("type") final List<String> types, + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + return Response.ok(Events.getCsv(getUserId(), deviceIds, groupIds, + types, JsonConverter.parseDate(from), JsonConverter.parseDate(to))).build(); + } + + @Path("summary") + @GET + @Produces("application/json") + public Response getSummaryJson( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + return Response.ok(Summary.getJson(getUserId(), deviceIds, groupIds, + JsonConverter.parseDate(from), JsonConverter.parseDate(to))).build(); + } + + @Path("summary") + @GET + @Produces("application/ms-excel") + public Response getSummaryCsv( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + return Response.ok(Summary.getCsv(getUserId(), deviceIds, groupIds, + JsonConverter.parseDate(from), JsonConverter.parseDate(to))).build(); + } + +} diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java index 92fcc3ebd..3c2cc252b 100644 --- a/src/org/traccar/database/PermissionsManager.java +++ b/src/org/traccar/database/PermissionsManager.java @@ -41,6 +41,7 @@ public class PermissionsManager { private final Map<Long, Set<Long>> groupPermissions = new HashMap<>(); private final Map<Long, Set<Long>> devicePermissions = new HashMap<>(); private final Map<Long, Set<Long>> deviceUsers = new HashMap<>(); + private final Map<Long, Set<Long>> groupDevices = new HashMap<>(); public Set<Long> getGroupPermissions(long userId) { if (!groupPermissions.containsKey(userId)) { @@ -63,6 +64,13 @@ public class PermissionsManager { return deviceUsers.get(deviceId); } + public Set<Long> getGroupDevices(long groupId) { + if (!groupDevices.containsKey(groupId)) { + groupDevices.put(groupId, new HashSet<Long>()); + } + return groupDevices.get(groupId); + } + public PermissionsManager(DataManager dataManager) { this.dataManager = dataManager; refresh(); @@ -91,11 +99,17 @@ public class PermissionsManager { userDevicePermissions.add(device.getId()); } } - for (DevicePermission permission : dataManager.getDevicePermissions()) { getDevicePermissions(permission.getUserId()).add(permission.getDeviceId()); } + groupDevices.clear(); + for (Group group : Context.getDeviceManager().getAllGroups()) { + for (Device device : groupTree.getDevices(group.getId())) { + getGroupDevices(group.getId()).add(device.getId()); + } + } + } catch (SQLException error) { Log.warning(error); } diff --git a/src/org/traccar/reports/Events.java b/src/org/traccar/reports/Events.java new file mode 100644 index 000000000..9e14bd3db --- /dev/null +++ b/src/org/traccar/reports/Events.java @@ -0,0 +1,45 @@ +package org.traccar.reports; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.Date; + +import javax.json.Json; +import javax.json.JsonObjectBuilder; + +import org.traccar.Context; +import org.traccar.model.Event; +import org.traccar.web.CsvBuilder; +import org.traccar.web.JsonConverter; + +public final class Events { + + private Events() { + } + + public static String getJson(long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Collection<String> types, Date from, Date to) throws SQLException { + JsonObjectBuilder json = Json.createObjectBuilder(); + for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { + Context.getPermissionsManager().checkDevice(userId, deviceId); + for (String type : types) { + json.add(String.valueOf(deviceId), JsonConverter.arrayToJson(Context.getDataManager() + .getEvents(deviceId, type, from, to))); + } + } + return json.build().toString(); + } + + public static String getCsv(long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Collection<String> types, Date from, Date to) throws SQLException { + CsvBuilder csv = new CsvBuilder(); + csv.addHeaderLine(new Event()); + for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { + Context.getPermissionsManager().checkDevice(userId, deviceId); + for (String type : types) { + csv.addArray(Context.getDataManager().getEvents(deviceId, type, from, to)); + } + } + return csv.build(); + } +} diff --git a/src/org/traccar/reports/ReportUtils.java b/src/org/traccar/reports/ReportUtils.java new file mode 100644 index 000000000..5041871f7 --- /dev/null +++ b/src/org/traccar/reports/ReportUtils.java @@ -0,0 +1,22 @@ +package org.traccar.reports; + +import java.util.ArrayList; +import java.util.Collection; + +import org.traccar.Context; + +public final class ReportUtils { + + private ReportUtils() { + } + + 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; + } + +} diff --git a/src/org/traccar/reports/Route.java b/src/org/traccar/reports/Route.java new file mode 100644 index 000000000..7abe5c194 --- /dev/null +++ b/src/org/traccar/reports/Route.java @@ -0,0 +1,41 @@ +package org.traccar.reports; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.Date; + +import javax.json.Json; +import javax.json.JsonObjectBuilder; + +import org.traccar.Context; +import org.traccar.model.Position; +import org.traccar.web.CsvBuilder; +import org.traccar.web.JsonConverter; + +public final class Route { + + private Route() { + } + + public static String getJson(long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws SQLException { + JsonObjectBuilder json = Json.createObjectBuilder(); + for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { + Context.getPermissionsManager().checkDevice(userId, deviceId); + json.add(String.valueOf(deviceId), JsonConverter.arrayToJson(Context.getDataManager() + .getPositions(deviceId, from, to))); + } + return json.build().toString(); + } + + public static String getCsv(long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws SQLException { + CsvBuilder csv = new CsvBuilder(); + csv.addHeaderLine(new Position()); + for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { + Context.getPermissionsManager().checkDevice(userId, deviceId); + csv.addArray(Context.getDataManager().getPositions(deviceId, from, to)); + } + return csv.build(); + } +} diff --git a/src/org/traccar/reports/Summary.java b/src/org/traccar/reports/Summary.java new file mode 100644 index 000000000..474562002 --- /dev/null +++ b/src/org/traccar/reports/Summary.java @@ -0,0 +1,66 @@ +package org.traccar.reports; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.sql.SQLException; +import java.util.Collection; +import java.util.Date; + +import javax.json.Json; +import javax.json.JsonObjectBuilder; + +import org.traccar.Context; +import org.traccar.helper.DistanceCalculator; +import org.traccar.model.Position; +import org.traccar.reports.model.SummaryReport; +import org.traccar.web.CsvBuilder; +import org.traccar.web.JsonConverter; + +public final class Summary { + + private Summary() { + } + + private static SummaryReport calculateGeneralResult(long deviceId, Date from, Date to) throws SQLException { + SummaryReport result = new SummaryReport(); + Collection<Position> positions = Context.getDataManager().getPositions(deviceId, from, to); + if (positions != null && !positions.isEmpty()) { + result.setDeviceName(Context.getDeviceManager().getDeviceById(deviceId).getName()); + Position previousPosition = null; + double speedSum = 0; + for (Position position : positions) { + if (previousPosition != null) { + result.addDistance(DistanceCalculator.distance(previousPosition.getLatitude(), + previousPosition.getLongitude(), position.getLatitude(), position.getLongitude())); + } + previousPosition = position; + speedSum += position.getSpeed(); + result.setMaxSpeed(position.getSpeed()); + } + result.setAverageSpeed(speedSum / positions.size()); + result.setDistance(new BigDecimal(result.getDistance()).setScale(2, RoundingMode.HALF_UP).doubleValue()); + } + return result; + } + + public static String getJson(long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws SQLException { + JsonObjectBuilder json = Json.createObjectBuilder(); + for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { + Context.getPermissionsManager().checkDevice(userId, deviceId); + json.add(String.valueOf(deviceId), JsonConverter.objectToJson(calculateGeneralResult(deviceId, from, to))); + } + return json.build().toString(); + } + + public static String getCsv(long userId, Collection<Long> deviceIds, Collection<Long> groupIds, + Date from, Date to) throws SQLException { + CsvBuilder csv = new CsvBuilder(); + csv.addHeaderLine(new SummaryReport()); + for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) { + Context.getPermissionsManager().checkDevice(userId, deviceId); + csv.addLine(calculateGeneralResult(deviceId, from, to)); + } + return csv.build(); + } +} diff --git a/src/org/traccar/reports/model/SummaryReport.java b/src/org/traccar/reports/model/SummaryReport.java new file mode 100644 index 000000000..aed3915f1 --- /dev/null +++ b/src/org/traccar/reports/model/SummaryReport.java @@ -0,0 +1,42 @@ +package org.traccar.reports.model; + +public class SummaryReport { + + 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; + } + } +} diff --git a/src/org/traccar/web/CsvBuilder.java b/src/org/traccar/web/CsvBuilder.java new file mode 100644 index 000000000..73d5fac1d --- /dev/null +++ b/src/org/traccar/web/CsvBuilder.java @@ -0,0 +1,133 @@ +package org.traccar.web; + +import java.beans.Introspector; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.Map; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.ISODateTimeFormat; +import org.traccar.helper.Log; +import org.traccar.model.MiscFormatter; + +public class CsvBuilder { + + private static final String LINE_ENDING = "\r\n"; + private static final String SEPARATOR = ";"; + private static final DateTimeFormatter DATE_FORMAT = ISODateTimeFormat.dateTime(); + + private StringBuilder builder = new StringBuilder(); + + private void addLineEnding() { + builder.append(LINE_ENDING); + } + private void addSeparator() { + builder.append(SEPARATOR); + } + + private SortedSet<Method> getSortedMethods(Object object) { + Method[] methodArray = object.getClass().getMethods(); + SortedSet<Method> methods = new TreeSet<Method>(new Comparator<Method>() { + @Override + public int compare(Method m1, Method m2) { + return m1.getName().compareTo(m2.getName()); + } + }); + methods.addAll(Arrays.asList(methodArray)); + return methods; + } + + public void addLine(Object object) { + + SortedSet<Method> methods = getSortedMethods(object); + + for (Method method : methods) { + if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) { + try { + if (method.getReturnType().equals(boolean.class)) { + builder.append((Boolean) method.invoke(object)); + addSeparator(); + } else if (method.getReturnType().equals(int.class)) { + builder.append((Integer) method.invoke(object)); + addSeparator(); + } else if (method.getReturnType().equals(long.class)) { + builder.append((Long) method.invoke(object)); + addSeparator(); + } else if (method.getReturnType().equals(double.class)) { + builder.append((Double) method.invoke(object)); + addSeparator(); + } else if (method.getReturnType().equals(String.class)) { + builder.append((String) method.invoke(object)); + addSeparator(); + } else if (method.getReturnType().equals(Date.class)) { + Date value = (Date) method.invoke(object); + builder.append(DATE_FORMAT.print(new DateTime(value))); + addSeparator(); + } else if (method.getReturnType().equals(Map.class)) { + Map value = (Map) method.invoke(object); + if (value != null) { + builder.append(MiscFormatter.toJson(value).toString()); + addSeparator(); + } + } + } catch (IllegalAccessException | InvocationTargetException error) { + Log.warning(error); + } + } + } + addLineEnding(); + } + + public void addHeaderLine(Object object) { + + SortedSet<Method> methods = getSortedMethods(object); + + for (Method method : methods) { + if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) { + String name = Introspector.decapitalize(method.getName().substring(3)); + if (!name.equals("class")) { + builder.append(name); + addSeparator(); + } + } + } + addLineEnding(); + } + + public void addArray(Collection<?> array) { + for (Object object : array) { + switch (object.getClass().getSimpleName().toLowerCase()) { + case "string": + builder.append(object.toString()); + addLineEnding(); + break; + case "long": + builder.append((long) object); + addLineEnding(); + break; + case "double": + builder.append((double) object); + addLineEnding(); + break; + case "boolean": + builder.append((boolean) object); + addLineEnding(); + break; + default: + addLine(object); + break; + } + } + } + + public String build() { + return builder.toString(); + } +} diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java index 4ef31b1df..e022a9285 100644 --- a/src/org/traccar/web/WebServer.java +++ b/src/org/traccar/web/WebServer.java @@ -44,6 +44,7 @@ import org.traccar.api.resource.GroupResource; import org.traccar.api.resource.NotificationResource; import org.traccar.api.resource.DeviceResource; import org.traccar.api.resource.PositionResource; +import org.traccar.api.resource.ReportResource; import org.traccar.api.resource.CommandTypeResource; import org.traccar.api.resource.DeviceGeofenceResource; import org.traccar.api.resource.EventResource; @@ -160,7 +161,7 @@ public class WebServer { GroupResource.class, DeviceResource.class, PositionResource.class, CommandTypeResource.class, EventResource.class, GeofenceResource.class, DeviceGeofenceResource.class, GeofencePermissionResource.class, GroupGeofenceResource.class, - NotificationResource.class); + NotificationResource.class, ReportResource.class); servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*"); handlers.addHandler(servletHandler); |