aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAbyss777 <abyss@fox5.ru>2017-08-08 09:53:31 +0500
committerAbyss777 <abyss@fox5.ru>2017-08-09 11:04:29 +0500
commit94a922eaf167a73a0188255398fb1c6799767e18 (patch)
treed1f23e82db216a1c859a096a2c4f7f8676d50a6b
parentc76acd69275ae2d6612bb731758df8a5b91e6508 (diff)
downloadtrackermap-server-94a922eaf167a73a0188255398fb1c6799767e18.tar.gz
trackermap-server-94a922eaf167a73a0188255398fb1c6799767e18.tar.bz2
trackermap-server-94a922eaf167a73a0188255398fb1c6799767e18.zip
Implement motion detection similar to trips detection
-rw-r--r--src/org/traccar/database/ConnectionManager.java45
-rw-r--r--src/org/traccar/database/DeviceManager.java12
-rw-r--r--src/org/traccar/events/MotionEventHandler.java75
-rw-r--r--src/org/traccar/model/DeviceState.java41
-rw-r--r--src/org/traccar/reports/ReportUtils.java2
-rw-r--r--test/org/traccar/events/MotionEventHandlerTest.java56
6 files changed, 207 insertions, 24 deletions
diff --git a/src/org/traccar/database/ConnectionManager.java b/src/org/traccar/database/ConnectionManager.java
index 1445fb785..b3b00fefa 100644
--- a/src/org/traccar/database/ConnectionManager.java
+++ b/src/org/traccar/database/ConnectionManager.java
@@ -23,8 +23,11 @@ import org.traccar.GlobalTimer;
import org.traccar.Protocol;
import org.traccar.helper.Log;
import org.traccar.model.Device;
+import org.traccar.model.DeviceState;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.reports.ReportUtils;
+import org.traccar.reports.model.TripsConfig;
import java.net.SocketAddress;
import java.sql.SQLException;
@@ -41,6 +44,7 @@ public class ConnectionManager {
private final long deviceTimeout;
private final boolean enableStatusEvents;
+ private TripsConfig tripsConfig = null;
private final Map<Long, ActiveDevice> activeDevices = new ConcurrentHashMap<>();
private final Map<Long, Set<UpdateListener>> listeners = new ConcurrentHashMap<>();
@@ -49,6 +53,9 @@ public class ConnectionManager {
public ConnectionManager() {
deviceTimeout = Context.getConfig().getLong("status.timeout", DEFAULT_TIMEOUT) * 1000;
enableStatusEvents = Context.getConfig().getBoolean("event.enable");
+ if (Context.getConfig().getBoolean("status.updateDeviceState")) {
+ tripsConfig = ReportUtils.initTripsConfig();
+ }
}
public void addActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) {
@@ -80,21 +87,30 @@ public class ConnectionManager {
if (enableStatusEvents && !status.equals(oldStatus)) {
String eventType;
+ Event stateEvent = null;
switch (status) {
case Device.STATUS_ONLINE:
eventType = Event.TYPE_DEVICE_ONLINE;
break;
case Device.STATUS_UNKNOWN:
eventType = Event.TYPE_DEVICE_UNKNOWN;
+ if (tripsConfig != null) {
+ stateEvent = updateDeviceState(deviceId);
+ }
break;
default:
eventType = Event.TYPE_DEVICE_OFFLINE;
+ if (tripsConfig != null) {
+ stateEvent = updateDeviceState(deviceId);
+ }
break;
}
- Event event = new Event(eventType, deviceId);
- if (Context.getNotificationManager() != null) {
- Context.getNotificationManager().updateEvent(event, null);
+ Set<Event> events = new HashSet<>();
+ if (stateEvent != null) {
+ events.add(stateEvent);
}
+ events.add(new Event(eventType, deviceId));
+ Context.getNotificationManager().updateEvents(events, null);
}
Timeout timeout = timeouts.remove(deviceId);
@@ -126,6 +142,29 @@ public class ConnectionManager {
updateDevice(device);
}
+ public Event updateDeviceState(long deviceId) {
+ DeviceState deviceState = Context.getDeviceManager().getDeviceState(deviceId);
+ if (deviceState == null || deviceState.getMotionState() == null) {
+ return null;
+ }
+ Event result = null;
+ Boolean oldMotion = deviceState.getMotionState();
+ long currentTime = new Date().getTime();
+ boolean newMotion = !oldMotion;
+ Position potentialPosition = deviceState.getMotionPosition();
+ if (potentialPosition != null) {
+ long potentialTime = potentialPosition.getFixTime().getTime()
+ + (newMotion ? tripsConfig.getMinimalTripDuration() : tripsConfig.getMinimalParkingDuration());
+ if (potentialTime <= currentTime) {
+ String eventType = newMotion ? Event.TYPE_DEVICE_MOVING : Event.TYPE_DEVICE_STOPPED;
+ result = new Event(eventType, potentialPosition.getDeviceId(), potentialPosition.getId());
+ deviceState.setMotionState(newMotion);
+ deviceState.setMotionPosition(null);
+ }
+ }
+ return result;
+ }
+
public synchronized void updateDevice(Device device) {
for (long userId : Context.getPermissionsManager().getDeviceUsers(device.getId())) {
if (listeners.containsKey(userId)) {
diff --git a/src/org/traccar/database/DeviceManager.java b/src/org/traccar/database/DeviceManager.java
index 5d123f9b8..3b7e5c617 100644
--- a/src/org/traccar/database/DeviceManager.java
+++ b/src/org/traccar/database/DeviceManager.java
@@ -33,6 +33,7 @@ import org.traccar.helper.Log;
import org.traccar.model.Command;
import org.traccar.model.CommandType;
import org.traccar.model.Device;
+import org.traccar.model.DeviceState;
import org.traccar.model.DeviceTotalDistance;
import org.traccar.model.Group;
import org.traccar.model.Position;
@@ -52,6 +53,8 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
private final Map<Long, Position> positions = new ConcurrentHashMap<>();
+ private final Map<Long, DeviceState> deviceStates = new ConcurrentHashMap<>();
+
private boolean fallbackToText;
public DeviceManager(DataManager dataManager) {
@@ -387,4 +390,13 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
}
return result;
}
+
+ public DeviceState getDeviceState(long deviceId) {
+ return deviceStates.get(deviceId);
+ }
+
+ public void setDeviceState(long deviceId, DeviceState deviceState) {
+ deviceStates.put(deviceId, deviceState);
+ }
+
}
diff --git a/src/org/traccar/events/MotionEventHandler.java b/src/org/traccar/events/MotionEventHandler.java
index ed81176a8..9168d0fd8 100644
--- a/src/org/traccar/events/MotionEventHandler.java
+++ b/src/org/traccar/events/MotionEventHandler.java
@@ -21,11 +21,60 @@ import java.util.Collections;
import org.traccar.BaseEventHandler;
import org.traccar.Context;
import org.traccar.model.Device;
+import org.traccar.model.DeviceState;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.reports.ReportUtils;
+import org.traccar.reports.model.TripsConfig;
public class MotionEventHandler extends BaseEventHandler {
+ private TripsConfig tripsConfig;
+
+ public MotionEventHandler() {
+ if (Context.getConfig() != null) {
+ tripsConfig = ReportUtils.initTripsConfig();
+ }
+ }
+
+ public static Event updateMotionState(DeviceState deviceState, Position position, TripsConfig tripsConfig) {
+ Event result = null;
+ Boolean oldMotion = deviceState.getMotionState();
+
+ long currentTime = position.getFixTime().getTime();
+ boolean newMotion = position.getBoolean(Position.KEY_MOTION);
+ if (newMotion != oldMotion) {
+ if (deviceState.getMotionPosition() == null) {
+ deviceState.setMotionPosition(position);
+ }
+ } else {
+ deviceState.setMotionPosition(null);
+ }
+
+ Position potentialPosition = deviceState.getMotionPosition();
+ if (potentialPosition != null) {
+ long potentialTime = potentialPosition.getFixTime().getTime();
+ double distance = ReportUtils.calculateDistance(potentialPosition, position, false);
+ if (newMotion) {
+ if (potentialTime + tripsConfig.getMinimalTripDuration() <= currentTime
+ || distance >= tripsConfig.getMinimalTripDistance()) {
+ result = new Event(Event.TYPE_DEVICE_MOVING, potentialPosition.getDeviceId(),
+ potentialPosition.getId());
+ deviceState.setMotionState(true);
+ deviceState.setMotionPosition(null);
+ }
+ } else {
+ if (potentialTime + tripsConfig.getMinimalParkingDuration() <= currentTime) {
+ result = new Event(Event.TYPE_DEVICE_STOPPED, potentialPosition.getDeviceId(),
+ potentialPosition.getId());
+ deviceState.setMotionState(false);
+ deviceState.setMotionPosition(null);
+ }
+ }
+ }
+ return result;
+ }
+
@Override
protected Collection<Event> analyzePosition(Position position) {
@@ -37,18 +86,22 @@ public class MotionEventHandler extends BaseEventHandler {
return null;
}
- boolean motion = position.getBoolean(Position.KEY_MOTION);
- boolean oldMotion = false;
- Position lastPosition = Context.getIdentityManager().getLastPosition(position.getDeviceId());
- if (lastPosition != null) {
- oldMotion = lastPosition.getBoolean(Position.KEY_MOTION);
+ Event result = null;
+
+ long deviceId = position.getDeviceId();
+ DeviceState deviceState = Context.getDeviceManager().getDeviceState(deviceId);
+
+ if (deviceState == null) {
+ deviceState = new DeviceState();
+ deviceState.setMotionState(position.getBoolean(Position.KEY_MOTION));
+ } else if (deviceState.getMotionState() == null) {
+ deviceState.setMotionState(position.getBoolean(Position.KEY_MOTION));
+ } else {
+ result = updateMotionState(deviceState, position, tripsConfig);
}
- if (motion && !oldMotion) {
- return Collections.singleton(
- new Event(Event.TYPE_DEVICE_MOVING, position.getDeviceId(), position.getId()));
- } else if (!motion && oldMotion) {
- return Collections.singleton(
- new Event(Event.TYPE_DEVICE_STOPPED, position.getDeviceId(), position.getId()));
+ Context.getDeviceManager().setDeviceState(deviceId, deviceState);
+ if (result != null) {
+ return Collections.singleton(result);
}
return null;
}
diff --git a/src/org/traccar/model/DeviceState.java b/src/org/traccar/model/DeviceState.java
new file mode 100644
index 000000000..3626b9953
--- /dev/null
+++ b/src/org/traccar/model/DeviceState.java
@@ -0,0 +1,41 @@
+/*
+ * 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.model;
+
+public class DeviceState {
+
+ private Boolean motionState;
+
+ public void setMotionState(boolean motionState) {
+ this.motionState = motionState;
+ }
+
+ public Boolean getMotionState() {
+ return motionState;
+ }
+
+ private Position motionPosition;
+
+ public void setMotionPosition(Position motionPosition) {
+ this.motionPosition = motionPosition;
+ }
+
+ public Position getMotionPosition() {
+ return motionPosition;
+ }
+
+}
diff --git a/src/org/traccar/reports/ReportUtils.java b/src/org/traccar/reports/ReportUtils.java
index 540feb6c6..e8db7e3b5 100644
--- a/src/org/traccar/reports/ReportUtils.java
+++ b/src/org/traccar/reports/ReportUtils.java
@@ -157,8 +157,8 @@ public final class ReportUtils {
public static TripsConfig initTripsConfig() {
return new TripsConfig(
- Context.getConfig().getLong("report.trip.minimalTripDuration", 300) * 1000,
Context.getConfig().getLong("report.trip.minimalTripDistance", 500),
+ Context.getConfig().getLong("report.trip.minimalTripDuration", 300) * 1000,
Context.getConfig().getLong("report.trip.minimalParkingDuration", 300) * 1000,
Context.getConfig().getBoolean("report.trip.greedyParking"),
Context.getConfig().getLong("report.trip.minimalNoDataDuration", 3600) * 1000);
diff --git a/test/org/traccar/events/MotionEventHandlerTest.java b/test/org/traccar/events/MotionEventHandlerTest.java
index f05ef54d5..c44f3f4eb 100644
--- a/test/org/traccar/events/MotionEventHandlerTest.java
+++ b/test/org/traccar/events/MotionEventHandlerTest.java
@@ -2,28 +2,66 @@ package org.traccar.events;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
-import java.util.Collection;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
import org.junit.Test;
import org.traccar.BaseTest;
+import org.traccar.model.DeviceState;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.reports.model.TripsConfig;
public class MotionEventHandlerTest extends BaseTest {
-
+
+ private Date date(String time) throws ParseException {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat.parse(time);
+ }
+
@Test
public void testMotionEventHandler() throws Exception {
-
- MotionEventHandler motionEventHandler = new MotionEventHandler();
-
+ TripsConfig tripsConfig = new TripsConfig(500, 300 * 1000, 300 * 1000, false, 0);
+
Position position = new Position();
+ position.setTime(date("2017-01-01 00:00:00"));
position.set(Position.KEY_MOTION, true);
- position.setValid(true);
- Collection<Event> events = motionEventHandler.analyzePosition(position);
- assertNotNull(events);
- Event event = (Event) events.toArray()[0];
+ position.set(Position.KEY_TOTAL_DISTANCE, 0);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setMotionState(false);
+ deviceState.setMotionPosition(position);
+ Position nextPosition = new Position();
+
+ nextPosition.setTime(date("2017-01-01 00:02:00"));
+ nextPosition.set(Position.KEY_MOTION, true);
+ nextPosition.set(Position.KEY_TOTAL_DISTANCE, 200);
+
+ Event event = MotionEventHandler.updateMotionState(deviceState, nextPosition, tripsConfig);
+ assertNull(event);
+
+ nextPosition.set(Position.KEY_TOTAL_DISTANCE, 600);
+ event = MotionEventHandler.updateMotionState(deviceState, nextPosition, tripsConfig);
+ assertNotNull(event);
+ assertEquals(Event.TYPE_DEVICE_MOVING, event.getType());
+ assertTrue(deviceState.getMotionState());
+ assertNull(deviceState.getMotionPosition());
+
+ deviceState.setMotionState(false);
+ deviceState.setMotionPosition(position);
+ nextPosition.setTime(date("2017-01-01 00:06:00"));
+ nextPosition.set(Position.KEY_TOTAL_DISTANCE, 200);
+ event = MotionEventHandler.updateMotionState(deviceState, nextPosition, tripsConfig);
+ assertNotNull(event);
assertEquals(Event.TYPE_DEVICE_MOVING, event.getType());
+ assertTrue(deviceState.getMotionState());
+ assertNull(deviceState.getMotionPosition());
}
}