aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debug.xml3
-rw-r--r--setup/unix/traccar.xml3
-rw-r--r--setup/windows/traccar.xml3
-rw-r--r--src/org/traccar/BaseProtocol.java5
-rw-r--r--src/org/traccar/BaseProtocolDecoder.java6
-rw-r--r--src/org/traccar/Context.java17
-rw-r--r--src/org/traccar/DefaultDataHandler.java5
-rw-r--r--src/org/traccar/DistanceHandler.java4
-rw-r--r--src/org/traccar/FilterHandler.java4
-rw-r--r--src/org/traccar/MainEventHandler.java7
-rw-r--r--src/org/traccar/api/AsyncSocket.java2
-rw-r--r--src/org/traccar/api/resource/DeviceResource.java10
-rw-r--r--src/org/traccar/api/resource/PositionResource.java2
-rw-r--r--src/org/traccar/database/ConnectionManager.java72
-rw-r--r--src/org/traccar/database/DataManager.java133
-rw-r--r--src/org/traccar/database/DeviceManager.java261
-rw-r--r--src/org/traccar/database/GeofenceManager.java4
-rw-r--r--src/org/traccar/database/IdentityManager.java3
-rw-r--r--src/org/traccar/database/PermissionsManager.java2
-rw-r--r--src/org/traccar/database/QueryBuilder.java12
-rw-r--r--src/org/traccar/events/GeofenceEventHandler.java2
-rw-r--r--src/org/traccar/events/MotionEventHandler.java23
-rw-r--r--src/org/traccar/events/OverspeedEventHandler.java2
-rw-r--r--src/org/traccar/model/Position.java9
-rw-r--r--src/org/traccar/protocol/CarcellProtocol.java52
-rw-r--r--src/org/traccar/protocol/CarcellProtocolDecoder.java166
-rw-r--r--src/org/traccar/protocol/CarcellProtocolEncoder.java40
-rw-r--r--src/org/traccar/protocol/Gt06ProtocolDecoder.java14
-rw-r--r--src/org/traccar/protocol/MegastekProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/MiniFinderProtocolDecoder.java26
-rw-r--r--src/org/traccar/protocol/SuntechProtocol.java5
-rw-r--r--src/org/traccar/protocol/SuntechProtocolEncoder.java16
-rw-r--r--src/org/traccar/protocol/T800xProtocol.java6
-rw-r--r--src/org/traccar/protocol/T800xProtocolDecoder.java11
-rw-r--r--src/org/traccar/protocol/T800xProtocolEncoder.java63
-rw-r--r--test/org/traccar/ProtocolTest.java5
-rw-r--r--test/org/traccar/protocol/Gt06ProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/T800xProtocolEncoderTest.java23
-rwxr-xr-xtools/test-commands.py2
-rw-r--r--web/app/GeofenceConverter.js4
-rw-r--r--web/app/view/GroupsController.js1
41 files changed, 791 insertions, 242 deletions
diff --git a/debug.xml b/debug.xml
index 1ff0b33cd..9c8352589 100644
--- a/debug.xml
+++ b/debug.xml
@@ -190,7 +190,7 @@
</entry>
<entry key='database.insertGroup'>
- INSERT INTO groups (name) VALUES (:name);
+ INSERT INTO groups (name, groupId) VALUES (:name, :groupId);
</entry>
<entry key='database.updateGroup'>
@@ -443,5 +443,6 @@
<entry key='huasheng.port'>5111</entry>
<entry key='l100.port'>5112</entry>
<entry key='granit.port'>5113</entry>
+ <entry key='carcell.port'>5114</entry>
</properties>
diff --git a/setup/unix/traccar.xml b/setup/unix/traccar.xml
index 83abb48fd..8a119f35a 100644
--- a/setup/unix/traccar.xml
+++ b/setup/unix/traccar.xml
@@ -139,7 +139,7 @@
</entry>
<entry key='database.insertGroup'>
- INSERT INTO groups (name) VALUES (:name);
+ INSERT INTO groups (name, groupId) VALUES (:name, :groupId);
</entry>
<entry key='database.updateGroup'>
@@ -388,5 +388,6 @@
<entry key='huasheng.port'>5111</entry>
<entry key='l100.port'>5112</entry>
<entry key='granit.port'>5113</entry>
+ <entry key='carcell.port'>5114</entry>
</properties>
diff --git a/setup/windows/traccar.xml b/setup/windows/traccar.xml
index 72f04d0a0..049034b01 100644
--- a/setup/windows/traccar.xml
+++ b/setup/windows/traccar.xml
@@ -139,7 +139,7 @@
</entry>
<entry key='database.insertGroup'>
- INSERT INTO groups (name) VALUES (:name);
+ INSERT INTO groups (name, groupId) VALUES (:name, :groupId);
</entry>
<entry key='database.updateGroup'>
@@ -388,5 +388,6 @@
<entry key='huasheng.port'>5111</entry>
<entry key='l100.port'>5112</entry>
<entry key='granit.port'>5113</entry>
+ <entry key='carcell.port'>5114</entry>
</properties>
diff --git a/src/org/traccar/BaseProtocol.java b/src/org/traccar/BaseProtocol.java
index 4a3e63db4..eb09022f5 100644
--- a/src/org/traccar/BaseProtocol.java
+++ b/src/org/traccar/BaseProtocol.java
@@ -33,7 +33,6 @@ public abstract class BaseProtocol implements Protocol {
public BaseProtocol(String name) {
this.name = name;
- supportedCommands.add(Command.TYPE_CUSTOM);
}
@Override
@@ -47,7 +46,9 @@ public abstract class BaseProtocol implements Protocol {
@Override
public Collection<String> getSupportedCommands() {
- return supportedCommands;
+ Set<String> commands = new HashSet<>(supportedCommands);
+ commands.add(Command.TYPE_CUSTOM);
+ return commands;
}
@Override
diff --git a/src/org/traccar/BaseProtocolDecoder.java b/src/org/traccar/BaseProtocolDecoder.java
index 3429f41f0..9a253e351 100644
--- a/src/org/traccar/BaseProtocolDecoder.java
+++ b/src/org/traccar/BaseProtocolDecoder.java
@@ -47,7 +47,9 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
Device device = Context.getIdentityManager().getDeviceByUniqueId(uniqueId);
if (device != null) {
deviceId = device.getId();
- Context.getConnectionManager().addActiveDevice(deviceId, protocol, channel, remoteAddress);
+ if (Context.getConnectionManager() != null) {
+ Context.getConnectionManager().addActiveDevice(deviceId, protocol, channel, remoteAddress);
+ }
return true;
} else {
deviceId = 0;
@@ -78,7 +80,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
public void getLastLocation(Position position, Date deviceTime) {
position.setOutdated(true);
- Position last = Context.getConnectionManager().getLastPosition(getDeviceId());
+ Position last = Context.getIdentityManager().getLastPosition(getDeviceId());
if (last != null) {
position.setFixTime(last.getFixTime());
position.setValid(last.getValid());
diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java
index 0bb18b675..d32837755 100644
--- a/src/org/traccar/Context.java
+++ b/src/org/traccar/Context.java
@@ -18,6 +18,7 @@ package org.traccar;
import com.ning.http.client.AsyncHttpClient;
import org.traccar.database.ConnectionManager;
import org.traccar.database.DataManager;
+import org.traccar.database.DeviceManager;
import org.traccar.database.IdentityManager;
import org.traccar.database.NotificationManager;
import org.traccar.database.PermissionsManager;
@@ -67,6 +68,12 @@ public final class Context {
return dataManager;
}
+ private static DeviceManager deviceManager;
+
+ public static DeviceManager getDeviceManager() {
+ return deviceManager;
+ }
+
private static ConnectionManager connectionManager;
public static ConnectionManager getConnectionManager() {
@@ -142,7 +149,12 @@ public final class Context {
if (config.hasKey("database.url")) {
dataManager = new DataManager(config);
}
- identityManager = dataManager;
+
+ if (dataManager != null) {
+ deviceManager = new DeviceManager(dataManager);
+ }
+
+ identityManager = deviceManager;
if (config.getBoolean("geocoder.enable")) {
String type = config.getString("geocoder.type", "google");
@@ -205,7 +217,7 @@ public final class Context {
permissionsManager = new PermissionsManager(dataManager);
- connectionManager = new ConnectionManager(dataManager);
+ connectionManager = new ConnectionManager();
if (config.getBoolean("event.geofenceHandler")) {
geofenceManager = new GeofenceManager(dataManager);
@@ -225,7 +237,6 @@ public final class Context {
public static void init(IdentityManager testIdentityManager) {
config = new Config();
- connectionManager = new ConnectionManager(null);
identityManager = testIdentityManager;
}
diff --git a/src/org/traccar/DefaultDataHandler.java b/src/org/traccar/DefaultDataHandler.java
index 594961389..8923c3a0e 100644
--- a/src/org/traccar/DefaultDataHandler.java
+++ b/src/org/traccar/DefaultDataHandler.java
@@ -25,10 +25,7 @@ public class DefaultDataHandler extends BaseDataHandler {
try {
Context.getDataManager().addPosition(position);
- Position lastPosition = Context.getConnectionManager().getLastPosition(position.getDeviceId());
- if (lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) > 0) {
- Context.getDataManager().updateLatestPosition(position);
- }
+ Context.getDeviceManager().updateLatestPosition(position);
} catch (Exception error) {
Log.warning(error);
}
diff --git a/src/org/traccar/DistanceHandler.java b/src/org/traccar/DistanceHandler.java
index b22815033..9f174a6c7 100644
--- a/src/org/traccar/DistanceHandler.java
+++ b/src/org/traccar/DistanceHandler.java
@@ -25,8 +25,8 @@ import java.math.RoundingMode;
public class DistanceHandler extends BaseDataHandler {
private Position getLastPosition(long deviceId) {
- if (Context.getConnectionManager() != null) {
- return Context.getConnectionManager().getLastPosition(deviceId);
+ if (Context.getIdentityManager() != null) {
+ return Context.getIdentityManager().getLastPosition(deviceId);
}
return null;
}
diff --git a/src/org/traccar/FilterHandler.java b/src/org/traccar/FilterHandler.java
index ab570ee26..5315bad9e 100644
--- a/src/org/traccar/FilterHandler.java
+++ b/src/org/traccar/FilterHandler.java
@@ -57,8 +57,8 @@ public class FilterHandler extends BaseDataHandler {
}
private Position getLastPosition(long deviceId) {
- if (Context.getConnectionManager() != null) {
- return Context.getConnectionManager().getLastPosition(deviceId);
+ if (Context.getIdentityManager() != null) {
+ return Context.getIdentityManager().getLastPosition(deviceId);
}
return null;
}
diff --git a/src/org/traccar/MainEventHandler.java b/src/org/traccar/MainEventHandler.java
index a0df34de5..771009aca 100644
--- a/src/org/traccar/MainEventHandler.java
+++ b/src/org/traccar/MainEventHandler.java
@@ -37,7 +37,7 @@ public class MainEventHandler extends IdleStateAwareChannelHandler {
Position position = (Position) e.getMessage();
- String uniqueId = Context.getDataManager().getDeviceById(position.getDeviceId()).getUniqueId();
+ String uniqueId = Context.getIdentityManager().getDeviceById(position.getDeviceId()).getUniqueId();
// Log position
StringBuilder s = new StringBuilder();
@@ -54,11 +54,6 @@ public class MainEventHandler extends IdleStateAwareChannelHandler {
s.append(", result: ").append(cmdResult);
}
Log.info(s.toString());
-
- Position lastPosition = Context.getConnectionManager().getLastPosition(position.getDeviceId());
- if (lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) > 0) {
- Context.getConnectionManager().updatePosition(position);
- }
}
}
diff --git a/src/org/traccar/api/AsyncSocket.java b/src/org/traccar/api/AsyncSocket.java
index d1e5594f4..4422dbccd 100644
--- a/src/org/traccar/api/AsyncSocket.java
+++ b/src/org/traccar/api/AsyncSocket.java
@@ -48,7 +48,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
super.onWebSocketConnect(session);
Map<String, Collection<?>> data = new HashMap<>();
- data.put(KEY_POSITIONS, Context.getConnectionManager().getInitialState(userId));
+ data.put(KEY_POSITIONS, Context.getDeviceManager().getInitialState(userId));
sendData(data);
Context.getConnectionManager().addListener(userId, this);
diff --git a/src/org/traccar/api/resource/DeviceResource.java b/src/org/traccar/api/resource/DeviceResource.java
index d6c53dc04..f48df7553 100644
--- a/src/org/traccar/api/resource/DeviceResource.java
+++ b/src/org/traccar/api/resource/DeviceResource.java
@@ -44,20 +44,20 @@ public class DeviceResource extends BaseResource {
@QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException {
if (all) {
Context.getPermissionsManager().checkAdmin(getUserId());
- return Context.getDataManager().getAllDevicesCached();
+ return Context.getDeviceManager().getAllDevices();
} else {
if (userId == 0) {
userId = getUserId();
}
Context.getPermissionsManager().checkUser(getUserId(), userId);
- return Context.getDataManager().getDevices(userId);
+ return Context.getDeviceManager().getDevices(userId);
}
}
@POST
public Response add(Device entity) throws SQLException {
Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getDataManager().addDevice(entity);
+ Context.getDeviceManager().addDevice(entity);
Context.getDataManager().linkDevice(getUserId(), entity.getId());
Context.getPermissionsManager().refresh();
if (Context.getGeofenceManager() != null) {
@@ -71,7 +71,7 @@ public class DeviceResource extends BaseResource {
public Response update(@PathParam("id") long id, Device entity) throws SQLException {
Context.getPermissionsManager().checkReadonly(getUserId());
Context.getPermissionsManager().checkDevice(getUserId(), id);
- Context.getDataManager().updateDevice(entity);
+ Context.getDeviceManager().updateDevice(entity);
if (Context.getGeofenceManager() != null) {
Context.getGeofenceManager().refresh();
}
@@ -83,7 +83,7 @@ public class DeviceResource extends BaseResource {
public Response remove(@PathParam("id") long id) throws SQLException {
Context.getPermissionsManager().checkReadonly(getUserId());
Context.getPermissionsManager().checkDevice(getUserId(), id);
- Context.getDataManager().removeDevice(id);
+ Context.getDeviceManager().removeDevice(id);
Context.getPermissionsManager().refresh();
if (Context.getGeofenceManager() != null) {
Context.getGeofenceManager().refresh();
diff --git a/src/org/traccar/api/resource/PositionResource.java b/src/org/traccar/api/resource/PositionResource.java
index 2da517d4f..e00e06e7a 100644
--- a/src/org/traccar/api/resource/PositionResource.java
+++ b/src/org/traccar/api/resource/PositionResource.java
@@ -39,7 +39,7 @@ public class PositionResource extends BaseResource {
@QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to)
throws SQLException {
if (deviceId == 0) {
- return Context.getConnectionManager().getInitialState(getUserId());
+ return Context.getDeviceManager().getInitialState(getUserId());
} else {
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
return Context.getDataManager().getPositions(
diff --git a/src/org/traccar/database/ConnectionManager.java b/src/org/traccar/database/ConnectionManager.java
index 8796673b1..46ccab81e 100644
--- a/src/org/traccar/database/ConnectionManager.java
+++ b/src/org/traccar/database/ConnectionManager.java
@@ -28,12 +28,9 @@ import org.traccar.model.Position;
import java.net.SocketAddress;
import java.sql.SQLException;
-import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -45,21 +42,11 @@ public class ConnectionManager {
private final long deviceTimeout;
private final Map<Long, ActiveDevice> activeDevices = new HashMap<>();
- private final Map<Long, Position> positions = new HashMap<>();
private final Map<Long, Set<UpdateListener>> listeners = new HashMap<>();
private final Map<Long, Timeout> timeouts = new HashMap<>();
- public ConnectionManager(DataManager dataManager) {
+ public ConnectionManager() {
deviceTimeout = Context.getConfig().getLong("status.timeout", DEFAULT_TIMEOUT) * 1000;
- if (dataManager != null) {
- try {
- for (Position position : dataManager.getLatestPositions()) {
- positions.put(position.getDeviceId(), position);
- }
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
}
public void addActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) {
@@ -80,31 +67,28 @@ public class ConnectionManager {
return activeDevices.get(deviceId);
}
- public synchronized void updateDevice(final long deviceId, String status, Date time) {
+ public void updateDevice(final long deviceId, String status, Date time) {
Device device = Context.getIdentityManager().getDeviceById(deviceId);
if (device == null) {
return;
}
- 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);
- }
- if (Context.getNotificationManager() != null) {
- Context.getNotificationManager().updateEvent(event, null);
- }
+ 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);
}
- device.setStatus(status);
-
- Timeout timeout = timeouts.remove(deviceId);
- if (timeout != null) {
- timeout.cancel();
+ if (Context.getNotificationManager() != null) {
+ Context.getNotificationManager().updateEvent(event, null);
}
}
+ device.setStatus(status);
+
+ Timeout timeout = timeouts.remove(deviceId);
+ if (timeout != null) {
+ timeout.cancel();
+ }
+
if (time != null) {
device.setLastUpdate(time);
@@ -122,12 +106,16 @@ public class ConnectionManager {
}
try {
- Context.getDataManager().updateDeviceStatus(device);
+ Context.getDeviceManager().updateDeviceStatus(device);
} catch (SQLException error) {
Log.warning(error);
}
- for (long userId : Context.getPermissionsManager().getDeviceUsers(deviceId)) {
+ updateDevice(device);
+ }
+
+ public synchronized void updateDevice(Device device) {
+ for (long userId : Context.getPermissionsManager().getDeviceUsers(device.getId())) {
if (listeners.containsKey(userId)) {
for (UpdateListener listener : listeners.get(userId)) {
listener.onUpdateDevice(device);
@@ -138,7 +126,6 @@ public class ConnectionManager {
public synchronized void updatePosition(Position position) {
long deviceId = position.getDeviceId();
- positions.put(deviceId, position);
for (long userId : Context.getPermissionsManager().getDeviceUsers(deviceId)) {
if (listeners.containsKey(userId)) {
@@ -157,23 +144,6 @@ public class ConnectionManager {
}
}
- public Position getLastPosition(long deviceId) {
- return positions.get(deviceId);
- }
-
- public synchronized Collection<Position> getInitialState(long userId) {
-
- List<Position> result = new LinkedList<>();
-
- for (long deviceId : Context.getPermissionsManager().getDevicePermissions(userId)) {
- if (positions.containsKey(deviceId)) {
- result.add(positions.get(deviceId));
- }
- }
-
- return result;
- }
-
public interface UpdateListener {
void onUpdateDevice(Device device);
void onUpdatePosition(Position position);
diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java
index bdd2e9adf..e5a245568 100644
--- a/src/org/traccar/database/DataManager.java
+++ b/src/org/traccar/database/DataManager.java
@@ -62,9 +62,9 @@ import org.traccar.model.GeofencePermission;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
-public class DataManager implements IdentityManager {
+public class DataManager {
- private static final long DEFAULT_REFRESH_DELAY = 300;
+ public static final long DEFAULT_REFRESH_DELAY = 300;
private final Config config;
@@ -72,11 +72,6 @@ public class DataManager implements IdentityManager {
private final long dataRefreshDelay;
- private final ReadWriteLock devicesLock = new ReentrantReadWriteLock();
- private final Map<Long, Device> devicesById = new HashMap<>();
- private final Map<String, Device> devicesByUniqueId = new HashMap<>();
- private long devicesLastUpdate;
-
private final ReadWriteLock groupsLock = new ReentrantReadWriteLock();
private final Map<Long, Group> groupsById = new HashMap<>();
private long groupsLastUpdate;
@@ -136,85 +131,6 @@ public class DataManager implements IdentityManager {
}
}
- private void updateDeviceCache(boolean force) throws SQLException {
- boolean needWrite;
- devicesLock.readLock().lock();
- try {
- needWrite = force || System.currentTimeMillis() - devicesLastUpdate > dataRefreshDelay;
- } finally {
- devicesLock.readLock().unlock();
- }
-
- if (needWrite) {
- devicesLock.writeLock().lock();
- try {
- if (force || System.currentTimeMillis() - devicesLastUpdate > dataRefreshDelay) {
- devicesById.clear();
- devicesByUniqueId.clear();
- ConnectionManager connectionManager = Context.getConnectionManager();
- GeofenceManager geofenceManager = Context.getGeofenceManager();
- for (Device device : getAllDevices()) {
- devicesById.put(device.getId(), device);
- devicesByUniqueId.put(device.getUniqueId(), device);
- if (connectionManager != null && geofenceManager != null) {
- Position lastPosition = connectionManager.getLastPosition(device.getId());
- if (lastPosition != null) {
- device.setGeofenceIds(geofenceManager.getCurrentDeviceGeofences(lastPosition));
- }
- }
- }
- devicesLastUpdate = System.currentTimeMillis();
- }
- } finally {
- devicesLock.writeLock().unlock();
- }
- }
- }
-
- @Override
- public Device getDeviceById(long id) {
- boolean forceUpdate;
- devicesLock.readLock().lock();
- try {
- forceUpdate = !devicesById.containsKey(id);
- } finally {
- devicesLock.readLock().unlock();
- }
-
- try {
- updateDeviceCache(forceUpdate);
- } catch (SQLException e) {
- Log.warning(e);
- }
-
- devicesLock.readLock().lock();
- try {
- return devicesById.get(id);
- } finally {
- devicesLock.readLock().unlock();
- }
- }
-
- @Override
- public Device getDeviceByUniqueId(String uniqueId) throws SQLException {
- boolean forceUpdate;
- devicesLock.readLock().lock();
- try {
- forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean("database.ignoreUnknown");
- } finally {
- devicesLock.readLock().unlock();
- }
-
- updateDeviceCache(forceUpdate);
-
- devicesLock.readLock().lock();
- try {
- return devicesByUniqueId.get(uniqueId);
- } finally {
- devicesLock.readLock().unlock();
- }
- }
-
private void updateGroupCache(boolean force) throws SQLException {
boolean needWrite;
groupsLock.readLock().lock();
@@ -347,72 +263,33 @@ public class DataManager implements IdentityManager {
.executeQuery(GroupPermission.class);
}
- private Collection<Device> getAllDevices() throws SQLException {
+ public Collection<Device> getAllDevices() throws SQLException {
return QueryBuilder.create(dataSource, getQuery("database.selectDevicesAll"))
.executeQuery(Device.class);
}
- public Collection<Device> getAllDevicesCached() {
- boolean forceUpdate;
- devicesLock.readLock().lock();
- try {
- forceUpdate = devicesById.isEmpty();
- } finally {
- devicesLock.readLock().unlock();
- }
-
- try {
- updateDeviceCache(forceUpdate);
- } catch (SQLException e) {
- Log.warning(e);
- }
-
- devicesLock.readLock().lock();
- try {
- return devicesById.values();
- } finally {
- devicesLock.readLock().unlock();
- }
- }
-
- public Collection<Device> getDevices(long userId) throws SQLException {
- Collection<Device> devices = new ArrayList<>();
- for (long id : Context.getPermissionsManager().getDevicePermissions(userId)) {
- devices.add(getDeviceById(id));
- }
- return devices;
- }
-
public void addDevice(Device device) throws SQLException {
device.setId(QueryBuilder.create(dataSource, getQuery("database.insertDevice"), true)
.setObject(device)
.executeUpdate());
- updateDeviceCache(true);
}
public void updateDevice(Device device) throws SQLException {
QueryBuilder.create(dataSource, getQuery("database.updateDevice"))
.setObject(device)
.executeUpdate();
- updateDeviceCache(true);
}
public void updateDeviceStatus(Device device) throws SQLException {
QueryBuilder.create(dataSource, getQuery("database.updateDeviceStatus"))
.setObject(device)
.executeUpdate();
- if (devicesById.containsKey(device.getId())) {
- Device cachedDevice = devicesById.get(device.getId());
- cachedDevice.setStatus(device.getStatus());
- cachedDevice.setMotion(device.getMotion());
- }
}
public void removeDevice(long deviceId) throws SQLException {
QueryBuilder.create(dataSource, getQuery("database.deleteDevice"))
.setLong("id", deviceId)
.executeUpdate();
- updateDeviceCache(true);
}
public void linkDevice(long userId, long deviceId) throws SQLException {
@@ -514,10 +391,6 @@ public class DataManager implements IdentityManager {
.setDate("now", new Date())
.setObject(position)
.executeUpdate();
- if (devicesById.containsKey(position.getDeviceId())) {
- Device cachedDevice = devicesById.get(position.getDeviceId());
- cachedDevice.setPositionId(position.getId());
- }
}
public Collection<Position> getLatestPositions() throws SQLException {
diff --git a/src/org/traccar/database/DeviceManager.java b/src/org/traccar/database/DeviceManager.java
new file mode 100644
index 000000000..70df1cc1a
--- /dev/null
+++ b/src/org/traccar/database/DeviceManager.java
@@ -0,0 +1,261 @@
+package org.traccar.database;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import org.traccar.Config;
+import org.traccar.Context;
+import org.traccar.helper.Log;
+import org.traccar.model.Device;
+import org.traccar.model.Position;
+
+public class DeviceManager implements IdentityManager {
+
+ private final Config config;
+ private final DataManager dataManager;
+ private final long dataRefreshDelay;
+
+ private final ReadWriteLock devicesLock = new ReentrantReadWriteLock();
+ private final Map<Long, Device> devicesById = new HashMap<>();
+ private final Map<String, Device> devicesByUniqueId = new HashMap<>();
+ private long devicesLastUpdate;
+
+ private final Map<Long, Position> positions = new ConcurrentHashMap<>();
+
+ public DeviceManager(DataManager dataManager) {
+ this.dataManager = dataManager;
+ this.config = Context.getConfig();
+ dataRefreshDelay = config.getLong("database.refreshDelay", DataManager.DEFAULT_REFRESH_DELAY) * 1000;
+ if (dataManager != null) {
+ try {
+ for (Position position : dataManager.getLatestPositions()) {
+ positions.put(position.getDeviceId(), position);
+ }
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+ }
+ }
+
+ private void updateDeviceCache(boolean force) throws SQLException {
+ boolean needWrite;
+ devicesLock.readLock().lock();
+ try {
+ needWrite = force || System.currentTimeMillis() - devicesLastUpdate > dataRefreshDelay;
+ } finally {
+ devicesLock.readLock().unlock();
+ }
+
+ if (needWrite) {
+ devicesLock.writeLock().lock();
+ try {
+ if (force || System.currentTimeMillis() - devicesLastUpdate > dataRefreshDelay) {
+ devicesById.clear();
+ devicesByUniqueId.clear();
+ GeofenceManager geofenceManager = Context.getGeofenceManager();
+ for (Device device : dataManager.getAllDevices()) {
+ devicesById.put(device.getId(), device);
+ devicesByUniqueId.put(device.getUniqueId(), device);
+ if (geofenceManager != null) {
+ Position lastPosition = getLastPosition(device.getId());
+ if (lastPosition != null) {
+ device.setGeofenceIds(geofenceManager.getCurrentDeviceGeofences(lastPosition));
+ }
+ }
+ }
+ devicesLastUpdate = System.currentTimeMillis();
+ }
+ } finally {
+ devicesLock.writeLock().unlock();
+ }
+ }
+ }
+
+ @Override
+ public Device getDeviceById(long id) {
+ boolean forceUpdate;
+ devicesLock.readLock().lock();
+ try {
+ forceUpdate = !devicesById.containsKey(id);
+ } finally {
+ devicesLock.readLock().unlock();
+ }
+
+ try {
+ updateDeviceCache(forceUpdate);
+ } catch (SQLException e) {
+ Log.warning(e);
+ }
+
+ devicesLock.readLock().lock();
+ try {
+ return devicesById.get(id);
+ } finally {
+ devicesLock.readLock().unlock();
+ }
+ }
+
+ @Override
+ public Device getDeviceByUniqueId(String uniqueId) throws SQLException {
+ boolean forceUpdate;
+ devicesLock.readLock().lock();
+ try {
+ forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean("database.ignoreUnknown");
+ } finally {
+ devicesLock.readLock().unlock();
+ }
+
+ updateDeviceCache(forceUpdate);
+
+ devicesLock.readLock().lock();
+ try {
+ return devicesByUniqueId.get(uniqueId);
+ } finally {
+ devicesLock.readLock().unlock();
+ }
+ }
+
+ public Collection<Device> getAllDevices() {
+ boolean forceUpdate;
+ devicesLock.readLock().lock();
+ try {
+ forceUpdate = devicesById.isEmpty();
+ } finally {
+ devicesLock.readLock().unlock();
+ }
+
+ try {
+ updateDeviceCache(forceUpdate);
+ } catch (SQLException e) {
+ Log.warning(e);
+ }
+
+ devicesLock.readLock().lock();
+ try {
+ return devicesById.values();
+ } finally {
+ devicesLock.readLock().unlock();
+ }
+ }
+
+ public Collection<Device> getDevices(long userId) throws SQLException {
+ Collection<Device> devices = new ArrayList<>();
+ devicesLock.readLock().lock();
+ try {
+ for (long id : Context.getPermissionsManager().getDevicePermissions(userId)) {
+ devices.add(devicesById.get(id));
+ }
+ } finally {
+ devicesLock.readLock().unlock();
+ }
+ return devices;
+ }
+
+ public void addDevice(Device device) throws SQLException {
+ dataManager.addDevice(device);
+
+ devicesLock.writeLock().lock();
+ try {
+ devicesById.put(device.getId(), device);
+ devicesByUniqueId.put(device.getUniqueId(), device);
+ } finally {
+ devicesLock.writeLock().unlock();
+ }
+ }
+
+ public void updateDevice(Device device) throws SQLException {
+ dataManager.updateDevice(device);
+
+ devicesLock.writeLock().lock();
+ try {
+ devicesById.put(device.getId(), device);
+ devicesByUniqueId.put(device.getUniqueId(), device);
+ } finally {
+ devicesLock.writeLock().unlock();
+ }
+ }
+
+ public void updateDeviceStatus(Device device) throws SQLException {
+ dataManager.updateDeviceStatus(device);
+
+ devicesLock.writeLock().lock();
+ try {
+ if (devicesById.containsKey(device.getId())) {
+ Device cachedDevice = devicesById.get(device.getId());
+ cachedDevice.setStatus(device.getStatus());
+ cachedDevice.setMotion(device.getMotion());
+ }
+ } finally {
+ devicesLock.writeLock().unlock();
+ }
+ }
+
+ public void removeDevice(long deviceId) throws SQLException {
+ dataManager.removeDevice(deviceId);
+
+ devicesLock.writeLock().lock();
+ try {
+ if (devicesById.containsKey(deviceId)) {
+ String deviceUniqueId = devicesById.get(deviceId).getUniqueId();
+ devicesById.remove(deviceId);
+ devicesByUniqueId.remove(deviceUniqueId);
+ }
+ } finally {
+ devicesLock.writeLock().unlock();
+ }
+
+ positions.remove(deviceId);
+ }
+
+ public void updateLatestPosition(Position position) throws SQLException {
+
+ Position lastPosition = getLastPosition(position.getDeviceId());
+ if (lastPosition == null || position.getFixTime().compareTo(lastPosition.getFixTime()) > 0) {
+
+ dataManager.updateLatestPosition(position);
+
+ devicesLock.writeLock().lock();
+ try {
+ if (devicesById.containsKey(position.getDeviceId())) {
+ devicesById.get(position.getDeviceId()).setPositionId(position.getId());
+ }
+ } finally {
+ devicesLock.writeLock().unlock();
+ }
+
+ positions.put(position.getDeviceId(), position);
+
+ if (Context.getConnectionManager() != null) {
+ Context.getConnectionManager().updatePosition(position);
+ }
+ }
+ }
+
+ @Override
+ public Position getLastPosition(long deviceId) {
+ return positions.get(deviceId);
+ }
+
+ public Collection<Position> getInitialState(long userId) {
+
+ List<Position> result = new LinkedList<>();
+
+ if (Context.getPermissionsManager() != null) {
+ for (long deviceId : Context.getPermissionsManager().getDevicePermissions(userId)) {
+ if (positions.containsKey(deviceId)) {
+ result.add(positions.get(deviceId));
+ }
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/src/org/traccar/database/GeofenceManager.java b/src/org/traccar/database/GeofenceManager.java
index c35e19e9a..dc31172b9 100644
--- a/src/org/traccar/database/GeofenceManager.java
+++ b/src/org/traccar/database/GeofenceManager.java
@@ -173,7 +173,7 @@ public class GeofenceManager {
.add(deviceGeofence.getGeofenceId());
}
- for (Device device : dataManager.getAllDevicesCached()) {
+ for (Device device : Context.getDeviceManager().getAllDevices()) {
long groupId = device.getGroupId();
while (groupId != 0) {
getDeviceGeofences(deviceGeofencesWithGroups,
@@ -190,7 +190,7 @@ public class GeofenceManager {
} else {
deviceGeofenceIds.clear();
}
- Position lastPosition = Context.getConnectionManager().getLastPosition(device.getId());
+ Position lastPosition = Context.getIdentityManager().getLastPosition(device.getId());
if (lastPosition != null && deviceGeofencesWithGroups.containsKey(device.getId())) {
for (long geofenceId : deviceGeofencesWithGroups.get(device.getId())) {
Geofence geofence = getGeofence(geofenceId);
diff --git a/src/org/traccar/database/IdentityManager.java b/src/org/traccar/database/IdentityManager.java
index 8bdce09a3..8c0de8b38 100644
--- a/src/org/traccar/database/IdentityManager.java
+++ b/src/org/traccar/database/IdentityManager.java
@@ -16,6 +16,7 @@
package org.traccar.database;
import org.traccar.model.Device;
+import org.traccar.model.Position;
public interface IdentityManager {
@@ -23,4 +24,6 @@ public interface IdentityManager {
Device getDeviceByUniqueId(String uniqueId) throws Exception;
+ Position getLastPosition(long deviceId);
+
}
diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java
index b6dd2e2a9..5a15375b4 100644
--- a/src/org/traccar/database/PermissionsManager.java
+++ b/src/org/traccar/database/PermissionsManager.java
@@ -78,7 +78,7 @@ public class PermissionsManager {
users.put(user.getId(), user);
}
- GroupTree groupTree = new GroupTree(dataManager.getAllGroups(), dataManager.getAllDevicesCached());
+ GroupTree groupTree = new GroupTree(dataManager.getAllGroups(), Context.getDeviceManager().getAllDevices());
for (GroupPermission permission : dataManager.getGroupPermissions()) {
Set<Long> userGroupPermissions = getGroupPermissions(permission.getUserId());
Set<Long> userDevicePermissions = getDevicePermissions(permission.getUserId());
diff --git a/src/org/traccar/database/QueryBuilder.java b/src/org/traccar/database/QueryBuilder.java
index 73569ac2a..1a83daab9 100644
--- a/src/org/traccar/database/QueryBuilder.java
+++ b/src/org/traccar/database/QueryBuilder.java
@@ -173,9 +173,17 @@ public final class QueryBuilder {
}
public QueryBuilder setLong(String name, long value) throws SQLException {
+ return setLong(name, value, false);
+ }
+
+ public QueryBuilder setLong(String name, long value, boolean nullIfZero) throws SQLException {
for (int i : indexes(name)) {
try {
- statement.setLong(i, value);
+ if (value == 0 && nullIfZero) {
+ statement.setNull(i, Types.INTEGER);
+ } else {
+ statement.setLong(i, value);
+ }
} catch (SQLException error) {
statement.close();
connection.close();
@@ -245,7 +253,7 @@ public final class QueryBuilder {
} else if (method.getReturnType().equals(int.class)) {
setInteger(name, (Integer) method.invoke(object));
} else if (method.getReturnType().equals(long.class)) {
- setLong(name, (Long) method.invoke(object));
+ setLong(name, (Long) method.invoke(object), name.endsWith("Id"));
} else if (method.getReturnType().equals(double.class)) {
setDouble(name, (Double) method.invoke(object));
} else if (method.getReturnType().equals(String.class)) {
diff --git a/src/org/traccar/events/GeofenceEventHandler.java b/src/org/traccar/events/GeofenceEventHandler.java
index 9a546182d..1ea7aee41 100644
--- a/src/org/traccar/events/GeofenceEventHandler.java
+++ b/src/org/traccar/events/GeofenceEventHandler.java
@@ -43,7 +43,7 @@ public class GeofenceEventHandler extends BaseEventHandler {
@Override
protected Collection<Event> analyzePosition(Position position) {
- Device device = dataManager.getDeviceById(position.getDeviceId());
+ Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId());
if (device == null) {
return null;
}
diff --git a/src/org/traccar/events/MotionEventHandler.java b/src/org/traccar/events/MotionEventHandler.java
index d10513d26..4a3d2f0f0 100644
--- a/src/org/traccar/events/MotionEventHandler.java
+++ b/src/org/traccar/events/MotionEventHandler.java
@@ -38,7 +38,7 @@ public class MotionEventHandler extends BaseEventHandler {
@Override
protected Collection<Event> analyzePosition(Position position) {
- Device device = Context.getDataManager().getDeviceById(position.getDeviceId());
+ Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId());
if (device == null) {
return null;
}
@@ -53,16 +53,19 @@ public class MotionEventHandler extends BaseEventHandler {
if (motion == null) {
motion = Device.STATUS_STOPPED;
}
- if (valid && speed > SPEED_THRESHOLD && !motion.equals(Device.STATUS_MOVING)) {
- Context.getConnectionManager().updateDevice(position.getDeviceId(), Device.STATUS_MOVING, null);
- result = new ArrayList<>();
- result.add(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);
- result = new ArrayList<>();
- result.add(new Event(Event.TYPE_DEVICE_STOPPED, position.getDeviceId(), position.getId()));
- }
try {
+ if (valid && speed > SPEED_THRESHOLD && !motion.equals(Device.STATUS_MOVING)) {
+ device.setMotion(Device.STATUS_MOVING);
+ Context.getDeviceManager().updateDeviceStatus(device);
+ result = new ArrayList<>();
+ result.add(new Event(Event.TYPE_DEVICE_MOVING, position.getDeviceId(), position.getId()));
+ } else if (valid && speed < SPEED_THRESHOLD && motion.equals(Device.STATUS_MOVING)) {
+ device.setMotion(Device.STATUS_STOPPED);
+ Context.getDeviceManager().updateDeviceStatus(device);
+ result = new ArrayList<>();
+ result.add(new Event(Event.TYPE_DEVICE_STOPPED, position.getDeviceId(), position.getId()));
+ }
+
if (result != null && !result.isEmpty()) {
for (Event event : result) {
if (!Context.getDataManager().getLastEvents(position.getDeviceId(),
diff --git a/src/org/traccar/events/OverspeedEventHandler.java b/src/org/traccar/events/OverspeedEventHandler.java
index e14d4bcea..fd005e170 100644
--- a/src/org/traccar/events/OverspeedEventHandler.java
+++ b/src/org/traccar/events/OverspeedEventHandler.java
@@ -40,7 +40,7 @@ public class OverspeedEventHandler extends BaseEventHandler {
@Override
protected Collection<Event> analyzePosition(Position position) {
- Device device = Context.getDataManager().getDeviceById(position.getDeviceId());
+ Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId());
if (device == null) {
return null;
}
diff --git a/src/org/traccar/model/Position.java b/src/org/traccar/model/Position.java
index 4e03b2097..e667f097d 100644
--- a/src/org/traccar/model/Position.java
+++ b/src/org/traccar/model/Position.java
@@ -66,6 +66,15 @@ public class Position extends Message {
public static final String PREFIX_IO = "io";
public static final String PREFIX_COUNT = "count";
+ public static final String ALARM_SOS = "sos";
+ public static final String ALARM_VIBRATION = "vibration";
+ public static final String ALARM_MOVEMENT = "movement";
+ public static final String ALARM_OVERSPEED = "overspeed";
+ public static final String ALARM_FALL_DOWN = "fallDown";
+ public static final String ALARM_LOW_BATTERY = "lowBattery";
+ public static final String ALARM_MOTION = "motion";
+ public static final String ALARM_FAULT = "fault";
+
private String protocol;
public String getProtocol() {
diff --git a/src/org/traccar/protocol/CarcellProtocol.java b/src/org/traccar/protocol/CarcellProtocol.java
new file mode 100644
index 000000000..5982e9cf8
--- /dev/null
+++ b/src/org/traccar/protocol/CarcellProtocol.java
@@ -0,0 +1,52 @@
+/*
+ * 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.protocol;
+
+import java.util.List;
+
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.handler.codec.string.StringDecoder;
+import org.jboss.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.TrackerServer;
+import org.traccar.model.Command;
+
+public class CarcellProtocol extends BaseProtocol {
+
+ public CarcellProtocol() {
+ super("carcell");
+ setSupportedCommands(
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_ENGINE_RESUME);
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '\r'));
+ pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("objectEncoder", new CarcellProtocolEncoder());
+ pipeline.addLast("objectDecoder", new CarcellProtocolDecoder(CarcellProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/CarcellProtocolDecoder.java b/src/org/traccar/protocol/CarcellProtocolDecoder.java
new file mode 100644
index 000000000..50b294f45
--- /dev/null
+++ b/src/org/traccar/protocol/CarcellProtocolDecoder.java
@@ -0,0 +1,166 @@
+/*
+ * 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.protocol;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.Parser.CoordinateFormat;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+public class CarcellProtocolDecoder extends BaseProtocolDecoder {
+
+ public CarcellProtocolDecoder(CarcellProtocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .expression("([$%])") // memory flag
+ .number("(d+),") // imei
+ .groupBegin()
+ .number("([NS])(dd)(dd).(dddd),") // latitude
+ .number("([EW])(ddd)(dd).(dddd),") // longitude
+ .or()
+ .text("CEL,")
+ .number("([NS])(d+.d+),") // latitude
+ .number("([EW])(d+.d+),") // longitude
+ .groupEnd()
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .groupBegin()
+ .number("([-+]ddd)([-+]ddd)([-+]ddd),") // x,y,z
+ .or()
+ .number("(d+),") // accel
+ .groupEnd()
+ .number("(d+),") // battery
+ .number("(d+),") // csq
+ .number("(d),") // jamming
+ .number("(d+),") // hdop
+ .expression("([CG]),?") // clock type
+ .number("(dd)(dd)(dd),") // date
+ .number("(dd)(dd)(dd),") // time
+ .number("(d),") // block
+ .number("(d),") // ignition
+ .groupBegin()
+ .number("(d),") // cloned
+ .expression("([AF])") // panic
+ .number("(d),") // painel
+ .number("(d+),") // battery voltage
+ .or()
+ .number("(dd),") // time
+ .expression("([AF])") // panic
+ .number("(d),") // aux
+ .number("(d{2,4}),") // battery voltage
+ .number("(d{20}),") // ccid
+ .groupEnd()
+ .number("(xx)") // crc
+ .any() // full format
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.set(Position.KEY_ARCHIVE, parser.next().equals("%"));
+ position.setValid(true);
+
+ if (!identify(parser.next(), channel, remoteAddress)) {
+ return null;
+ }
+
+ position.setDeviceId(getDeviceId());
+
+ if (parser.hasNext(8)) {
+ position.setLatitude(parser.nextCoordinate(CoordinateFormat.HEM_DEG_MIN_MIN));
+ position.setLongitude(parser.nextCoordinate(CoordinateFormat.HEM_DEG_MIN_MIN));
+ }
+
+ if (parser.hasNext(4)) {
+ position.setLatitude(parser.nextCoordinate(CoordinateFormat.HEM_DEG));
+ position.setLongitude(parser.nextCoordinate(CoordinateFormat.HEM_DEG));
+ }
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setCourse(parser.nextInt());
+
+ if (parser.hasNext(3)) {
+ position.set("x", parser.nextInt());
+ position.set("y", parser.nextInt());
+ position.set("z", parser.nextInt());
+ }
+
+ if (parser.hasNext(1)) {
+ position.set("accel", parser.nextInt());
+ }
+
+ Double internalBattery = (parser.nextDouble() + 100d) * 0.0294d;
+ position.set(Position.KEY_BATTERY, internalBattery);
+ position.set(Position.KEY_GSM, parser.nextInt());
+ position.set("jamming", parser.next().equals("1"));
+ position.set(Position.KEY_GPS, parser.nextInt());
+
+ parser.next(); // clock type
+
+ DateBuilder dateBuilder = new DateBuilder().
+ setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt())
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ position.set("blocked", parser.next().equals("1"));
+ position.set(Position.KEY_IGNITION, parser.next().equals("1"));
+
+ if (parser.hasNext(4)) {
+ position.set("cloned", parser.next().equals("1"));
+
+ parser.next(); // panic button status
+
+ String painelStatus = parser.next();
+ position.set(Position.KEY_ALARM, painelStatus.equals("1"));
+ position.set("painel", painelStatus.equals("2"));
+
+ Double mainVoltage = parser.nextDouble() / 100d;
+ position.set(Position.KEY_POWER, mainVoltage);
+ }
+
+ if (parser.hasNext(5)) {
+ position.set("timeUntilDelivery", parser.nextInt());
+ parser.next(); // panic button status
+ parser.next(); // aux
+
+ Double mainVoltage = parser.nextDouble() / 100d;
+ position.set(Position.KEY_POWER, mainVoltage);
+
+ position.set("iccid", parser.next());
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/CarcellProtocolEncoder.java b/src/org/traccar/protocol/CarcellProtocolEncoder.java
new file mode 100644
index 000000000..d01a11e52
--- /dev/null
+++ b/src/org/traccar/protocol/CarcellProtocolEncoder.java
@@ -0,0 +1,40 @@
+/*
+ * 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.protocol;
+
+import org.traccar.StringProtocolEncoder;
+import org.traccar.helper.Log;
+import org.traccar.model.Command;
+
+public class CarcellProtocolEncoder extends StringProtocolEncoder {
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_ENGINE_STOP:
+ return formatCommand(command, "$SRVCMD,{%s},BA#\r\n", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_ENGINE_RESUME:
+ return formatCommand(command, "$SRVCMD,{%s},BD#\r\n", Command.KEY_UNIQUE_ID);
+ default:
+ Log.warning(new UnsupportedOperationException(command.getType()));
+ break;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/org/traccar/protocol/Gt06ProtocolDecoder.java
index ce915fde8..57e2d64b7 100644
--- a/src/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -277,7 +277,19 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
if (type == MSG_INFO) {
int subType = buf.readUnsignedByte();
- if (subType == 0x05) {
+ if (subType == 0x00) {
+
+ Position position = new Position();
+ position.setDeviceId(getDeviceId());
+ position.setProtocol(getProtocolName());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+
+ return position;
+
+ } else if (subType == 0x05) {
Position position = new Position();
position.setDeviceId(getDeviceId());
diff --git a/src/org/traccar/protocol/MegastekProtocolDecoder.java b/src/org/traccar/protocol/MegastekProtocolDecoder.java
index 0c332d0da..ca554aa48 100644
--- a/src/org/traccar/protocol/MegastekProtocolDecoder.java
+++ b/src/org/traccar/protocol/MegastekProtocolDecoder.java
@@ -173,7 +173,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_CHARGE, Integer.parseInt(charger) == 1);
}
- if (parser.hasNext(3)) {
+ if (parser.hasNext(4)) {
position.set(Position.KEY_MCC, parser.nextInt());
position.set(Position.KEY_MNC, parser.nextInt());
position.set(Position.KEY_LAC, parser.nextInt(16));
diff --git a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java
index 089b491d2..96eaaf0fa 100644
--- a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java
+++ b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java
@@ -90,9 +90,33 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext(5)) {
int flags = parser.nextInt(16);
- position.set(Position.KEY_FLAGS, flags);
+
position.setValid(BitUtil.check(flags, 0));
+ if (BitUtil.check(flags, 2)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_FAULT);
+ }
+ if (BitUtil.check(flags, 6)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+ if (BitUtil.check(flags, 7)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ }
+ if (BitUtil.check(flags, 8)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_FALL_DOWN);
+ }
+ if (BitUtil.check(flags, 12)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ }
+ if (BitUtil.check(flags, 14)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_MOTION);
+ }
+ if (BitUtil.check(flags, 15)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_MOVEMENT);
+ }
+
+ position.set(Position.KEY_GSM, BitUtil.between(flags, 16, 20));
+
position.setAltitude(parser.nextDouble());
position.set(Position.KEY_BATTERY, parser.next());
diff --git a/src/org/traccar/protocol/SuntechProtocol.java b/src/org/traccar/protocol/SuntechProtocol.java
index ce0e73280..4a40e1bfc 100644
--- a/src/org/traccar/protocol/SuntechProtocol.java
+++ b/src/org/traccar/protocol/SuntechProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 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.
@@ -31,6 +31,9 @@ public class SuntechProtocol extends BaseProtocol {
public SuntechProtocol() {
super("suntech");
setSupportedCommands(
+ Command.TYPE_OUTPUT_CONTROL,
+ Command.TYPE_REBOOT_DEVICE,
+ Command.TYPE_POSITION_SINGLE,
Command.TYPE_ENGINE_STOP,
Command.TYPE_ENGINE_RESUME);
}
diff --git a/src/org/traccar/protocol/SuntechProtocolEncoder.java b/src/org/traccar/protocol/SuntechProtocolEncoder.java
index d988f97be..000583759 100644
--- a/src/org/traccar/protocol/SuntechProtocolEncoder.java
+++ b/src/org/traccar/protocol/SuntechProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 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.
@@ -25,6 +25,20 @@ public class SuntechProtocolEncoder extends StringProtocolEncoder {
protected Object encodeCommand(Command command) {
switch (command.getType()) {
+ case Command.TYPE_REBOOT_DEVICE:
+ return formatCommand(command, "SA200CMD;{%s};02;Reboot\r", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_POSITION_SINGLE:
+ return formatCommand(command, "SA200GTR;{%s};02;\r", Command.KEY_UNIQUE_ID);
+ case Command.TYPE_OUTPUT_CONTROL:
+ if (command.getAttributes().containsKey(Command.KEY_DATA)) {
+ if (command.getAttributes().get(Command.KEY_DATA).equals("1")) {
+ return formatCommand(command, "SA200CMD;{%s};02;Enable{%s}\r",
+ Command.KEY_UNIQUE_ID, Command.KEY_INDEX);
+ } else {
+ return formatCommand(command, "SA200CMD;{%s};02;Disable{%s}\r",
+ Command.KEY_UNIQUE_ID, Command.KEY_INDEX);
+ }
+ }
case Command.TYPE_ENGINE_STOP:
return formatCommand(command, "SA200CMD;{%s};02;Enable1\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_ENGINE_RESUME:
diff --git a/src/org/traccar/protocol/T800xProtocol.java b/src/org/traccar/protocol/T800xProtocol.java
index f98dfc943..05650124c 100644
--- a/src/org/traccar/protocol/T800xProtocol.java
+++ b/src/org/traccar/protocol/T800xProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 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.
@@ -20,6 +20,7 @@ import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.TrackerServer;
+import org.traccar.model.Command;
import java.util.List;
@@ -27,6 +28,8 @@ public class T800xProtocol extends BaseProtocol {
public T800xProtocol() {
super("t800x");
+ setSupportedCommands(
+ Command.TYPE_CUSTOM);
}
@Override
@@ -35,6 +38,7 @@ public class T800xProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 3, 2, -5, 0));
+ pipeline.addLast("objectEncoder", new T800xProtocolEncoder());
pipeline.addLast("objectDecoder", new T800xProtocolDecoder(T800xProtocol.this));
}
});
diff --git a/src/org/traccar/protocol/T800xProtocolDecoder.java b/src/org/traccar/protocol/T800xProtocolDecoder.java
index 67a4d55ca..dcf45bb06 100644
--- a/src/org/traccar/protocol/T800xProtocolDecoder.java
+++ b/src/org/traccar/protocol/T800xProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2015 - 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.
@@ -34,10 +34,11 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
- private static final int MSG_LOGIN = 0x01;
- private static final int MSG_GPS = 0x02;
- private static final int MSG_HEARTBEAT = 0x03;
- private static final int MSG_ALARM = 0x04;
+ public static final int MSG_LOGIN = 0x01;
+ public static final int MSG_GPS = 0x02;
+ public static final int MSG_HEARTBEAT = 0x03;
+ public static final int MSG_ALARM = 0x04;
+ public static final int MSG_COMMAND = 0x81;
private static float readSwappedFloat(ChannelBuffer buf) {
byte[] bytes = new byte[4];
diff --git a/src/org/traccar/protocol/T800xProtocolEncoder.java b/src/org/traccar/protocol/T800xProtocolEncoder.java
new file mode 100644
index 000000000..d603328e2
--- /dev/null
+++ b/src/org/traccar/protocol/T800xProtocolEncoder.java
@@ -0,0 +1,63 @@
+/*
+ * 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.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.traccar.BaseProtocolEncoder;
+import org.traccar.helper.Log;
+import org.traccar.model.Command;
+
+import javax.xml.bind.DatatypeConverter;
+import java.nio.charset.StandardCharsets;
+
+public class T800xProtocolEncoder extends BaseProtocolEncoder {
+
+ public static final int MODE_SETTING = 0x01;
+ public static final int MODE_BROADCAST = 0x02;
+ public static final int MODE_FORWARD = 0x03;
+
+ private ChannelBuffer encodeContent(Command command, String content) {
+
+ ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+
+ buf.writeByte('#');
+ buf.writeByte('#');
+ buf.writeByte(T800xProtocolDecoder.MSG_COMMAND);
+ buf.writeShort(7 + 8 + 1 + content.length());
+ buf.writeShort(1); // serial number
+ buf.writeBytes(DatatypeConverter.parseHexBinary("0" + getUniqueId(command.getDeviceId())));
+ buf.writeByte(MODE_SETTING);
+ buf.writeBytes(content.getBytes(StandardCharsets.US_ASCII));
+
+ return buf;
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return encodeContent(command, (String) command.getAttributes().get(Command.KEY_DATA));
+ default:
+ Log.warning(new UnsupportedOperationException(command.getType()));
+ break;
+ }
+
+ return null;
+ }
+
+}
diff --git a/test/org/traccar/ProtocolTest.java b/test/org/traccar/ProtocolTest.java
index 503bc4c00..07a19b691 100644
--- a/test/org/traccar/ProtocolTest.java
+++ b/test/org/traccar/ProtocolTest.java
@@ -44,6 +44,11 @@ public class ProtocolTest {
public Device getDeviceByUniqueId(String uniqueId) {
return createDevice();
}
+
+ @Override
+ public Position getLastPosition(long deviceId) {
+ return null;
+ }
});
}
diff --git a/test/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/test/org/traccar/protocol/Gt06ProtocolDecoderTest.java
index 94d665478..9201caac6 100644
--- a/test/org/traccar/protocol/Gt06ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Gt06ProtocolDecoderTest.java
@@ -13,6 +13,9 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest {
verifyNothing(decoder, binary(
"787805120099abec0d0a"));
+ verifyAttributes(decoder, binary(
+ "797900089400000002e852d70d0a"));
+
verifyNothing(decoder, binary(
"78780D01086471700328358100093F040D0A"));
diff --git a/test/org/traccar/protocol/T800xProtocolEncoderTest.java b/test/org/traccar/protocol/T800xProtocolEncoderTest.java
new file mode 100644
index 000000000..af3700225
--- /dev/null
+++ b/test/org/traccar/protocol/T800xProtocolEncoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class T800xProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ T800xProtocolEncoder encoder = new T800xProtocolEncoder();
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_CUSTOM);
+ command.set(Command.KEY_DATA, "RELAY,0000,On#");
+
+ verifyCommand(encoder, command, binary("232381001e000101234567890123450152454c41592c303030302c4f6e23"));
+
+ }
+
+}
diff --git a/tools/test-commands.py b/tools/test-commands.py
index 6c4896a46..040efb177 100755
--- a/tools/test-commands.py
+++ b/tools/test-commands.py
@@ -1,9 +1,11 @@
#!/usr/bin/python
import socket
+import binascii
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("localhost", 5001))
+#s.send(binascii.unhexlify('68680f0504035889905831401700df1a00000d0a'))
s.send("imei:123456789012345,tracker,151030080103,,F,000101.000,A,5443.3834,N,02512.9071,E,0.00,0;")
while True:
diff --git a/web/app/GeofenceConverter.js b/web/app/GeofenceConverter.js
index ebe121385..339f09615 100644
--- a/web/app/GeofenceConverter.js
+++ b/web/app/GeofenceConverter.js
@@ -20,7 +20,7 @@ Ext.define('Traccar.GeofenceConverter', {
wktToGeometry: function (mapView, wkt) {
var geometry, projection, resolutionAtEquator, pointResolution, resolutionFactor, points = [], center, radius,
content, i, lat, lon, coordinates;
- if (wkt.startsWith('POLYGON')) {
+ if (wkt.lastIndexOf('POLYGON', 0) === 0) {
content = wkt.match(/\([^\(\)]+\)/);
if (content !== null) {
coordinates = content[0].match(/-?\d+\.?\d*/g);
@@ -34,7 +34,7 @@ Ext.define('Traccar.GeofenceConverter', {
geometry = new ol.geom.Polygon([points]);
}
}
- } else if (wkt.startsWith('CIRCLE')) {
+ } else if (wkt.lastIndexOf('CIRCLE', 0) === 0) {
content = wkt.match(/\([^\(\)]+\)/);
if (content !== null) {
coordinates = content[0].match(/-?\d+\.?\d*/g);
diff --git a/web/app/view/GroupsController.js b/web/app/view/GroupsController.js
index 1764423d7..4cf1459a6 100644
--- a/web/app/view/GroupsController.js
+++ b/web/app/view/GroupsController.js
@@ -19,6 +19,7 @@ Ext.define('Traccar.view.GroupsController', {
alias: 'controller.groups',
requires: [
+ 'Traccar.view.GroupDialog',
'Traccar.view.GroupGeofences'
],