aboutsummaryrefslogtreecommitdiff
path: root/src/org
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2016-06-01 15:28:54 +1200
committerAnton Tananaev <anton.tananaev@gmail.com>2016-06-01 15:28:54 +1200
commit3db183a5535a986b015566c6df929da8c6ca9630 (patch)
tree3183f44e2b9b8ccfa2c460221b1d04fa74f0e9e9 /src/org
parentcce427a783cbe0c2eca6a278d7327294002847e5 (diff)
parent41c46575a92f746622c61b07004b7a0832712a54 (diff)
downloadtraccar-server-3db183a5535a986b015566c6df929da8c6ca9630.tar.gz
traccar-server-3db183a5535a986b015566c6df929da8c6ca9630.tar.bz2
traccar-server-3db183a5535a986b015566c6df929da8c6ca9630.zip
Merge pull request #1981 from Abyss777/master
Implement events subsystem
Diffstat (limited to 'src/org')
-rw-r--r--src/org/traccar/BaseEventHandler.java35
-rw-r--r--src/org/traccar/BasePipelineFactory.java33
-rw-r--r--src/org/traccar/api/AsyncSocket.java7
-rw-r--r--src/org/traccar/api/resource/EventResource.java39
-rw-r--r--src/org/traccar/database/ConnectionManager.java42
-rw-r--r--src/org/traccar/database/DataManager.java36
-rw-r--r--src/org/traccar/events/CommandResultEventHandler.java18
-rw-r--r--src/org/traccar/events/MotionEventHandler.java55
-rw-r--r--src/org/traccar/events/OverspeedEventHandler.java45
-rw-r--r--src/org/traccar/model/Device.java13
-rw-r--r--src/org/traccar/model/Event.java74
-rw-r--r--src/org/traccar/web/WebServer.java4
12 files changed, 393 insertions, 8 deletions
diff --git a/src/org/traccar/BaseEventHandler.java b/src/org/traccar/BaseEventHandler.java
new file mode 100644
index 000000000..fde57748c
--- /dev/null
+++ b/src/org/traccar/BaseEventHandler.java
@@ -0,0 +1,35 @@
+package org.traccar;
+
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public abstract class BaseEventHandler extends BaseDataHandler {
+
+ private boolean isLastPosition = false;
+
+ public boolean isLastPosition() {
+ return isLastPosition;
+ }
+
+ @Override
+ protected Position handlePosition(Position position) {
+
+ Device device = Context.getDataManager().getDeviceById(position.getDeviceId());
+ if (device != null) {
+ long lastPositionId = device.getPositionId();
+ if (position.getId() == lastPositionId) {
+ isLastPosition = true;
+ }
+ }
+
+ Event event = analizePosition(position);
+ if (event != null) {
+ Context.getConnectionManager().updateEvent(event);
+ }
+ return position;
+ }
+
+ protected abstract Event analizePosition(Position position);
+
+}
diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java
index 6e350f61d..634c6d6a4 100644
--- a/src/org/traccar/BasePipelineFactory.java
+++ b/src/org/traccar/BasePipelineFactory.java
@@ -29,6 +29,9 @@ import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.handler.logging.LoggingHandler;
import org.jboss.netty.handler.timeout.IdleStateHandler;
+import org.traccar.events.CommandResultEventHandler;
+import org.traccar.events.MotionEventHandler;
+import org.traccar.events.OverspeedEventHandler;
import org.traccar.helper.Log;
import java.net.InetSocketAddress;
@@ -44,6 +47,10 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
private LocationProviderHandler locationProviderHandler;
private HemisphereHandler hemisphereHandler;
+ private CommandResultEventHandler commandResultEventHandler;
+ private OverspeedEventHandler overspeedEventHandler;
+ private MotionEventHandler motionEventHandler;
+
private static final class OpenChannelHandler extends SimpleChannelHandler {
private final TrackerServer server;
@@ -122,6 +129,17 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
|| Context.getConfig().hasKey("location.longitudeHemisphere")) {
hemisphereHandler = new HemisphereHandler();
}
+
+ commandResultEventHandler = new CommandResultEventHandler();
+
+ if (Context.getConfig().getBoolean("event.overspeedHandler")) {
+ overspeedEventHandler = new OverspeedEventHandler();
+ }
+
+ if (Context.getConfig().getBoolean("event.motionHandler")) {
+ motionEventHandler = new MotionEventHandler();
+ }
+
}
protected abstract void addSpecificHandlers(ChannelPipeline pipeline);
@@ -162,9 +180,23 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
if (Context.getDataManager() != null) {
pipeline.addLast("dataHandler", new DefaultDataHandler());
}
+
if (Context.getConfig().getBoolean("forward.enable")) {
pipeline.addLast("webHandler", new WebDataHandler(Context.getConfig().getString("forward.url")));
}
+
+ if (commandResultEventHandler != null) {
+ pipeline.addLast("CommandResultEventHandler", commandResultEventHandler);
+ }
+
+ if (overspeedEventHandler != null) {
+ pipeline.addLast("OverspeedEventHandler", overspeedEventHandler);
+ }
+
+ if (motionEventHandler != null) {
+ pipeline.addLast("MotionEventHandler", motionEventHandler);
+ }
+
pipeline.addLast("mainHandler", new MainEventHandler());
return pipeline;
}
@@ -181,5 +213,4 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
}
}
}
-
}
diff --git a/src/org/traccar/api/AsyncSocket.java b/src/org/traccar/api/AsyncSocket.java
index 35e760613..2259f840f 100644
--- a/src/org/traccar/api/AsyncSocket.java
+++ b/src/org/traccar/api/AsyncSocket.java
@@ -20,6 +20,7 @@ import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.traccar.Context;
import org.traccar.database.ConnectionManager;
import org.traccar.model.Device;
+import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.web.JsonConverter;
@@ -32,6 +33,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
private static final String KEY_DEVICES = "devices";
private static final String KEY_POSITIONS = "positions";
+ private static final String KEY_EVENTS = "events";
private long userId;
@@ -65,6 +67,11 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
sendData(KEY_POSITIONS, Collections.singletonList(position));
}
+ @Override
+ public void onUpdateEvent(Event event) {
+ sendData(KEY_EVENTS, Collections.singletonList(event));
+ }
+
private void sendData(String key, Collection<?> data) {
if (!data.isEmpty() && isConnected()) {
JsonObjectBuilder json = Json.createObjectBuilder();
diff --git a/src/org/traccar/api/resource/EventResource.java b/src/org/traccar/api/resource/EventResource.java
new file mode 100644
index 000000000..74a748ea5
--- /dev/null
+++ b/src/org/traccar/api/resource/EventResource.java
@@ -0,0 +1,39 @@
+package org.traccar.api.resource;
+
+import java.sql.SQLException;
+import java.util.Collection;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+import org.traccar.Context;
+import org.traccar.api.BaseResource;
+import org.traccar.model.Event;
+
+@Path("events")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+
+public class EventResource extends BaseResource {
+
+ @Path("{id}")
+ @GET
+ public Event get(@PathParam("id") long id) throws SQLException {
+ Event event = Context.getDataManager().getEvent(id);
+ Context.getPermissionsManager().checkDevice(getUserId(), event.getDeviceId());
+ return event;
+ }
+
+ @GET
+ public Collection<Event> get(
+ @QueryParam("deviceId") long deviceId, @QueryParam("type") String type,
+ @QueryParam("interval") int interval) throws SQLException {
+ Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
+ return Context.getDataManager().getLastEvents(deviceId, type, interval);
+ }
+}
diff --git a/src/org/traccar/database/ConnectionManager.java b/src/org/traccar/database/ConnectionManager.java
index bb9958232..9e0da8485 100644
--- a/src/org/traccar/database/ConnectionManager.java
+++ b/src/org/traccar/database/ConnectionManager.java
@@ -23,6 +23,7 @@ import org.traccar.GlobalTimer;
import org.traccar.Protocol;
import org.traccar.helper.Log;
import org.traccar.model.Device;
+import org.traccar.model.Event;
import org.traccar.model.Position;
import java.net.SocketAddress;
@@ -85,14 +86,26 @@ public class ConnectionManager {
return;
}
- device.setStatus(status);
- if (time != null) {
- device.setLastUpdate(time);
+ if (status.equals(Device.STATUS_MOVING) || status.equals(Device.STATUS_STOPPED)) {
+ device.setMotion(status);
+ } else {
+ if (!status.equals(device.getStatus())) {
+ Event event = new Event(Event.TYPE_DEVICE_OFFLINE, deviceId);
+ if (status.equals(Device.STATUS_ONLINE)) {
+ event.setType(Event.TYPE_DEVICE_ONLINE);
+ }
+ updateEvent(event);
+ }
+ device.setStatus(status);
+
+ Timeout timeout = timeouts.remove(deviceId);
+ if (timeout != null) {
+ timeout.cancel();
+ }
}
- Timeout timeout = timeouts.remove(deviceId);
- if (timeout != null) {
- timeout.cancel();
+ if (time != null) {
+ device.setLastUpdate(time);
}
if (status.equals(Device.STATUS_ONLINE)) {
@@ -134,6 +147,22 @@ public class ConnectionManager {
}
}
+ public synchronized void updateEvent(Event event) {
+ long deviceId = event.getDeviceId();
+ try {
+ Context.getDataManager().addEvent(event);
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+ for (long userId : Context.getPermissionsManager().getDeviceUsers(deviceId)) {
+ if (listeners.containsKey(userId)) {
+ for (UpdateListener listener : listeners.get(userId)) {
+ listener.onUpdateEvent(event);
+ }
+ }
+ }
+ }
+
public Position getLastPosition(long deviceId) {
return positions.get(deviceId);
}
@@ -154,6 +183,7 @@ public class ConnectionManager {
public interface UpdateListener {
void onUpdateDevice(Device device);
void onUpdatePosition(Position position);
+ void onUpdateEvent(Event event);
}
public synchronized void addListener(long userId, UpdateListener listener) {
diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java
index ac3be45ac..dffb8937c 100644
--- a/src/org/traccar/database/DataManager.java
+++ b/src/org/traccar/database/DataManager.java
@@ -27,6 +27,7 @@ import org.traccar.Config;
import org.traccar.Context;
import org.traccar.helper.Log;
import org.traccar.model.Device;
+import org.traccar.model.Event;
import org.traccar.model.DevicePermission;
import org.traccar.model.Group;
import org.traccar.model.GroupPermission;
@@ -42,6 +43,7 @@ import java.net.URL;
import java.net.URLClassLoader;
import java.sql.SQLException;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
@@ -357,6 +359,9 @@ public class DataManager implements IdentityManager {
QueryBuilder.create(dataSource, getQuery("database.updateDeviceStatus"))
.setObject(device)
.executeUpdate();
+ Device cachedDevice = getDeviceById(device.getId());
+ cachedDevice.setStatus(device.getStatus());
+ cachedDevice.setMotion(device.getMotion());
}
public void removeDevice(long deviceId) throws SQLException {
@@ -465,6 +470,8 @@ public class DataManager implements IdentityManager {
.setDate("now", new Date())
.setObject(position)
.executeUpdate();
+ Device device = getDeviceById(position.getDeviceId());
+ device.setPositionId(position.getId());
}
public Collection<Position> getLatestPositions() throws SQLException {
@@ -482,4 +489,33 @@ public class DataManager implements IdentityManager {
.setObject(server)
.executeUpdate();
}
+
+ public Event getEvent(long eventId) throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectEvent"))
+ .setLong("id", eventId)
+ .executeQuerySingle(Event.class);
+ }
+
+ public void addEvent(Event event) throws SQLException {
+ event.setId(QueryBuilder.create(dataSource, getQuery("database.insertEvent"), true)
+ .setObject(event)
+ .executeUpdate());
+ }
+
+ public Collection<Event> getEvents(long deviceId, String type, Date from, Date to) throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery("database.selectEvents"))
+ .setLong("deviceId", deviceId)
+ .setString("type", type)
+ .setDate("from", from)
+ .setDate("to", to)
+ .executeQuery(Event.class);
+ }
+
+ public Collection<Event> getLastEvents(long deviceId, String type, int interval) throws SQLException {
+ Calendar calendar = Calendar.getInstance();
+ calendar.add(Calendar.SECOND, -interval);
+ Date to = calendar.getTime();
+ return getEvents(deviceId, type, new Date(), to);
+ }
+
}
diff --git a/src/org/traccar/events/CommandResultEventHandler.java b/src/org/traccar/events/CommandResultEventHandler.java
new file mode 100644
index 000000000..23c62566a
--- /dev/null
+++ b/src/org/traccar/events/CommandResultEventHandler.java
@@ -0,0 +1,18 @@
+package org.traccar.events;
+
+import org.traccar.BaseEventHandler;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class CommandResultEventHandler extends BaseEventHandler {
+
+ @Override
+ protected Event analizePosition(Position position) {
+ Object commandResult = position.getAttributes().get(Position.KEY_RESULT);
+ if (commandResult != null) {
+ return new Event(Event.TYPE_COMMAND_RESULT, position.getDeviceId(), position.getId());
+ }
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/events/MotionEventHandler.java b/src/org/traccar/events/MotionEventHandler.java
new file mode 100644
index 000000000..a3b81ddc4
--- /dev/null
+++ b/src/org/traccar/events/MotionEventHandler.java
@@ -0,0 +1,55 @@
+package org.traccar.events;
+
+import java.sql.SQLException;
+
+import org.traccar.BaseEventHandler;
+import org.traccar.Context;
+import org.traccar.helper.Log;
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class MotionEventHandler extends BaseEventHandler {
+
+ private static final double SPEED_THRESHOLD = 0.01;
+ private int suppressRepeated;
+
+ public MotionEventHandler() {
+ suppressRepeated = Context.getConfig().getInteger("event.suppressRepeated", 60);
+ }
+
+ @Override
+ protected Event analizePosition(Position position) {
+ Event event = null;
+
+ if (!isLastPosition()) {
+ return event;
+ }
+
+ double speed = position.getSpeed();
+ boolean valid = position.getValid();
+ Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId());
+ if (device == null) {
+ return event;
+ }
+ String motion = device.getMotion();
+ if (valid && speed > SPEED_THRESHOLD && !motion.equals(Device.STATUS_MOVING)) {
+ Context.getConnectionManager().updateDevice(position.getDeviceId(), Device.STATUS_MOVING, null);
+ event = new Event(Event.TYPE_DEVICE_MOVING, position.getDeviceId(), position.getId());
+ } else if (valid && speed < SPEED_THRESHOLD && motion.equals(Device.STATUS_MOVING)) {
+ Context.getConnectionManager().updateDevice(position.getDeviceId(), Device.STATUS_STOPPED, null);
+ event = new Event(Event.TYPE_DEVICE_STOPPED, position.getDeviceId(), position.getId());
+ }
+ try {
+ if (event != null && !Context.getDataManager().getLastEvents(
+ position.getDeviceId(), event.getType(), suppressRepeated).isEmpty()) {
+ event = null;
+ }
+
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+ return event;
+ }
+
+}
diff --git a/src/org/traccar/events/OverspeedEventHandler.java b/src/org/traccar/events/OverspeedEventHandler.java
new file mode 100644
index 000000000..30410ff32
--- /dev/null
+++ b/src/org/traccar/events/OverspeedEventHandler.java
@@ -0,0 +1,45 @@
+package org.traccar.events;
+
+import java.sql.SQLException;
+
+import org.traccar.BaseEventHandler;
+import org.traccar.Context;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.helper.Log;
+import org.traccar.helper.UnitsConverter;
+
+public class OverspeedEventHandler extends BaseEventHandler {
+
+ private double globalSpeedLimit;
+ private int suppressRepeated;
+
+ public OverspeedEventHandler() {
+ globalSpeedLimit = UnitsConverter.knotsFromKph(Context.getConfig().getInteger("event.globalSpeedLimit", 0));
+ suppressRepeated = Context.getConfig().getInteger("event.suppressRepeated", 60);
+ }
+
+ @Override
+ protected Event analizePosition(Position position) {
+ Event event = null;
+ if (!isLastPosition()) {
+ return event;
+ }
+ double speed = position.getSpeed();
+ boolean valid = position.getValid();
+
+ if (valid && globalSpeedLimit != 0 && speed > globalSpeedLimit) {
+ try {
+ if (Context.getDataManager().getLastEvents(
+ position.getDeviceId(), Event.TYPE_DEVICE_OVERSPEED, suppressRepeated).isEmpty()) {
+ event = new Event(Event.TYPE_DEVICE_OVERSPEED, position.getDeviceId(), position.getId());
+ }
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+
+ }
+ return event;
+ }
+
+}
diff --git a/src/org/traccar/model/Device.java b/src/org/traccar/model/Device.java
index 934e753b1..d32f9f851 100644
--- a/src/org/traccar/model/Device.java
+++ b/src/org/traccar/model/Device.java
@@ -101,4 +101,17 @@ public class Device {
this.groupId = groupId;
}
+ public static final String STATUS_MOVING = "moving";
+ public static final String STATUS_STOPPED = "stopped";
+
+ private String motion;
+
+ public String getMotion() {
+ return motion;
+ }
+
+ public void setMotion(String motion) {
+ this.motion = motion;
+ }
+
}
diff --git a/src/org/traccar/model/Event.java b/src/org/traccar/model/Event.java
new file mode 100644
index 000000000..6de885c70
--- /dev/null
+++ b/src/org/traccar/model/Event.java
@@ -0,0 +1,74 @@
+package org.traccar.model;
+
+import java.util.Date;
+
+public class Event extends Message {
+
+ public Event(String type, long deviceId, long positionId) {
+ this.setType(type);
+ this.setDeviceId(deviceId);
+ this.setPositionId(positionId);
+ this.serverTime = new Date();
+ }
+
+ public Event(String type, long deviceId) {
+ this.setType(type);
+ this.setDeviceId(deviceId);
+ this.serverTime = new Date();
+ }
+
+ public Event() {
+ }
+
+ private long id;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public static final String TYPE_COMMAND_RESULT = "commandResult";
+
+ public static final String TYPE_DEVICE_ONLINE = "deviceOnline";
+ public static final String TYPE_DEVICE_OFFLINE = "deviceOffline";
+
+ public static final String TYPE_DEVICE_MOVING = "deviceMoving";
+ public static final String TYPE_DEVICE_STOPPED = "deviceStopped";
+
+ public static final String TYPE_DEVICE_OVERSPEED = "deviceOverspeed";
+
+ public static final String TYPE_GEOFENCE_ENTER = "geofenceEnter";
+ public static final String TYPE_GEOFENCE_EXIT = "geofenceExit";
+
+ private Date serverTime;
+
+ public Date getServerTime() {
+ if (serverTime != null) {
+ return new Date(serverTime.getTime());
+ } else {
+ return null;
+ }
+ }
+
+ public void setServerTime(Date serverTime) {
+ if (serverTime != null) {
+ this.serverTime = new Date(serverTime.getTime());
+ } else {
+ this.serverTime = null;
+ }
+ }
+
+ private long positionId;
+
+ public long getPositionId() {
+ return positionId;
+ }
+
+ public void setPositionId(long positionId) {
+ this.positionId = positionId;
+ }
+
+}
diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java
index 8144af0b6..751db7a33 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.DeviceResource;
import org.traccar.api.resource.PositionResource;
import org.traccar.api.resource.CommandTypeResource;
+import org.traccar.api.resource.EventResource;
import org.traccar.helper.Log;
import javax.naming.InitialContext;
@@ -148,7 +149,8 @@ public class WebServer {
resourceConfig.register(CorsResponseFilter.class);
resourceConfig.registerClasses(ServerResource.class, SessionResource.class, CommandResource.class,
GroupPermissionResource.class, DevicePermissionResource.class, UserResource.class,
- GroupResource.class, DeviceResource.class, PositionResource.class, CommandTypeResource.class);
+ GroupResource.class, DeviceResource.class, PositionResource.class,
+ CommandTypeResource.class, EventResource.class);
servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*");
handlers.addHandler(servletHandler);