aboutsummaryrefslogtreecommitdiff
path: root/src/org/traccar
diff options
context:
space:
mode:
authorIvan Muratov <binakot@gmail.com>2017-10-24 14:52:48 +0300
committerGitHub <noreply@github.com>2017-10-24 14:52:48 +0300
commitdb02157dbb29539dda4b51a5e8b317293cfc536c (patch)
treeb974f082172406e16a92cb9da8136ef856f571a5 /src/org/traccar
parent09d3cf2b5416327700ad22b652cf4a0dca09aaf2 (diff)
parent96e15853b9c28bd31295ca2c014e226e4a50aaa1 (diff)
downloadtrackermap-server-db02157dbb29539dda4b51a5e8b317293cfc536c.tar.gz
trackermap-server-db02157dbb29539dda4b51a5e8b317293cfc536c.tar.bz2
trackermap-server-db02157dbb29539dda4b51a5e8b317293cfc536c.zip
Merge branch 'master' into master
Diffstat (limited to 'src/org/traccar')
-rw-r--r--src/org/traccar/BaseEventHandler.java8
-rw-r--r--src/org/traccar/BasePipelineFactory.java25
-rw-r--r--src/org/traccar/BaseProtocolDecoder.java29
-rw-r--r--src/org/traccar/BaseProtocolEncoder.java4
-rw-r--r--src/org/traccar/Config.java4
-rw-r--r--src/org/traccar/Context.java118
-rw-r--r--src/org/traccar/FilterHandler.java50
-rw-r--r--src/org/traccar/MainEventHandler.java2
-rw-r--r--src/org/traccar/WebDataHandler.java2
-rw-r--r--src/org/traccar/api/BaseObjectResource.java149
-rw-r--r--src/org/traccar/api/BaseResource.java2
-rw-r--r--src/org/traccar/api/ExtendedObjectResource.java62
-rw-r--r--src/org/traccar/api/SimpleObjectResource.java (renamed from src/org/traccar/api/resource/CommandTypeResource.java)36
-rw-r--r--src/org/traccar/api/resource/AttributeAliasResource.java93
-rw-r--r--src/org/traccar/api/resource/AttributePermissionResource.java58
-rw-r--r--src/org/traccar/api/resource/AttributeResource.java93
-rw-r--r--src/org/traccar/api/resource/CalendarPermissionResource.java57
-rw-r--r--src/org/traccar/api/resource/CalendarResource.java61
-rw-r--r--src/org/traccar/api/resource/CommandResource.java57
-rw-r--r--src/org/traccar/api/resource/DeviceAttributeResource.java58
-rw-r--r--src/org/traccar/api/resource/DeviceGeofenceResource.java57
-rw-r--r--src/org/traccar/api/resource/DevicePermissionResource.java66
-rw-r--r--src/org/traccar/api/resource/DeviceResource.java96
-rw-r--r--src/org/traccar/api/resource/DriverResource.java36
-rw-r--r--src/org/traccar/api/resource/EventResource.java5
-rw-r--r--src/org/traccar/api/resource/GeofencePermissionResource.java56
-rw-r--r--src/org/traccar/api/resource/GeofenceResource.java85
-rw-r--r--src/org/traccar/api/resource/GroupAttributeResource.java58
-rw-r--r--src/org/traccar/api/resource/GroupGeofenceResource.java56
-rw-r--r--src/org/traccar/api/resource/GroupPermissionResource.java62
-rw-r--r--src/org/traccar/api/resource/GroupResource.java70
-rw-r--r--src/org/traccar/api/resource/NotificationResource.java34
-rw-r--r--src/org/traccar/api/resource/PermissionsResource.java79
-rw-r--r--src/org/traccar/api/resource/PositionResource.java4
-rw-r--r--src/org/traccar/api/resource/SessionResource.java2
-rw-r--r--src/org/traccar/api/resource/UserPermissionResource.java56
-rw-r--r--src/org/traccar/api/resource/UserResource.java72
-rw-r--r--src/org/traccar/database/AliasesManager.java113
-rw-r--r--src/org/traccar/database/AttributesManager.java173
-rw-r--r--src/org/traccar/database/BaseObjectManager.java124
-rw-r--r--src/org/traccar/database/CalendarManager.java99
-rw-r--r--src/org/traccar/database/CommandsManager.java149
-rw-r--r--src/org/traccar/database/ConnectionManager.java45
-rw-r--r--src/org/traccar/database/DataManager.java624
-rw-r--r--src/org/traccar/database/DeviceManager.java435
-rw-r--r--src/org/traccar/database/DriversManager.java73
-rw-r--r--src/org/traccar/database/ExtendedObjectManager.java112
-rw-r--r--src/org/traccar/database/GeofenceManager.java288
-rw-r--r--src/org/traccar/database/GroupsManager.java103
-rw-r--r--src/org/traccar/database/IdentityManager.java4
-rw-r--r--src/org/traccar/database/ManagableObjects.java27
-rw-r--r--src/org/traccar/database/NotificationManager.java196
-rw-r--r--src/org/traccar/database/PermissionsManager.java373
-rw-r--r--src/org/traccar/database/QueryBuilder.java26
-rw-r--r--src/org/traccar/database/QueryExtended.java (renamed from src/org/traccar/model/UserPermission.java)30
-rw-r--r--src/org/traccar/database/SimpleObjectManager.java91
-rw-r--r--src/org/traccar/database/StatisticsManager.java2
-rw-r--r--src/org/traccar/database/UsersManager.java86
-rw-r--r--src/org/traccar/events/AlertEventHandler.java6
-rw-r--r--src/org/traccar/events/CommandResultEventHandler.java6
-rw-r--r--src/org/traccar/events/DriverEventHandler.java50
-rw-r--r--src/org/traccar/events/FuelDropEventHandler.java10
-rw-r--r--src/org/traccar/events/GeofenceEventHandler.java26
-rw-r--r--src/org/traccar/events/IgnitionEventHandler.java16
-rw-r--r--src/org/traccar/events/MaintenanceEventHandler.java8
-rw-r--r--src/org/traccar/events/MotionEventHandler.java109
-rw-r--r--src/org/traccar/events/OverspeedEventHandler.java93
-rw-r--r--src/org/traccar/helper/BitBuffer.java12
-rw-r--r--src/org/traccar/helper/PatternUtil.java7
-rw-r--r--src/org/traccar/model/Attribute.java12
-rw-r--r--src/org/traccar/model/AttributeAlias.java61
-rw-r--r--src/org/traccar/model/BaseModel.java (renamed from src/org/traccar/model/GroupAttribute.java)21
-rw-r--r--src/org/traccar/model/Calendar.java8
-rw-r--r--src/org/traccar/model/CalendarPermission.java40
-rw-r--r--src/org/traccar/model/CellTower.java6
-rw-r--r--src/org/traccar/model/Command.java25
-rw-r--r--src/org/traccar/model/Device.java9
-rw-r--r--src/org/traccar/model/DeviceGeofence.java40
-rw-r--r--src/org/traccar/model/DevicePermission.java40
-rw-r--r--src/org/traccar/model/DeviceState.java61
-rw-r--r--src/org/traccar/model/Driver.java (renamed from src/org/traccar/model/AttributePermission.java)23
-rw-r--r--src/org/traccar/model/Event.java14
-rw-r--r--src/org/traccar/model/ExtendedModel.java (renamed from src/org/traccar/model/Extensible.java)20
-rw-r--r--src/org/traccar/model/Geofence.java2
-rw-r--r--src/org/traccar/model/GeofencePermission.java40
-rw-r--r--src/org/traccar/model/Group.java2
-rw-r--r--src/org/traccar/model/GroupGeofence.java40
-rw-r--r--src/org/traccar/model/GroupPermission.java40
-rw-r--r--src/org/traccar/model/ManagedUser.java (renamed from src/org/traccar/model/DeviceAttribute.java)21
-rw-r--r--src/org/traccar/model/Message.java2
-rw-r--r--src/org/traccar/model/Notification.java12
-rw-r--r--src/org/traccar/model/Permission.java57
-rw-r--r--src/org/traccar/model/Position.java18
-rw-r--r--src/org/traccar/model/Server.java36
-rw-r--r--src/org/traccar/model/Statistics.java2
-rw-r--r--src/org/traccar/model/Typed.java (renamed from src/org/traccar/model/CommandType.java)4
-rw-r--r--src/org/traccar/model/User.java50
-rw-r--r--src/org/traccar/notification/EventForwarder.java4
-rw-r--r--src/org/traccar/notification/NotificationFormatter.java15
-rw-r--r--src/org/traccar/notification/NotificationMail.java19
-rw-r--r--src/org/traccar/notification/PropertiesProvider.java23
-rw-r--r--src/org/traccar/processing/ComputedAttributesHandler.java18
-rw-r--r--src/org/traccar/processing/CopyAttributesHandler.java9
-rw-r--r--src/org/traccar/protocol/AdmProtocol.java9
-rw-r--r--src/org/traccar/protocol/AdmProtocolDecoder.java107
-rw-r--r--src/org/traccar/protocol/AdmProtocolEncoder.java42
-rw-r--r--src/org/traccar/protocol/AplicomProtocolDecoder.java128
-rw-r--r--src/org/traccar/protocol/AquilaProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/AstraProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/AtrackProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/CarscopProtocolDecoder.java8
-rw-r--r--src/org/traccar/protocol/CastelProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/CityeasyProtocolEncoder.java8
-rw-r--r--src/org/traccar/protocol/CradlepointProtocolDecoder.java36
-rw-r--r--src/org/traccar/protocol/EasyTrackProtocolDecoder.java5
-rw-r--r--src/org/traccar/protocol/EelinkProtocolDecoder.java111
-rw-r--r--src/org/traccar/protocol/EskyFrameDecoder.java39
-rw-r--r--src/org/traccar/protocol/EskyProtocol.java46
-rw-r--r--src/org/traccar/protocol/EskyProtocolDecoder.java89
-rw-r--r--src/org/traccar/protocol/FifotrackProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/GenxProtocol.java45
-rw-r--r--src/org/traccar/protocol/GenxProtocolDecoder.java79
-rw-r--r--src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java406
-rw-r--r--src/org/traccar/protocol/Gl200FrameDecoder.java96
-rw-r--r--src/org/traccar/protocol/Gl200Protocol.java8
-rw-r--r--src/org/traccar/protocol/Gl200ProtocolDecoder.java850
-rw-r--r--src/org/traccar/protocol/Gl200TextProtocolDecoder.java917
-rw-r--r--src/org/traccar/protocol/GnxProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/Gps103ProtocolDecoder.java6
-rw-r--r--src/org/traccar/protocol/Gt06FrameDecoder.java20
-rw-r--r--src/org/traccar/protocol/Gt06ProtocolDecoder.java141
-rw-r--r--src/org/traccar/protocol/H02Protocol.java11
-rw-r--r--src/org/traccar/protocol/H02ProtocolDecoder.java120
-rw-r--r--src/org/traccar/protocol/Jt600ProtocolDecoder.java145
-rw-r--r--src/org/traccar/protocol/Jt600ProtocolEncoder.java6
-rw-r--r--src/org/traccar/protocol/MegastekProtocolDecoder.java10
-rw-r--r--src/org/traccar/protocol/MeiligaoProtocolDecoder.java30
-rw-r--r--src/org/traccar/protocol/MeiligaoProtocolEncoder.java5
-rw-r--r--src/org/traccar/protocol/MeitrackProtocolDecoder.java34
-rw-r--r--src/org/traccar/protocol/MiniFinderProtocolDecoder.java6
-rw-r--r--src/org/traccar/protocol/MiniFinderProtocolEncoder.java6
-rw-r--r--src/org/traccar/protocol/MxtProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/OsmAndProtocolDecoder.java19
-rw-r--r--src/org/traccar/protocol/Pt502ProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/Pt502ProtocolEncoder.java20
-rw-r--r--src/org/traccar/protocol/RuptelaProtocolDecoder.java45
-rw-r--r--src/org/traccar/protocol/StarLinkProtocolDecoder.java26
-rw-r--r--src/org/traccar/protocol/Stl060ProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/SuntechProtocolDecoder.java252
-rw-r--r--src/org/traccar/protocol/T55ProtocolDecoder.java8
-rw-r--r--src/org/traccar/protocol/TaipProtocolDecoder.java57
-rw-r--r--src/org/traccar/protocol/TelicProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/TeltonikaProtocol.java4
-rw-r--r--src/org/traccar/protocol/TeltonikaProtocolDecoder.java160
-rw-r--r--src/org/traccar/protocol/Tk103ProtocolDecoder.java77
-rw-r--r--src/org/traccar/protocol/TlvProtocol.java43
-rw-r--r--src/org/traccar/protocol/TlvProtocolDecoder.java109
-rw-r--r--src/org/traccar/protocol/TmgProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/TotemProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/TramigoProtocolDecoder.java7
-rw-r--r--src/org/traccar/protocol/TrvProtocolDecoder.java75
-rw-r--r--src/org/traccar/protocol/TytanProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/TzoneProtocolDecoder.java13
-rw-r--r--src/org/traccar/protocol/UlbotechProtocolDecoder.java3
-rw-r--r--src/org/traccar/protocol/VisiontekProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/Vt200ProtocolDecoder.java60
-rw-r--r--src/org/traccar/protocol/VtfmsProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/WatchProtocol.java1
-rw-r--r--src/org/traccar/protocol/WatchProtocolDecoder.java10
-rw-r--r--src/org/traccar/protocol/WatchProtocolEncoder.java10
-rw-r--r--src/org/traccar/protocol/WialonProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/XexunProtocolDecoder.java13
-rw-r--r--src/org/traccar/protocol/Xt2400ProtocolDecoder.java15
-rw-r--r--src/org/traccar/reports/Events.java12
-rw-r--r--src/org/traccar/reports/ReportUtils.java205
-rw-r--r--src/org/traccar/reports/Route.java6
-rw-r--r--src/org/traccar/reports/Stops.java27
-rw-r--r--src/org/traccar/reports/Summary.java4
-rw-r--r--src/org/traccar/reports/Trips.java20
-rw-r--r--src/org/traccar/reports/model/TripReport.java20
-rw-r--r--src/org/traccar/reports/model/TripsConfig.java48
-rw-r--r--src/org/traccar/web/WebServer.java32
182 files changed, 6244 insertions, 4936 deletions
diff --git a/src/org/traccar/BaseEventHandler.java b/src/org/traccar/BaseEventHandler.java
index 588406bf4..b6f7e2085 100644
--- a/src/org/traccar/BaseEventHandler.java
+++ b/src/org/traccar/BaseEventHandler.java
@@ -15,7 +15,7 @@
*/
package org.traccar;
-import java.util.Collection;
+import java.util.Map;
import org.traccar.model.Event;
import org.traccar.model.Position;
@@ -25,13 +25,13 @@ public abstract class BaseEventHandler extends BaseDataHandler {
@Override
protected Position handlePosition(Position position) {
- Collection<Event> events = analyzePosition(position);
+ Map<Event, Position> events = analyzePosition(position);
if (events != null && Context.getNotificationManager() != null) {
- Context.getNotificationManager().updateEvents(events, position);
+ Context.getNotificationManager().updateEvents(events);
}
return position;
}
- protected abstract Collection<Event> analyzePosition(Position position);
+ protected abstract Map<Event, Position> analyzePosition(Position position);
}
diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java
index a6446dbaa..771ab8acb 100644
--- a/src/org/traccar/BasePipelineFactory.java
+++ b/src/org/traccar/BasePipelineFactory.java
@@ -30,6 +30,7 @@ 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.DriverEventHandler;
import org.traccar.events.FuelDropEventHandler;
import org.traccar.events.GeofenceEventHandler;
import org.traccar.events.IgnitionEventHandler;
@@ -50,6 +51,7 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
private FilterHandler filterHandler;
private DistanceHandler distanceHandler;
+ private RemoteAddressHandler remoteAddressHandler;
private MotionHandler motionHandler;
private GeocoderHandler geocoderHandler;
private GeolocationHandler geolocationHandler;
@@ -65,6 +67,7 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
private AlertEventHandler alertEventHandler;
private IgnitionEventHandler ignitionEventHandler;
private MaintenanceEventHandler maintenanceEventHandler;
+ private DriverEventHandler driverEventHandler;
private static final class OpenChannelHandler extends SimpleChannelHandler {
@@ -125,10 +128,15 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
}
}
- distanceHandler = new DistanceHandler(Context.getConfig().getBoolean("coordinates.filter"),
+ distanceHandler = new DistanceHandler(
+ Context.getConfig().getBoolean("coordinates.filter"),
Context.getConfig().getInteger("coordinates.minError"),
Context.getConfig().getInteger("coordinates.maxError"));
+ if (Context.getConfig().getBoolean("processing.remoteAddress.enable")) {
+ remoteAddressHandler = new RemoteAddressHandler();
+ }
+
if (Context.getConfig().getBoolean("filter.enable")) {
filterHandler = new FilterHandler();
}
@@ -145,7 +153,7 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
Context.getConfig().getBoolean("geolocation.processInvalidPositions"));
}
- motionHandler = new MotionHandler(Context.getConfig().getDouble("event.motion.speedThreshold", 0.01));
+ motionHandler = new MotionHandler(Context.getTripsConfig().getSpeedThreshold());
if (Context.getConfig().hasKey("location.latitudeHemisphere")
|| Context.getConfig().hasKey("location.longitudeHemisphere")) {
@@ -162,13 +170,14 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
if (Context.getConfig().getBoolean("event.enable")) {
commandResultEventHandler = new CommandResultEventHandler();
- overspeedEventHandler = new OverspeedEventHandler();
+ overspeedEventHandler = Context.getOverspeedEventHandler();
fuelDropEventHandler = new FuelDropEventHandler();
- motionEventHandler = new MotionEventHandler();
+ motionEventHandler = Context.getMotionEventHandler();
geofenceEventHandler = new GeofenceEventHandler();
alertEventHandler = new AlertEventHandler();
ignitionEventHandler = new IgnitionEventHandler();
maintenanceEventHandler = new MaintenanceEventHandler();
+ driverEventHandler = new DriverEventHandler();
}
}
@@ -198,7 +207,9 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
pipeline.addLast("distance", distanceHandler);
}
- pipeline.addLast("remoteAddress", new RemoteAddressHandler());
+ if (remoteAddressHandler != null) {
+ pipeline.addLast("remoteAddress", remoteAddressHandler);
+ }
addDynamicHandlers(pipeline);
@@ -262,6 +273,10 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
pipeline.addLast("MaintenanceEventHandler", maintenanceEventHandler);
}
+ if (driverEventHandler != null) {
+ pipeline.addLast("DriverEventHandler", driverEventHandler);
+ }
+
pipeline.addLast("mainHandler", new MainEventHandler());
return pipeline;
}
diff --git a/src/org/traccar/BaseProtocolDecoder.java b/src/org/traccar/BaseProtocolDecoder.java
index 54d2bf28f..2d6286bf8 100644
--- a/src/org/traccar/BaseProtocolDecoder.java
+++ b/src/org/traccar/BaseProtocolDecoder.java
@@ -17,7 +17,9 @@ package org.traccar;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.socket.DatagramChannel;
+import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.traccar.helper.Log;
+import org.traccar.helper.UnitsConverter;
import org.traccar.model.Device;
import org.traccar.model.Position;
@@ -45,15 +47,13 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
try {
- Context.getDeviceManager().addDevice(device);
+ Context.getDeviceManager().addItem(device);
Log.info("Automatically registered device " + uniqueId);
if (defaultGroupId != 0) {
- Context.getPermissionsManager().refreshPermissions();
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refresh();
- }
+ Context.getPermissionsManager().refreshDeviceAndGroupPermissions();
+ Context.getPermissionsManager().refreshAllExtendedPermissions();
}
return device.getId();
@@ -67,6 +67,20 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
return protocol.getName();
}
+ protected double convertSpeed(double value, String defaultUnits) {
+ switch (Context.getConfig().getString(getProtocolName() + ".speed", defaultUnits)) {
+ case "kmh":
+ return UnitsConverter.knotsFromKph(value);
+ case "mps":
+ return UnitsConverter.knotsFromMps(value);
+ case "mph":
+ return UnitsConverter.knotsFromMph(value);
+ case "kn":
+ default:
+ return value;
+ }
+ }
+
private DeviceSession channelDeviceSession; // connection-based protocols
private Map<SocketAddress, DeviceSession> addressDeviceSessions = new HashMap<>(); // connectionless protocols
@@ -76,7 +90,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
try {
for (String uniqueId : uniqueIds) {
if (uniqueId != null) {
- Device device = Context.getIdentityManager().getDeviceByUniqueId(uniqueId);
+ Device device = Context.getIdentityManager().getByUniqueId(uniqueId);
if (device != null) {
deviceId = device.getId();
break;
@@ -105,7 +119,8 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
public DeviceSession getDeviceSession(Channel channel, SocketAddress remoteAddress, String... uniqueIds) {
- if (Context.getConfig().getBoolean("decoder.ignoreSessionCache")) {
+ if (channel != null && channel.getPipeline().get(HttpRequestDecoder.class) != null
+ || Context.getConfig().getBoolean("decoder.ignoreSessionCache")) {
long deviceId = findDeviceId(remoteAddress, uniqueIds);
if (deviceId != 0) {
if (Context.getConnectionManager() != null) {
diff --git a/src/org/traccar/BaseProtocolEncoder.java b/src/org/traccar/BaseProtocolEncoder.java
index 3c2d08471..2c8a81868 100644
--- a/src/org/traccar/BaseProtocolEncoder.java
+++ b/src/org/traccar/BaseProtocolEncoder.java
@@ -25,12 +25,12 @@ import org.traccar.model.Device;
public abstract class BaseProtocolEncoder extends OneToOneEncoder {
protected String getUniqueId(long deviceId) {
- return Context.getIdentityManager().getDeviceById(deviceId).getUniqueId();
+ return Context.getIdentityManager().getById(deviceId).getUniqueId();
}
protected void initDevicePassword(Command command, String defaultPassword) {
if (!command.getAttributes().containsKey(Command.KEY_DEVICE_PASSWORD)) {
- Device device = Context.getIdentityManager().getDeviceById(command.getDeviceId());
+ Device device = Context.getIdentityManager().getById(command.getDeviceId());
String password = device.getString(Command.KEY_DEVICE_PASSWORD);
if (password != null) {
command.set(Command.KEY_DEVICE_PASSWORD, password);
diff --git a/src/org/traccar/Config.java b/src/org/traccar/Config.java
index 0bc3cafaa..43f4632da 100644
--- a/src/org/traccar/Config.java
+++ b/src/org/traccar/Config.java
@@ -96,4 +96,8 @@ public class Config {
return key.replaceAll("\\.", "_").replaceAll("(\\p{Lu})", "_$1").toUpperCase();
}
+ public void setString(String key, String value) {
+ properties.put(key, value);
+ }
+
}
diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java
index 9626d949c..3b24c6460 100644
--- a/src/org/traccar/Context.java
+++ b/src/org/traccar/Context.java
@@ -25,18 +25,24 @@ import java.util.Properties;
import org.apache.velocity.app.VelocityEngine;
import org.eclipse.jetty.util.URIUtil;
-import org.traccar.database.AliasesManager;
import org.traccar.database.CalendarManager;
+import org.traccar.database.CommandsManager;
import org.traccar.database.AttributesManager;
+import org.traccar.database.BaseObjectManager;
import org.traccar.database.ConnectionManager;
import org.traccar.database.DataManager;
import org.traccar.database.DeviceManager;
+import org.traccar.database.DriversManager;
import org.traccar.database.IdentityManager;
import org.traccar.database.MediaManager;
import org.traccar.database.NotificationManager;
import org.traccar.database.PermissionsManager;
import org.traccar.database.GeofenceManager;
+import org.traccar.database.GroupsManager;
import org.traccar.database.StatisticsManager;
+import org.traccar.database.UsersManager;
+import org.traccar.events.MotionEventHandler;
+import org.traccar.events.OverspeedEventHandler;
import org.traccar.geocoder.BingMapsGeocoder;
import org.traccar.geocoder.FactualGeocoder;
import org.traccar.geocoder.GeocodeFarmGeocoder;
@@ -48,11 +54,22 @@ import org.traccar.geocoder.OpenCageGeocoder;
import org.traccar.geocoder.Geocoder;
import org.traccar.geolocation.UnwiredGeolocationProvider;
import org.traccar.helper.Log;
+import org.traccar.model.Attribute;
+import org.traccar.model.BaseModel;
+import org.traccar.model.Calendar;
+import org.traccar.model.Command;
+import org.traccar.model.Device;
+import org.traccar.model.Driver;
+import org.traccar.model.Geofence;
+import org.traccar.model.Group;
+import org.traccar.model.Notification;
+import org.traccar.model.User;
import org.traccar.geolocation.GoogleGeolocationProvider;
import org.traccar.geolocation.GeolocationProvider;
import org.traccar.geolocation.MozillaGeolocationProvider;
import org.traccar.geolocation.OpenCellIdGeolocationProvider;
import org.traccar.notification.EventForwarder;
+import org.traccar.reports.model.TripsConfig;
import org.traccar.smpp.SmppClient;
import org.traccar.web.WebServer;
@@ -97,6 +114,18 @@ public final class Context {
return mediaManager;
}
+ private static UsersManager usersManager;
+
+ public static UsersManager getUsersManager() {
+ return usersManager;
+ }
+
+ private static GroupsManager groupsManager;
+
+ public static GroupsManager getGroupsManager() {
+ return groupsManager;
+ }
+
private static DeviceManager deviceManager;
public static DeviceManager getDeviceManager() {
@@ -175,18 +204,24 @@ public final class Context {
return eventForwarder;
}
- private static AliasesManager aliasesManager;
-
- public static AliasesManager getAliasesManager() {
- return aliasesManager;
- }
-
private static AttributesManager attributesManager;
public static AttributesManager getAttributesManager() {
return attributesManager;
}
+ private static DriversManager driversManager;
+
+ public static DriversManager getDriversManager() {
+ return driversManager;
+ }
+
+ private static CommandsManager commandsManager;
+
+ public static CommandsManager getCommandsManager() {
+ return commandsManager;
+ }
+
private static StatisticsManager statisticsManager;
public static StatisticsManager getStatisticsManager() {
@@ -199,6 +234,35 @@ public final class Context {
return smppClient;
}
+ private static MotionEventHandler motionEventHandler;
+
+ public static MotionEventHandler getMotionEventHandler() {
+ return motionEventHandler;
+ }
+
+ private static OverspeedEventHandler overspeedEventHandler;
+
+ public static OverspeedEventHandler getOverspeedEventHandler() {
+ return overspeedEventHandler;
+ }
+
+ private static TripsConfig tripsConfig;
+
+ public static TripsConfig getTripsConfig() {
+ return tripsConfig;
+ }
+
+ public static TripsConfig initTripsConfig() {
+ return new TripsConfig(
+ config.getLong("report.trip.minimalTripDistance", 500),
+ config.getLong("report.trip.minimalTripDuration", 300) * 1000,
+ config.getLong("report.trip.minimalParkingDuration", 300) * 1000,
+ config.getLong("report.trip.minimalNoDataDuration", 3600) * 1000,
+ config.getBoolean("report.trip.useIgnition"),
+ config.getBoolean("event.motion.processInvalidPositions"),
+ config.getDouble("event.motion.speedThreshold", 0.01));
+ }
+
public static void init(String[] arguments) throws Exception {
config = new Config();
@@ -226,6 +290,8 @@ public final class Context {
}
if (dataManager != null) {
+ usersManager = new UsersManager(dataManager);
+ groupsManager = new GroupsManager(dataManager);
deviceManager = new DeviceManager(dataManager);
}
@@ -291,10 +357,12 @@ public final class Context {
webServer = new WebServer(config, dataManager.getDataSource());
}
- permissionsManager = new PermissionsManager(dataManager);
+ permissionsManager = new PermissionsManager(dataManager, usersManager);
connectionManager = new ConnectionManager();
+ tripsConfig = initTripsConfig();
+
if (config.getBoolean("event.enable")) {
geofenceManager = new GeofenceManager(dataManager);
calendarManager = new CalendarManager(dataManager);
@@ -318,6 +386,11 @@ public final class Context {
velocityEngine = new VelocityEngine();
velocityEngine.init(velocityProperties);
+
+ motionEventHandler = new MotionEventHandler(tripsConfig);
+ overspeedEventHandler = new OverspeedEventHandler(
+ Context.getConfig().getLong("event.overspeed.minimalDuration") * 1000,
+ Context.getConfig().getBoolean("event.overspeed.notRepeat"));
}
serverManager = new ServerManager();
@@ -326,10 +399,12 @@ public final class Context {
eventForwarder = new EventForwarder();
}
- aliasesManager = new AliasesManager(dataManager);
-
attributesManager = new AttributesManager(dataManager);
+ driversManager = new DriversManager(dataManager);
+
+ commandsManager = new CommandsManager(dataManager);
+
statisticsManager = new StatisticsManager();
if (config.getBoolean("sms.smpp.enable")) {
@@ -344,4 +419,27 @@ public final class Context {
identityManager = testIdentityManager;
}
+ public static <T extends BaseModel> BaseObjectManager<T> getManager(Class<T> clazz) {
+ if (clazz.equals(Device.class)) {
+ return (BaseObjectManager<T>) deviceManager;
+ } else if (clazz.equals(Group.class)) {
+ return (BaseObjectManager<T>) groupsManager;
+ } else if (clazz.equals(User.class)) {
+ return (BaseObjectManager<T>) usersManager;
+ } else if (clazz.equals(Calendar.class)) {
+ return (BaseObjectManager<T>) calendarManager;
+ } else if (clazz.equals(Attribute.class)) {
+ return (BaseObjectManager<T>) attributesManager;
+ } else if (clazz.equals(Geofence.class)) {
+ return (BaseObjectManager<T>) geofenceManager;
+ } else if (clazz.equals(Driver.class)) {
+ return (BaseObjectManager<T>) driversManager;
+ } else if (clazz.equals(Command.class)) {
+ return (BaseObjectManager<T>) commandsManager;
+ } else if (clazz.equals(Notification.class)) {
+ return (BaseObjectManager<T>) notificationManager;
+ }
+ return null;
+ }
+
}
diff --git a/src/org/traccar/FilterHandler.java b/src/org/traccar/FilterHandler.java
index 9e532347d..4cd3eb0eb 100644
--- a/src/org/traccar/FilterHandler.java
+++ b/src/org/traccar/FilterHandler.java
@@ -29,7 +29,8 @@ public class FilterHandler extends BaseDataHandler {
private boolean filterStatic;
private int filterDistance;
private int filterMaxSpeed;
- private long filterLimit;
+ private long skipLimit;
+ private boolean skipAttributes;
public void setFilterInvalid(boolean filterInvalid) {
this.filterInvalid = filterInvalid;
@@ -63,8 +64,12 @@ public class FilterHandler extends BaseDataHandler {
this.filterMaxSpeed = filterMaxSpeed;
}
- public void setFilterLimit(long filterLimit) {
- this.filterLimit = filterLimit;
+ public void setSkipLimit(long skipLimit) {
+ this.skipLimit = skipLimit;
+ }
+
+ public void setSkipAttributes(boolean skipAttributes) {
+ this.skipAttributes = skipAttributes;
}
public FilterHandler() {
@@ -78,7 +83,8 @@ public class FilterHandler extends BaseDataHandler {
filterStatic = config.getBoolean("filter.static");
filterDistance = config.getInteger("filter.distance");
filterMaxSpeed = config.getInteger("filter.maxSpeed");
- filterLimit = config.getLong("filter.limit") * 1000;
+ skipLimit = config.getLong("filter.skipLimit") * 1000;
+ skipAttributes = config.getBoolean("filter.skipAttributes.enable");
}
}
@@ -126,22 +132,30 @@ public class FilterHandler extends BaseDataHandler {
private boolean filterMaxSpeed(Position position, Position last) {
if (filterMaxSpeed != 0 && last != null) {
double distance = position.getDouble(Position.KEY_DISTANCE);
- long time = position.getFixTime().getTime() - last.getFixTime().getTime();
- return UnitsConverter.knotsFromMps(distance / time) > filterMaxSpeed;
+ double time = position.getFixTime().getTime() - last.getFixTime().getTime();
+ return UnitsConverter.knotsFromMps(distance / (time / 1000)) > filterMaxSpeed;
+ }
+ return false;
+ }
+
+ private boolean skipLimit(Position position, Position last) {
+ if (skipLimit != 0 && last != null) {
+ return (position.getFixTime().getTime() - last.getFixTime().getTime()) > skipLimit;
}
return false;
}
- private boolean filterLimit(Position position, Position last) {
- if (filterLimit != 0) {
- if (last != null) {
- return (position.getFixTime().getTime() - last.getFixTime().getTime()) > filterLimit;
- } else {
- return false;
+ private boolean skipAttributes(Position position) {
+ if (skipAttributes) {
+ String attributesString = Context.getIdentityManager().lookupAttributeString(
+ position.getDeviceId(), "filter.skipAttributes", "", true);
+ for (String attribute : attributesString.split("[ ,]")) {
+ if (position.getAttributes().containsKey(attribute)) {
+ return true;
+ }
}
- } else {
- return false;
}
+ return false;
}
private boolean filter(Position position) {
@@ -153,6 +167,10 @@ public class FilterHandler extends BaseDataHandler {
last = Context.getIdentityManager().getLastPosition(position.getDeviceId());
}
+ if (skipLimit(position, last) || skipAttributes(position)) {
+ return false;
+ }
+
if (filterInvalid(position)) {
filterType.append("Invalid ");
}
@@ -178,13 +196,13 @@ public class FilterHandler extends BaseDataHandler {
filterType.append("MaxSpeed ");
}
- if (filterType.length() > 0 && !filterLimit(position, last)) {
+ if (filterType.length() > 0) {
StringBuilder message = new StringBuilder();
message.append("Position filtered by ");
message.append(filterType.toString());
message.append("filters from device: ");
- message.append(Context.getIdentityManager().getDeviceById(position.getDeviceId()).getUniqueId());
+ message.append(Context.getIdentityManager().getById(position.getDeviceId()).getUniqueId());
message.append(" with id: ");
message.append(position.getDeviceId());
diff --git a/src/org/traccar/MainEventHandler.java b/src/org/traccar/MainEventHandler.java
index a005ee44b..8e88e15b9 100644
--- a/src/org/traccar/MainEventHandler.java
+++ b/src/org/traccar/MainEventHandler.java
@@ -55,7 +55,7 @@ public class MainEventHandler extends IdleStateAwareChannelHandler {
Log.warning(error);
}
- String uniqueId = Context.getIdentityManager().getDeviceById(position.getDeviceId()).getUniqueId();
+ String uniqueId = Context.getIdentityManager().getById(position.getDeviceId()).getUniqueId();
// Log position
StringBuilder s = new StringBuilder();
diff --git a/src/org/traccar/WebDataHandler.java b/src/org/traccar/WebDataHandler.java
index eaf0978ef..c64dcc81b 100644
--- a/src/org/traccar/WebDataHandler.java
+++ b/src/org/traccar/WebDataHandler.java
@@ -75,7 +75,7 @@ public class WebDataHandler extends BaseDataHandler {
public String formatRequest(Position position) {
- Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId());
+ Device device = Context.getIdentityManager().getById(position.getDeviceId());
String request = url
.replace("{name}", device.getName())
diff --git a/src/org/traccar/api/BaseObjectResource.java b/src/org/traccar/api/BaseObjectResource.java
new file mode 100644
index 000000000..634957a49
--- /dev/null
+++ b/src/org/traccar/api/BaseObjectResource.java
@@ -0,0 +1,149 @@
+/*
+ * 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.api;
+
+import java.sql.SQLException;
+import java.util.Set;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Response;
+
+import org.traccar.Context;
+import org.traccar.database.BaseObjectManager;
+import org.traccar.database.ExtendedObjectManager;
+import org.traccar.database.ManagableObjects;
+import org.traccar.database.SimpleObjectManager;
+import org.traccar.model.BaseModel;
+import org.traccar.model.Command;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.model.User;
+
+public abstract class BaseObjectResource<T extends BaseModel> extends BaseResource {
+
+ private Class<T> baseClass;
+
+ public BaseObjectResource(Class<T> baseClass) {
+ this.baseClass = baseClass;
+ }
+
+ protected final Class<T> getBaseClass() {
+ return baseClass;
+ }
+
+ protected final Set<Long> getSimpleManagerItems(BaseObjectManager<T> manager, boolean all, long userId) {
+ Set<Long> result = null;
+ if (all) {
+ if (Context.getPermissionsManager().getUserAdmin(getUserId())) {
+ result = manager.getAllItems();
+ } else {
+ Context.getPermissionsManager().checkManager(getUserId());
+ result = ((ManagableObjects) manager).getManagedItems(getUserId());
+ }
+ } else {
+ if (userId == 0) {
+ userId = getUserId();
+ }
+ Context.getPermissionsManager().checkUser(getUserId(), userId);
+ result = ((ManagableObjects) manager).getUserItems(userId);
+ }
+ return result;
+ }
+
+ @POST
+ public Response add(T entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ if (baseClass.equals(Device.class)) {
+ Context.getPermissionsManager().checkDeviceReadonly(getUserId());
+ Context.getPermissionsManager().checkDeviceLimit(getUserId());
+ } else if (baseClass.equals(Command.class)) {
+ Context.getPermissionsManager().checkLimitCommands(getUserId());
+ }
+
+ BaseObjectManager<T> manager = Context.getManager(baseClass);
+ manager.addItem(entity);
+
+ Context.getDataManager().linkObject(User.class, getUserId(), baseClass, entity.getId(), true);
+
+ if (manager instanceof SimpleObjectManager) {
+ ((SimpleObjectManager<T>) manager).refreshUserItems();
+ } else if (baseClass.equals(Group.class) || baseClass.equals(Device.class)) {
+ Context.getPermissionsManager().refreshDeviceAndGroupPermissions();
+ Context.getPermissionsManager().refreshAllExtendedPermissions();
+ }
+ return Response.ok(entity).build();
+ }
+
+ @Path("{id}")
+ @PUT
+ public Response update(T entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ if (baseClass.equals(Device.class)) {
+ Context.getPermissionsManager().checkDeviceReadonly(getUserId());
+ } else if (baseClass.equals(User.class)) {
+ User before = Context.getPermissionsManager().getUser(entity.getId());
+ Context.getPermissionsManager().checkUserUpdate(getUserId(), before, (User) entity);
+ } else if (baseClass.equals(Command.class)) {
+ Context.getPermissionsManager().checkLimitCommands(getUserId());
+ }
+ Context.getPermissionsManager().checkPermission(baseClass, getUserId(), entity.getId());
+
+ Context.getManager(baseClass).updateItem(entity);
+
+ if (baseClass.equals(Group.class) || baseClass.equals(Device.class)) {
+ Context.getPermissionsManager().refreshDeviceAndGroupPermissions();
+ Context.getPermissionsManager().refreshAllExtendedPermissions();
+ }
+ return Response.ok(entity).build();
+ }
+
+ @Path("{id}")
+ @DELETE
+ public Response remove(@PathParam("id") long id) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ if (baseClass.equals(Device.class)) {
+ Context.getPermissionsManager().checkDeviceReadonly(getUserId());
+ } else if (baseClass.equals(Command.class)) {
+ Context.getPermissionsManager().checkLimitCommands(getUserId());
+ }
+ Context.getPermissionsManager().checkPermission(baseClass, getUserId(), id);
+
+ BaseObjectManager<T> manager = Context.getManager(baseClass);
+ manager.removeItem(id);
+
+ if (manager instanceof SimpleObjectManager) {
+ ((SimpleObjectManager<T>) manager).refreshUserItems();
+ if (manager instanceof ExtendedObjectManager) {
+ ((ExtendedObjectManager<T>) manager).refreshExtendedPermissions();
+ }
+ }
+ if (baseClass.equals(Group.class) || baseClass.equals(Device.class) || baseClass.equals(User.class)) {
+ Context.getPermissionsManager().refreshDeviceAndGroupPermissions();
+ if (baseClass.equals(User.class)) {
+ Context.getPermissionsManager().refreshAllUsersPermissions();
+ } else {
+ Context.getPermissionsManager().refreshAllExtendedPermissions();
+ }
+ }
+ return Response.noContent().build();
+ }
+
+}
diff --git a/src/org/traccar/api/BaseResource.java b/src/org/traccar/api/BaseResource.java
index 44ef33c53..cc272df9c 100644
--- a/src/org/traccar/api/BaseResource.java
+++ b/src/org/traccar/api/BaseResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/org/traccar/api/ExtendedObjectResource.java b/src/org/traccar/api/ExtendedObjectResource.java
new file mode 100644
index 000000000..007a7b1bd
--- /dev/null
+++ b/src/org/traccar/api/ExtendedObjectResource.java
@@ -0,0 +1,62 @@
+/*
+ * 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.api;
+
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.QueryParam;
+
+import org.traccar.Context;
+import org.traccar.database.ExtendedObjectManager;
+import org.traccar.model.BaseModel;
+
+public class ExtendedObjectResource<T extends BaseModel> extends BaseObjectResource<T> {
+
+ public ExtendedObjectResource(Class<T> baseClass) {
+ super(baseClass);
+ }
+
+ @GET
+ public Collection<T> get(
+ @QueryParam("all") boolean all, @QueryParam("userId") long userId, @QueryParam("groupId") long groupId,
+ @QueryParam("deviceId") long deviceId, @QueryParam("refresh") boolean refresh) throws SQLException {
+
+ ExtendedObjectManager<T> manager = (ExtendedObjectManager<T>) Context.getManager(getBaseClass());
+ if (refresh) {
+ manager.refreshItems();
+ }
+
+ Set<Long> result = new HashSet<>(getSimpleManagerItems(manager, all, userId));
+
+ if (groupId != 0) {
+ Context.getPermissionsManager().checkGroup(getUserId(), groupId);
+ result.retainAll(manager.getGroupItems(groupId));
+ }
+
+ if (deviceId != 0) {
+ Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
+ result.retainAll(manager.getDeviceItems(deviceId));
+ }
+ return manager.getItems(result);
+
+ }
+
+}
diff --git a/src/org/traccar/api/resource/CommandTypeResource.java b/src/org/traccar/api/SimpleObjectResource.java
index d5d220547..a7fcae0e7 100644
--- a/src/org/traccar/api/resource/CommandTypeResource.java
+++ b/src/org/traccar/api/SimpleObjectResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Gabor Somogyi (gabor.g.somogyi@gmail.com)
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,30 +14,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.api.resource;
+package org.traccar.api;
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.CommandType;
+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.Produces;
import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.MediaType;
-import java.util.Collection;
-@Path("commandtypes")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class CommandTypeResource extends BaseResource {
+import org.traccar.Context;
+import org.traccar.database.BaseObjectManager;
+import org.traccar.model.BaseModel;
+
+public class SimpleObjectResource<T extends BaseModel> extends BaseObjectResource<T> {
+
+ public SimpleObjectResource(Class<T> baseClass) {
+ super(baseClass);
+ }
@GET
- public Collection<CommandType> get(@QueryParam("deviceId") long deviceId,
- @QueryParam("textChannel") boolean textChannel) {
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- return Context.getDeviceManager().getCommandTypes(deviceId, textChannel);
+ public Collection<T> get(
+ @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException {
+
+ BaseObjectManager<T> manager = Context.getManager(getBaseClass());
+ return manager.getItems(getSimpleManagerItems(manager, all, userId));
}
}
diff --git a/src/org/traccar/api/resource/AttributeAliasResource.java b/src/org/traccar/api/resource/AttributeAliasResource.java
deleted file mode 100644
index b2636acf1..000000000
--- a/src/org/traccar/api/resource/AttributeAliasResource.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.api.resource;
-
-import java.sql.SQLException;
-import java.util.Collection;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-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 javax.ws.rs.core.Response;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.AttributeAlias;
-
-@Path("attributes/aliases")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class AttributeAliasResource extends BaseResource {
-
- @GET
- public Collection<AttributeAlias> get(@QueryParam("deviceId") long deviceId) throws SQLException {
- if (deviceId != 0) {
- if (!Context.getPermissionsManager().isAdmin(getUserId())) {
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- }
- return Context.getAliasesManager().getAttributeAliases(deviceId);
- } else {
- return Context.getAliasesManager().getAllAttributeAliases(getUserId());
- }
- }
-
- @POST
- public Response add(AttributeAlias entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkDeviceReadonly(getUserId());
- if (!Context.getPermissionsManager().isAdmin(getUserId())) {
- Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
- }
- Context.getAliasesManager().addAttributeAlias(entity);
- return Response.ok(entity).build();
- }
-
- @Path("{id}")
- @PUT
- public Response update(AttributeAlias entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkDeviceReadonly(getUserId());
- if (!Context.getPermissionsManager().isAdmin(getUserId())) {
- AttributeAlias oldEntity = Context.getAliasesManager().getAttributeAlias(entity.getId());
- Context.getPermissionsManager().checkDevice(getUserId(), oldEntity.getDeviceId());
- Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
- }
- Context.getAliasesManager().updateAttributeAlias(entity);
- return Response.ok(entity).build();
- }
-
- @Path("{id}")
- @DELETE
- public Response remove(@PathParam("id") long id) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkDeviceReadonly(getUserId());
- if (!Context.getPermissionsManager().isAdmin(getUserId())) {
- AttributeAlias entity = Context.getAliasesManager().getAttributeAlias(id);
- Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
- }
- Context.getAliasesManager().removeArrtibuteAlias(id);
- return Response.noContent().build();
- }
-
-}
diff --git a/src/org/traccar/api/resource/AttributePermissionResource.java b/src/org/traccar/api/resource/AttributePermissionResource.java
deleted file mode 100644
index 1924bcdf1..000000000
--- a/src/org/traccar/api/resource/AttributePermissionResource.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.api.resource;
-
-import java.sql.SQLException;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.AttributePermission;
-
-@Path("permissions/attributes")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class AttributePermissionResource extends BaseResource {
-
- @POST
- public Response add(AttributePermission entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
- Context.getPermissionsManager().checkAttribute(getUserId(), entity.getAttributeId());
- Context.getDataManager().linkAttribute(entity.getUserId(), entity.getAttributeId());
- Context.getAttributesManager().refreshUserAttributes();
- return Response.ok(entity).build();
- }
-
- @DELETE
- public Response remove(AttributePermission entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
- Context.getPermissionsManager().checkAttribute(getUserId(), entity.getAttributeId());
- Context.getDataManager().unlinkAttribute(entity.getUserId(), entity.getAttributeId());
- Context.getAttributesManager().refreshUserAttributes();
- return Response.noContent().build();
- }
-
-}
diff --git a/src/org/traccar/api/resource/AttributeResource.java b/src/org/traccar/api/resource/AttributeResource.java
index 4d326779b..26a1f6931 100644
--- a/src/org/traccar/api/resource/AttributeResource.java
+++ b/src/org/traccar/api/resource/AttributeResource.java
@@ -17,25 +17,17 @@
package org.traccar.api.resource;
import java.sql.SQLException;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
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 javax.ws.rs.core.Response;
import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.database.AttributesManager;
+import org.traccar.api.ExtendedObjectResource;
import org.traccar.model.Attribute;
import org.traccar.model.Position;
import org.traccar.processing.ComputedAttributesHandler;
@@ -43,55 +35,17 @@ import org.traccar.processing.ComputedAttributesHandler;
@Path("attributes/computed")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-public class AttributeResource extends BaseResource {
+public class AttributeResource extends ExtendedObjectResource<Attribute> {
- @GET
- public Collection<Attribute> get(
- @QueryParam("all") boolean all, @QueryParam("userId") long userId, @QueryParam("groupId") long groupId,
- @QueryParam("deviceId") long deviceId, @QueryParam("refresh") boolean refresh) throws SQLException {
-
- AttributesManager attributesManager = Context.getAttributesManager();
- if (refresh) {
- attributesManager.refreshAttributes();
- }
-
- Set<Long> result = new HashSet<>();
- if (all) {
- if (Context.getPermissionsManager().isAdmin(getUserId())) {
- result.addAll(attributesManager.getAllAttributes());
- } else {
- Context.getPermissionsManager().checkManager(getUserId());
- result.addAll(attributesManager.getManagedAttributes(getUserId()));
- }
- } else {
- if (userId == 0) {
- userId = getUserId();
- }
- Context.getPermissionsManager().checkUser(getUserId(), userId);
- result.addAll(attributesManager.getUserAttributes(userId));
- }
-
- if (groupId != 0) {
- Context.getPermissionsManager().checkGroup(getUserId(), groupId);
- result.retainAll(attributesManager.getGroupAttributes(groupId));
- }
-
- if (deviceId != 0) {
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- result.retainAll(attributesManager.getDeviceAttributes(deviceId));
- }
- return attributesManager.getAttributes(result);
-
- }
-
- private Response add(Attribute entity) throws SQLException {
- Context.getAttributesManager().addAttribute(entity);
- Context.getDataManager().linkAttribute(getUserId(), entity.getId());
- Context.getAttributesManager().refreshUserAttributes();
- return Response.ok(entity).build();
+ public AttributeResource() {
+ super(Attribute.class);
}
- private Response test(long deviceId, Attribute entity) {
+ @POST
+ @Path("test")
+ public Response test(@QueryParam("deviceId") long deviceId, Attribute entity) throws SQLException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
Position last = Context.getIdentityManager().getLastPosition(deviceId);
if (last != null) {
Object result = new ComputedAttributesHandler().computeAttribute(entity, last);
@@ -112,33 +66,4 @@ public class AttributeResource extends BaseResource {
}
}
- @POST
- public Response post(@QueryParam("deviceId") long deviceId, Attribute entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- if (deviceId != 0) {
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- return test(deviceId, entity);
- } else {
- return add(entity);
- }
- }
-
- @Path("{id}")
- @PUT
- public Response update(Attribute entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkAttribute(getUserId(), entity.getId());
- Context.getAttributesManager().updateAttribute(entity);
- return Response.ok(entity).build();
- }
-
- @Path("{id}")
- @DELETE
- public Response remove(@PathParam("id") long id) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkAttribute(getUserId(), id);
- Context.getAttributesManager().removeAttribute(id);
- return Response.noContent().build();
- }
-
}
diff --git a/src/org/traccar/api/resource/CalendarPermissionResource.java b/src/org/traccar/api/resource/CalendarPermissionResource.java
deleted file mode 100644
index a49254b6b..000000000
--- a/src/org/traccar/api/resource/CalendarPermissionResource.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.api.resource;
-
-import java.sql.SQLException;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.CalendarPermission;
-
-@Path("permissions/calendars")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class CalendarPermissionResource extends BaseResource {
-
- @POST
- public Response add(CalendarPermission entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
- Context.getPermissionsManager().checkCalendar(getUserId(), entity.getCalendarId());
- Context.getDataManager().linkCalendar(entity.getUserId(), entity.getCalendarId());
- Context.getCalendarManager().refreshUserCalendars();
- return Response.ok(entity).build();
- }
-
- @DELETE
- public Response remove(CalendarPermission entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
- Context.getPermissionsManager().checkCalendar(getUserId(), entity.getCalendarId());
- Context.getDataManager().unlinkCalendar(entity.getUserId(), entity.getCalendarId());
- Context.getCalendarManager().refreshUserCalendars();
- return Response.noContent().build();
- }
-}
diff --git a/src/org/traccar/api/resource/CalendarResource.java b/src/org/traccar/api/resource/CalendarResource.java
index 641d3b4b5..9399c34a5 100644
--- a/src/org/traccar/api/resource/CalendarResource.java
+++ b/src/org/traccar/api/resource/CalendarResource.java
@@ -16,74 +16,21 @@
*/
package org.traccar.api.resource;
-import java.sql.SQLException;
-import java.util.Collection;
-
import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
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 javax.ws.rs.core.Response;
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
+import org.traccar.api.SimpleObjectResource;
import org.traccar.model.Calendar;
@Path("calendars")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-public class CalendarResource extends BaseResource {
-
- @GET
- public Collection<Calendar> get(
- @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException {
-
- if (all) {
- if (Context.getPermissionsManager().isAdmin(getUserId())) {
- return Context.getCalendarManager().getAllCalendars();
- } else {
- Context.getPermissionsManager().checkManager(getUserId());
- return Context.getCalendarManager().getManagedCalendars(getUserId());
- }
- } else {
- if (userId == 0) {
- userId = getUserId();
- }
- Context.getPermissionsManager().checkUser(getUserId(), userId);
- return Context.getCalendarManager().getUserCalendars(userId);
- }
- }
+public class CalendarResource extends SimpleObjectResource<Calendar> {
- @POST
- public Response add(Calendar entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getCalendarManager().addCalendar(entity);
- Context.getDataManager().linkCalendar(getUserId(), entity.getId());
- Context.getCalendarManager().refreshUserCalendars();
- return Response.ok(entity).build();
+ public CalendarResource() {
+ super(Calendar.class);
}
- @Path("{id}")
- @PUT
- public Response update(Calendar entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkCalendar(getUserId(), entity.getId());
- Context.getCalendarManager().updateCalendar(entity);
- return Response.ok(entity).build();
- }
-
- @Path("{id}")
- @DELETE
- public Response remove(@PathParam("id") long id) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkCalendar(getUserId(), id);
- Context.getCalendarManager().removeCalendar(id);
- return Response.noContent().build();
- }
}
diff --git a/src/org/traccar/api/resource/CommandResource.java b/src/org/traccar/api/resource/CommandResource.java
index 9ed92d3d5..703638701 100644
--- a/src/org/traccar/api/resource/CommandResource.java
+++ b/src/org/traccar/api/resource/CommandResource.java
@@ -1,5 +1,7 @@
/*
* Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 Gabor Somogyi (gabor.g.somogyi@gmail.com)
+ * 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.
@@ -16,27 +18,72 @@
package org.traccar.api.resource;
import org.traccar.Context;
-import org.traccar.api.BaseResource;
+import org.traccar.api.ExtendedObjectResource;
+import org.traccar.database.CommandsManager;
import org.traccar.model.Command;
+import org.traccar.model.Typed;
+
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("commands")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-public class CommandResource extends BaseResource {
+public class CommandResource extends ExtendedObjectResource<Command> {
+
+ public CommandResource() {
+ super(Command.class);
+ }
+
+ @GET
+ @Path("send")
+ public Collection<Command> get(@QueryParam("deviceId") long deviceId) throws SQLException {
+ Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
+ CommandsManager commandsManager = Context.getCommandsManager();
+ Set<Long> result = new HashSet<>(commandsManager.getUserItems(getUserId()));
+ result.retainAll(commandsManager.getSupportedCommands(deviceId));
+ return commandsManager.getItems(result);
+ }
@POST
- public Response add(Command entity) throws Exception {
+ @Path("send")
+ public Response send(Command entity) throws Exception {
Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
- Context.getDeviceManager().sendCommand(entity);
+ long deviceId = entity.getDeviceId();
+ long id = entity.getId();
+ Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
+ if (id != 0) {
+ Context.getPermissionsManager().checkPermission(Command.class, getUserId(), id);
+ Context.getPermissionsManager().checkUserDeviceCommand(getUserId(), deviceId, id);
+ } else {
+ Context.getPermissionsManager().checkLimitCommands(getUserId());
+ }
+ if (!Context.getCommandsManager().sendCommand(entity)) {
+ return Response.accepted(entity).build();
+ }
return Response.ok(entity).build();
}
+ @GET
+ @Path("types")
+ public Collection<Typed> get(@QueryParam("deviceId") long deviceId,
+ @QueryParam("textChannel") boolean textChannel) {
+ if (deviceId != 0) {
+ Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
+ return Context.getCommandsManager().getCommandTypes(deviceId, textChannel);
+ } else {
+ return Context.getCommandsManager().getAllCommandTypes();
+ }
+ }
}
diff --git a/src/org/traccar/api/resource/DeviceAttributeResource.java b/src/org/traccar/api/resource/DeviceAttributeResource.java
deleted file mode 100644
index 8d80c9235..000000000
--- a/src/org/traccar/api/resource/DeviceAttributeResource.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.api.resource;
-
-import java.sql.SQLException;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.DeviceAttribute;
-
-@Path("devices/attributes")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class DeviceAttributeResource extends BaseResource {
-
- @POST
- public Response add(DeviceAttribute entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
- Context.getPermissionsManager().checkAttribute(getUserId(), entity.getAttributeId());
- Context.getDataManager().linkDeviceAttribute(entity.getDeviceId(), entity.getAttributeId());
- Context.getAttributesManager().refresh();
- return Response.ok(entity).build();
- }
-
- @DELETE
- public Response remove(DeviceAttribute entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
- Context.getPermissionsManager().checkGeofence(getUserId(), entity.getAttributeId());
- Context.getDataManager().unlinkDeviceAttribute(entity.getDeviceId(), entity.getAttributeId());
- Context.getAttributesManager().refresh();
- return Response.noContent().build();
- }
-
-}
diff --git a/src/org/traccar/api/resource/DeviceGeofenceResource.java b/src/org/traccar/api/resource/DeviceGeofenceResource.java
deleted file mode 100644
index 6254fe3cf..000000000
--- a/src/org/traccar/api/resource/DeviceGeofenceResource.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@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.api.resource;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.DeviceGeofence;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-import java.sql.SQLException;
-
-@Path("devices/geofences")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class DeviceGeofenceResource extends BaseResource {
-
- @POST
- public Response add(DeviceGeofence entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
- Context.getPermissionsManager().checkGeofence(getUserId(), entity.getGeofenceId());
- Context.getDataManager().linkDeviceGeofence(entity.getDeviceId(), entity.getGeofenceId());
- Context.getGeofenceManager().refresh();
- return Response.ok(entity).build();
- }
-
- @DELETE
- public Response remove(DeviceGeofence entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
- Context.getPermissionsManager().checkGeofence(getUserId(), entity.getGeofenceId());
- Context.getDataManager().unlinkDeviceGeofence(entity.getDeviceId(), entity.getGeofenceId());
- Context.getGeofenceManager().refresh();
- return Response.noContent().build();
- }
-
-}
diff --git a/src/org/traccar/api/resource/DevicePermissionResource.java b/src/org/traccar/api/resource/DevicePermissionResource.java
deleted file mode 100644
index 6e00dc47f..000000000
--- a/src/org/traccar/api/resource/DevicePermissionResource.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@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.api.resource;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.DevicePermission;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.sql.SQLException;
-
-@Path("permissions/devices")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class DevicePermissionResource extends BaseResource {
-
- @POST
- public Response add(DevicePermission entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
- Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
- Context.getDataManager().linkDevice(entity.getUserId(), entity.getDeviceId());
- Context.getPermissionsManager().refreshPermissions();
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refresh();
- }
- return Response.ok(entity).build();
- }
-
- @DELETE
- public Response remove(DevicePermission entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- if (getUserId() != entity.getUserId()) {
- Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
- } else {
- Context.getPermissionsManager().checkAdmin(getUserId());
- }
- Context.getPermissionsManager().checkDevice(getUserId(), entity.getDeviceId());
- Context.getDataManager().unlinkDevice(entity.getUserId(), entity.getDeviceId());
- Context.getPermissionsManager().refreshPermissions();
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refresh();
- }
- return Response.noContent().build();
- }
-
-}
diff --git a/src/org/traccar/api/resource/DeviceResource.java b/src/org/traccar/api/resource/DeviceResource.java
index ce46b4e29..1fae92dc7 100644
--- a/src/org/traccar/api/resource/DeviceResource.java
+++ b/src/org/traccar/api/resource/DeviceResource.java
@@ -16,102 +16,68 @@
package org.traccar.api.resource;
import org.traccar.Context;
-import org.traccar.api.BaseResource;
+import org.traccar.api.BaseObjectResource;
+import org.traccar.database.DeviceManager;
import org.traccar.model.Device;
import org.traccar.model.DeviceTotalDistance;
import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
-import javax.ws.rs.POST;
import javax.ws.rs.PUT;
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 javax.ws.rs.core.Response;
import java.sql.SQLException;
-import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
@Path("devices")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-public class DeviceResource extends BaseResource {
+public class DeviceResource extends BaseObjectResource<Device> {
+
+ public DeviceResource() {
+ super(Device.class);
+ }
@GET
public Collection<Device> get(
@QueryParam("all") boolean all, @QueryParam("userId") long userId,
+ @QueryParam("uniqueId") List<String> uniqueIds,
@QueryParam("id") List<Long> deviceIds) throws SQLException {
- if (deviceIds.isEmpty()) {
- if (all) {
- if (Context.getPermissionsManager().isAdmin(getUserId())) {
- return Context.getDeviceManager().getAllDevices();
- } else {
- Context.getPermissionsManager().checkManager(getUserId());
- return Context.getDeviceManager().getManagedDevices(getUserId());
- }
+ DeviceManager deviceManager = Context.getDeviceManager();
+ Set<Long> result = null;
+ if (all) {
+ if (Context.getPermissionsManager().getUserAdmin(getUserId())) {
+ result = deviceManager.getAllItems();
} else {
- if (userId == 0) {
- userId = getUserId();
- }
- Context.getPermissionsManager().checkUser(getUserId(), userId);
- return Context.getDeviceManager().getDevices(userId);
+ Context.getPermissionsManager().checkManager(getUserId());
+ result = deviceManager.getManagedItems(getUserId());
}
+ } else if (uniqueIds.isEmpty() && deviceIds.isEmpty()) {
+ if (userId == 0) {
+ userId = getUserId();
+ }
+ Context.getPermissionsManager().checkUser(getUserId(), userId);
+ result = deviceManager.getUserItems(userId);
} else {
- ArrayList<Device> devices = new ArrayList<>();
+ result = new HashSet<Long>();
+ for (String uniqueId : uniqueIds) {
+ Device device = deviceManager.getByUniqueId(uniqueId);
+ Context.getPermissionsManager().checkDevice(getUserId(), device.getId());
+ result.add(device.getId());
+ }
for (Long deviceId : deviceIds) {
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- devices.add(Context.getDeviceManager().getDeviceById(deviceId));
+ result.add(deviceId);
}
- return devices;
}
- }
-
- @POST
- public Response add(Device entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkDeviceReadonly(getUserId());
- Context.getPermissionsManager().checkDeviceLimit(getUserId());
- Context.getDeviceManager().addDevice(entity);
- Context.getDataManager().linkDevice(getUserId(), entity.getId());
- Context.getPermissionsManager().refreshPermissions();
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refresh();
- }
- return Response.ok(entity).build();
- }
-
- @Path("{id}")
- @PUT
- public Response update(Device entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkDeviceReadonly(getUserId());
- Context.getPermissionsManager().checkDevice(getUserId(), entity.getId());
- Context.getDeviceManager().updateDevice(entity);
- Context.getPermissionsManager().refreshPermissions();
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refresh();
- }
- return Response.ok(entity).build();
- }
-
- @Path("{id}")
- @DELETE
- public Response remove(@PathParam("id") long id) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkDeviceReadonly(getUserId());
- Context.getPermissionsManager().checkDevice(getUserId(), id);
- Context.getDeviceManager().removeDevice(id);
- Context.getPermissionsManager().refreshPermissions();
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refresh();
- }
- Context.getAliasesManager().removeDevice(id);
- return Response.noContent().build();
+ return deviceManager.getItems(result);
}
@Path("{id}/distance")
diff --git a/src/org/traccar/api/resource/DriverResource.java b/src/org/traccar/api/resource/DriverResource.java
new file mode 100644
index 000000000..91aa54c5e
--- /dev/null
+++ b/src/org/traccar/api/resource/DriverResource.java
@@ -0,0 +1,36 @@
+/*
+ * 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.api.resource;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.traccar.api.ExtendedObjectResource;
+import org.traccar.model.Driver;
+
+@Path("drivers")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class DriverResource extends ExtendedObjectResource<Driver> {
+
+ public DriverResource() {
+ super(Driver.class);
+ }
+
+}
diff --git a/src/org/traccar/api/resource/EventResource.java b/src/org/traccar/api/resource/EventResource.java
index 0ef5456af..a7cf9edbd 100644
--- a/src/org/traccar/api/resource/EventResource.java
+++ b/src/org/traccar/api/resource/EventResource.java
@@ -12,6 +12,7 @@ import javax.ws.rs.core.MediaType;
import org.traccar.Context;
import org.traccar.api.BaseResource;
import org.traccar.model.Event;
+import org.traccar.model.Geofence;
@Path("events")
@Produces(MediaType.APPLICATION_JSON)
@@ -22,10 +23,10 @@ public class EventResource extends BaseResource {
@Path("{id}")
@GET
public Event get(@PathParam("id") long id) throws SQLException {
- Event event = Context.getDataManager().getEvent(id);
+ Event event = Context.getDataManager().getObject(Event.class, id);
Context.getPermissionsManager().checkDevice(getUserId(), event.getDeviceId());
if (event.getGeofenceId() != 0) {
- Context.getPermissionsManager().checkGeofence(getUserId(), event.getGeofenceId());
+ Context.getPermissionsManager().checkPermission(Geofence.class, getUserId(), event.getGeofenceId());
}
return event;
}
diff --git a/src/org/traccar/api/resource/GeofencePermissionResource.java b/src/org/traccar/api/resource/GeofencePermissionResource.java
deleted file mode 100644
index 8faa63d85..000000000
--- a/src/org/traccar/api/resource/GeofencePermissionResource.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@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.api.resource;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.GeofencePermission;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.sql.SQLException;
-
-@Path("permissions/geofences")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class GeofencePermissionResource extends BaseResource {
-
- @POST
- public Response add(GeofencePermission entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
- Context.getPermissionsManager().checkGeofence(getUserId(), entity.getGeofenceId());
- Context.getDataManager().linkGeofence(entity.getUserId(), entity.getGeofenceId());
- Context.getGeofenceManager().refreshUserGeofences();
- return Response.ok(entity).build();
- }
-
- @DELETE
- public Response remove(GeofencePermission entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
- Context.getPermissionsManager().checkGeofence(getUserId(), entity.getGeofenceId());
- Context.getDataManager().unlinkGeofence(entity.getUserId(), entity.getGeofenceId());
- Context.getGeofenceManager().refreshUserGeofences();
- return Response.noContent().build();
- }
-
-}
diff --git a/src/org/traccar/api/resource/GeofenceResource.java b/src/org/traccar/api/resource/GeofenceResource.java
index d5acf106a..58f2c188c 100644
--- a/src/org/traccar/api/resource/GeofenceResource.java
+++ b/src/org/traccar/api/resource/GeofenceResource.java
@@ -15,98 +15,21 @@
*/
package org.traccar.api.resource;
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.database.GeofenceManager;
+import org.traccar.api.ExtendedObjectResource;
import org.traccar.model.Geofence;
import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
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 javax.ws.rs.core.Response;
-
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
@Path("geofences")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-public class GeofenceResource extends BaseResource {
-
- @GET
- public Collection<Geofence> get(
- @QueryParam("all") boolean all, @QueryParam("userId") long userId, @QueryParam("groupId") long groupId,
- @QueryParam("deviceId") long deviceId, @QueryParam("refresh") boolean refresh) throws SQLException {
-
- GeofenceManager geofenceManager = Context.getGeofenceManager();
- if (refresh) {
- geofenceManager.refreshGeofences();
- }
-
- Set<Long> result = new HashSet<>();
- if (all) {
- if (Context.getPermissionsManager().isAdmin(getUserId())) {
- result.addAll(geofenceManager.getAllGeofencesIds());
- } else {
- Context.getPermissionsManager().checkManager(getUserId());
- result.addAll(geofenceManager.getManagedGeofencesIds(getUserId()));
- }
- } else {
- if (userId == 0) {
- userId = getUserId();
- }
- Context.getPermissionsManager().checkUser(getUserId(), userId);
- result.addAll(geofenceManager.getUserGeofencesIds(userId));
- }
-
- if (groupId != 0) {
- Context.getPermissionsManager().checkGroup(getUserId(), groupId);
- result.retainAll(geofenceManager.getGroupGeofencesIds(groupId));
- }
-
- if (deviceId != 0) {
- Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- result.retainAll(geofenceManager.getDeviceGeofencesIds(deviceId));
- }
- return geofenceManager.getGeofences(result);
-
- }
-
- @POST
- public Response add(Geofence entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getDataManager().addGeofence(entity);
- Context.getDataManager().linkGeofence(getUserId(), entity.getId());
- Context.getGeofenceManager().refreshGeofences();
- return Response.ok(entity).build();
- }
-
- @Path("{id}")
- @PUT
- public Response update(Geofence entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkGeofence(getUserId(), entity.getId());
- Context.getGeofenceManager().updateGeofence(entity);
- return Response.ok(entity).build();
- }
+public class GeofenceResource extends ExtendedObjectResource<Geofence> {
- @Path("{id}")
- @DELETE
- public Response remove(@PathParam("id") long id) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkGeofence(getUserId(), id);
- Context.getDataManager().removeGeofence(id);
- Context.getGeofenceManager().refreshGeofences();
- return Response.noContent().build();
+ public GeofenceResource() {
+ super(Geofence.class);
}
}
diff --git a/src/org/traccar/api/resource/GroupAttributeResource.java b/src/org/traccar/api/resource/GroupAttributeResource.java
deleted file mode 100644
index 84b876d34..000000000
--- a/src/org/traccar/api/resource/GroupAttributeResource.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.api.resource;
-
-import java.sql.SQLException;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.GroupAttribute;
-
-@Path("groups/attributes")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class GroupAttributeResource extends BaseResource {
-
- @POST
- public Response add(GroupAttribute entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkGroup(getUserId(), entity.getGroupId());
- Context.getPermissionsManager().checkAttribute(getUserId(), entity.getAttributeId());
- Context.getDataManager().linkGroupAttribute(entity.getGroupId(), entity.getAttributeId());
- Context.getAttributesManager().refresh();
- return Response.ok(entity).build();
- }
-
- @DELETE
- public Response remove(GroupAttribute entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkGroup(getUserId(), entity.getGroupId());
- Context.getPermissionsManager().checkGeofence(getUserId(), entity.getAttributeId());
- Context.getDataManager().unlinkGroupAttribute(entity.getGroupId(), entity.getAttributeId());
- Context.getAttributesManager().refresh();
- return Response.noContent().build();
- }
-
-}
diff --git a/src/org/traccar/api/resource/GroupGeofenceResource.java b/src/org/traccar/api/resource/GroupGeofenceResource.java
deleted file mode 100644
index 81fd4e45f..000000000
--- a/src/org/traccar/api/resource/GroupGeofenceResource.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@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.api.resource;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.GroupGeofence;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.sql.SQLException;
-
-@Path("groups/geofences")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class GroupGeofenceResource extends BaseResource {
-
- @POST
- public Response add(GroupGeofence entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkGroup(getUserId(), entity.getGroupId());
- Context.getPermissionsManager().checkGeofence(getUserId(), entity.getGeofenceId());
- Context.getDataManager().linkGroupGeofence(entity.getGroupId(), entity.getGeofenceId());
- Context.getGeofenceManager().refresh();
- return Response.ok(entity).build();
- }
-
- @DELETE
- public Response remove(GroupGeofence entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkGroup(getUserId(), entity.getGroupId());
- Context.getPermissionsManager().checkGeofence(getUserId(), entity.getGeofenceId());
- Context.getDataManager().unlinkGroupGeofence(entity.getGroupId(), entity.getGeofenceId());
- Context.getGeofenceManager().refresh();
- return Response.noContent().build();
- }
-
-}
diff --git a/src/org/traccar/api/resource/GroupPermissionResource.java b/src/org/traccar/api/resource/GroupPermissionResource.java
deleted file mode 100644
index 61a725222..000000000
--- a/src/org/traccar/api/resource/GroupPermissionResource.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@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.api.resource;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.GroupPermission;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import java.sql.SQLException;
-
-@Path("permissions/groups")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class GroupPermissionResource extends BaseResource {
-
- @POST
- public Response add(GroupPermission entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
- Context.getPermissionsManager().checkGroup(getUserId(), entity.getGroupId());
- Context.getDataManager().linkGroup(entity.getUserId(), entity.getGroupId());
- Context.getPermissionsManager().refreshPermissions();
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refresh();
- }
- return Response.ok(entity).build();
- }
-
- @DELETE
- public Response remove(GroupPermission entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
- Context.getPermissionsManager().checkGroup(getUserId(), entity.getGroupId());
- Context.getDataManager().unlinkGroup(entity.getUserId(), entity.getGroupId());
- Context.getPermissionsManager().refreshPermissions();
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refresh();
- }
- return Response.noContent().build();
- }
-
-}
diff --git a/src/org/traccar/api/resource/GroupResource.java b/src/org/traccar/api/resource/GroupResource.java
index ceba69105..fcea15d0a 100644
--- a/src/org/traccar/api/resource/GroupResource.java
+++ b/src/org/traccar/api/resource/GroupResource.java
@@ -15,83 +15,21 @@
*/
package org.traccar.api.resource;
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
+import org.traccar.api.SimpleObjectResource;
import org.traccar.model.Group;
import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
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 javax.ws.rs.core.Response;
-import java.sql.SQLException;
-import java.util.Collection;
@Path("groups")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-public class GroupResource extends BaseResource {
+public class GroupResource extends SimpleObjectResource<Group> {
- @GET
- public Collection<Group> get(
- @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException {
- if (all) {
- if (Context.getPermissionsManager().isAdmin(getUserId())) {
- return Context.getDeviceManager().getAllGroups();
- } else {
- Context.getPermissionsManager().checkManager(getUserId());
- return Context.getDeviceManager().getManagedGroups(getUserId());
- }
- } else {
- if (userId == 0) {
- userId = getUserId();
- }
- Context.getPermissionsManager().checkUser(getUserId(), userId);
- return Context.getDeviceManager().getGroups(userId);
- }
- }
-
- @POST
- public Response add(Group entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getDeviceManager().addGroup(entity);
- Context.getDataManager().linkGroup(getUserId(), entity.getId());
- Context.getPermissionsManager().refreshPermissions();
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refresh();
- }
- return Response.ok(entity).build();
- }
-
- @Path("{id}")
- @PUT
- public Response update(Group entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkGroup(getUserId(), entity.getId());
- Context.getDeviceManager().updateGroup(entity);
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refresh();
- }
- return Response.ok(entity).build();
- }
-
- @Path("{id}")
- @DELETE
- public Response remove(@PathParam("id") long id) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkGroup(getUserId(), id);
- Context.getDeviceManager().removeGroup(id);
- Context.getPermissionsManager().refreshPermissions();
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refresh();
- }
- return Response.noContent().build();
+ public GroupResource() {
+ super(Group.class);
}
}
diff --git a/src/org/traccar/api/resource/NotificationResource.java b/src/org/traccar/api/resource/NotificationResource.java
index dee972607..540f02926 100644
--- a/src/org/traccar/api/resource/NotificationResource.java
+++ b/src/org/traccar/api/resource/NotificationResource.java
@@ -15,7 +15,6 @@
*/
package org.traccar.api.resource;
-import java.sql.SQLException;
import java.util.Collection;
import javax.mail.MessagingException;
@@ -24,14 +23,14 @@ import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.traccar.Context;
-import org.traccar.api.BaseResource;
+import org.traccar.api.ExtendedObjectResource;
import org.traccar.model.Event;
import org.traccar.model.Notification;
+import org.traccar.model.Typed;
import org.traccar.notification.NotificationMail;
import org.traccar.notification.NotificationSms;
@@ -40,34 +39,23 @@ import com.cloudhopper.smpp.type.SmppChannelException;
import com.cloudhopper.smpp.type.SmppTimeoutException;
import com.cloudhopper.smpp.type.UnrecoverablePduException;
-@Path("users/notifications")
+@Path("notifications")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-public class NotificationResource extends BaseResource {
+public class NotificationResource extends ExtendedObjectResource<Notification> {
- @GET
- public Collection<Notification> get(@QueryParam("all") boolean all,
- @QueryParam("userId") long userId) throws SQLException {
- if (all) {
- return Context.getNotificationManager().getAllNotifications();
- }
- if (userId == 0) {
- userId = getUserId();
- }
- Context.getPermissionsManager().checkUser(getUserId(), userId);
- return Context.getNotificationManager().getAllUserNotifications(userId);
+ public NotificationResource() {
+ super(Notification.class);
}
- @POST
- public Response update(Notification entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkUser(getUserId(), entity.getUserId());
- Context.getNotificationManager().updateNotification(entity);
- return Response.ok(entity).build();
+ @GET
+ @Path("types")
+ public Collection<Typed> get() {
+ return Context.getNotificationManager().getAllNotificationTypes();
}
- @Path("test")
@POST
+ @Path("test")
public Response testMessage() throws MessagingException, RecoverablePduException,
UnrecoverablePduException, SmppTimeoutException, SmppChannelException, InterruptedException {
NotificationMail.sendMailSync(getUserId(), new Event("test", 0), null);
diff --git a/src/org/traccar/api/resource/PermissionsResource.java b/src/org/traccar/api/resource/PermissionsResource.java
new file mode 100644
index 000000000..9b9f65ad1
--- /dev/null
+++ b/src/org/traccar/api/resource/PermissionsResource.java
@@ -0,0 +1,79 @@
+/*
+ * 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.api.resource;
+
+import java.sql.SQLException;
+import java.util.LinkedHashMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.traccar.Context;
+import org.traccar.api.BaseResource;
+import org.traccar.model.Device;
+import org.traccar.model.Permission;
+import org.traccar.model.User;
+
+@Path("permissions")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class PermissionsResource extends BaseResource {
+
+ private void checkPermission(Permission permission, boolean link) {
+ if (!link && permission.getOwnerClass().equals(User.class)
+ && permission.getPropertyClass().equals(Device.class)) {
+ if (getUserId() != permission.getOwnerId()) {
+ Context.getPermissionsManager().checkUser(getUserId(), permission.getOwnerId());
+ } else {
+ Context.getPermissionsManager().checkAdmin(getUserId());
+ }
+ } else {
+ Context.getPermissionsManager().checkPermission(
+ permission.getOwnerClass(), getUserId(), permission.getOwnerId());
+ }
+ Context.getPermissionsManager().checkPermission(
+ permission.getPropertyClass(), getUserId(), permission.getPropertyId());
+ }
+
+ @POST
+ public Response add(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Permission permission = new Permission(entity);
+ checkPermission(permission, true);
+ Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
+ permission.getPropertyClass(), permission.getPropertyId(), true);
+ Context.getPermissionsManager().refreshPermissions(permission);
+ return Response.noContent().build();
+ }
+
+ @DELETE
+ public Response remove(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException {
+ Context.getPermissionsManager().checkReadonly(getUserId());
+ Permission permission = new Permission(entity);
+ checkPermission(permission, false);
+ Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
+ permission.getPropertyClass(), permission.getPropertyId(), false);
+ Context.getPermissionsManager().refreshPermissions(permission);
+ return Response.noContent().build();
+ }
+
+}
diff --git a/src/org/traccar/api/resource/PositionResource.java b/src/org/traccar/api/resource/PositionResource.java
index 9d3cd9ae6..c031b842f 100644
--- a/src/org/traccar/api/resource/PositionResource.java
+++ b/src/org/traccar/api/resource/PositionResource.java
@@ -54,7 +54,7 @@ public class PositionResource extends BaseResource {
if (!positionIds.isEmpty()) {
ArrayList<Position> positions = new ArrayList<>();
for (Long positionId : positionIds) {
- Position position = Context.getDataManager().getPosition(positionId);
+ Position position = Context.getDataManager().getObject(Position.class, positionId);
Context.getPermissionsManager().checkDevice(getUserId(), position.getDeviceId());
positions.add(position);
}
@@ -87,7 +87,7 @@ public class PositionResource extends BaseResource {
@QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to)
throws SQLException {
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- GpxBuilder gpx = new GpxBuilder(Context.getIdentityManager().getDeviceById(deviceId).getName());
+ GpxBuilder gpx = new GpxBuilder(Context.getIdentityManager().getById(deviceId).getName());
gpx.addPositions(Context.getDataManager().getPositions(
deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to)));
return Response.ok(gpx.build()).header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_GPX).build();
diff --git a/src/org/traccar/api/resource/SessionResource.java b/src/org/traccar/api/resource/SessionResource.java
index acdbb7c87..fa2a14c6f 100644
--- a/src/org/traccar/api/resource/SessionResource.java
+++ b/src/org/traccar/api/resource/SessionResource.java
@@ -80,7 +80,7 @@ public class SessionResource extends BaseResource {
request.getSession().setAttribute(USER_ID_KEY, userId);
}
} else if (token != null) {
- User user = Context.getPermissionsManager().getUserByToken(token);
+ User user = Context.getUsersManager().getUserByToken(token);
if (user != null) {
userId = user.getId();
request.getSession().setAttribute(USER_ID_KEY, userId);
diff --git a/src/org/traccar/api/resource/UserPermissionResource.java b/src/org/traccar/api/resource/UserPermissionResource.java
deleted file mode 100644
index a97c4a665..000000000
--- a/src/org/traccar/api/resource/UserPermissionResource.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.api.resource;
-
-import java.sql.SQLException;
-
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-import org.traccar.Context;
-import org.traccar.api.BaseResource;
-import org.traccar.model.UserPermission;
-
-@Path("permissions/users")
-@Produces(MediaType.APPLICATION_JSON)
-@Consumes(MediaType.APPLICATION_JSON)
-public class UserPermissionResource extends BaseResource {
-
- @POST
- public Response add(UserPermission entity) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
- if (entity.getUserId() != entity.getManagedUserId()) {
- Context.getDataManager().linkUser(entity.getUserId(), entity.getManagedUserId());
- Context.getPermissionsManager().refreshUserPermissions();
- }
- return Response.ok(entity).build();
- }
-
- @DELETE
- public Response remove(UserPermission entity) throws SQLException {
- Context.getPermissionsManager().checkAdmin(getUserId());
- Context.getDataManager().unlinkUser(entity.getUserId(), entity.getManagedUserId());
- Context.getPermissionsManager().refreshUserPermissions();
- return Response.noContent().build();
- }
-
-}
diff --git a/src/org/traccar/api/resource/UserResource.java b/src/org/traccar/api/resource/UserResource.java
index 4d8a8b3a4..0f6f6edba 100644
--- a/src/org/traccar/api/resource/UserResource.java
+++ b/src/org/traccar/api/resource/UserResource.java
@@ -16,17 +16,16 @@
package org.traccar.api.resource;
import org.traccar.Context;
-import org.traccar.api.BaseResource;
+import org.traccar.api.BaseObjectResource;
+import org.traccar.database.UsersManager;
+import org.traccar.model.ManagedUser;
import org.traccar.model.User;
import javax.annotation.security.PermitAll;
import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
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;
@@ -34,33 +33,42 @@ import javax.ws.rs.core.Response;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Date;
+import java.util.Set;
@Path("users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
-public class UserResource extends BaseResource {
+public class UserResource extends BaseObjectResource<User> {
+
+ public UserResource() {
+ super(User.class);
+ }
@GET
public Collection<User> get(@QueryParam("userId") long userId) throws SQLException {
- if (Context.getPermissionsManager().isAdmin(getUserId())) {
+ UsersManager usersManager = Context.getUsersManager();
+ Set<Long> result = null;
+ if (Context.getPermissionsManager().getUserAdmin(getUserId())) {
if (userId != 0) {
- return Context.getPermissionsManager().getUsers(userId);
+ result = usersManager.getUserItems(userId);
} else {
- return Context.getPermissionsManager().getAllUsers();
+ result = usersManager.getAllItems();
}
- } else if (Context.getPermissionsManager().isManager(getUserId())) {
- return Context.getPermissionsManager().getManagedUsers(getUserId());
+ } else if (Context.getPermissionsManager().getUserManager(getUserId())) {
+ result = usersManager.getManagedItems(getUserId());
} else {
throw new SecurityException("Admin or manager access required");
}
+ return usersManager.getItems(result);
}
+ @Override
@PermitAll
@POST
public Response add(User entity) throws SQLException {
- if (!Context.getPermissionsManager().isAdmin(getUserId())) {
+ if (!Context.getPermissionsManager().getUserAdmin(getUserId())) {
Context.getPermissionsManager().checkUserUpdate(getUserId(), new User(), entity);
- if (Context.getPermissionsManager().isManager(getUserId())) {
+ if (Context.getPermissionsManager().getUserManager(getUserId())) {
Context.getPermissionsManager().checkUserLimit(getUserId());
} else {
Context.getPermissionsManager().checkRegistration(getUserId());
@@ -72,44 +80,12 @@ public class UserResource extends BaseResource {
}
}
}
- Context.getPermissionsManager().addUser(entity);
- if (Context.getPermissionsManager().isManager(getUserId())) {
- Context.getDataManager().linkUser(getUserId(), entity.getId());
- }
- Context.getPermissionsManager().refreshUserPermissions();
- if (Context.getNotificationManager() != null) {
- Context.getNotificationManager().refresh();
+ Context.getUsersManager().addItem(entity);
+ if (Context.getPermissionsManager().getUserManager(getUserId())) {
+ Context.getDataManager().linkObject(User.class, getUserId(), ManagedUser.class, entity.getId(), true);
}
+ Context.getUsersManager().refreshUserItems();
return Response.ok(entity).build();
}
- @Path("{id}")
- @PUT
- public Response update(User entity) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- User before = Context.getPermissionsManager().getUser(entity.getId());
- Context.getPermissionsManager().checkUser(getUserId(), entity.getId());
- Context.getPermissionsManager().checkUserUpdate(getUserId(), before, entity);
- Context.getPermissionsManager().updateUser(entity);
- if (Context.getNotificationManager() != null) {
- Context.getNotificationManager().refresh();
- }
- return Response.ok(entity).build();
- }
-
- @Path("{id}")
- @DELETE
- public Response remove(@PathParam("id") long id) throws SQLException {
- Context.getPermissionsManager().checkReadonly(getUserId());
- Context.getPermissionsManager().checkUser(getUserId(), id);
- Context.getPermissionsManager().removeUser(id);
- if (Context.getGeofenceManager() != null) {
- Context.getGeofenceManager().refreshUserGeofences();
- }
- if (Context.getNotificationManager() != null) {
- Context.getNotificationManager().refresh();
- }
- return Response.noContent().build();
- }
-
}
diff --git a/src/org/traccar/database/AliasesManager.java b/src/org/traccar/database/AliasesManager.java
deleted file mode 100644
index 4f4f09731..000000000
--- a/src/org/traccar/database/AliasesManager.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.database;
-
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.traccar.Context;
-import org.traccar.helper.Log;
-import org.traccar.model.AttributeAlias;
-
-public class AliasesManager {
-
- private final DataManager dataManager;
-
- private final Map<Long, Set<AttributeAlias>> deviceAliases = new ConcurrentHashMap<>();
- private final Map<Long, AttributeAlias> aliasesById = new ConcurrentHashMap<>();
-
- public AliasesManager(DataManager dataManager) {
- this.dataManager = dataManager;
- if (dataManager != null) {
- try {
- for (AttributeAlias attributeAlias : dataManager.getAttributeAliases()) {
- getAttributeAliases(attributeAlias.getDeviceId())
- .add(attributeAlias);
- aliasesById.put(attributeAlias.getId(), attributeAlias);
- }
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
- }
-
- public Set<AttributeAlias> getAttributeAliases(long deviceId) {
- if (!deviceAliases.containsKey(deviceId)) {
- deviceAliases.put(deviceId, new HashSet<AttributeAlias>());
- }
- return deviceAliases.get(deviceId);
- }
-
- public void removeDevice(long deviceId) {
- for (AttributeAlias attributeAlias : getAttributeAliases(deviceId)) {
- aliasesById.remove(attributeAlias.getId());
- }
- deviceAliases.remove(deviceId);
- }
-
- public void addAttributeAlias(AttributeAlias attributeAlias) throws SQLException {
- dataManager.addAttributeAlias(attributeAlias);
- aliasesById.put(attributeAlias.getId(), attributeAlias);
- getAttributeAliases(attributeAlias.getDeviceId()).add(attributeAlias);
- }
-
- public void updateAttributeAlias(AttributeAlias attributeAlias) throws SQLException {
- dataManager.updateAttributeAlias(attributeAlias);
- AttributeAlias cachedAlias = aliasesById.get(attributeAlias.getId());
- if (cachedAlias.getDeviceId() != attributeAlias.getDeviceId()) {
- getAttributeAliases(cachedAlias.getDeviceId()).remove(cachedAlias);
- cachedAlias.setDeviceId(attributeAlias.getDeviceId());
- getAttributeAliases(cachedAlias.getDeviceId()).add(cachedAlias);
- }
- cachedAlias.setAttribute(attributeAlias.getAttribute());
- cachedAlias.setAlias(attributeAlias.getAlias());
- }
-
- public void removeArrtibuteAlias(long attributeAliasId) throws SQLException {
- dataManager.removeAttributeAlias(attributeAliasId);
- AttributeAlias cachedAlias = aliasesById.get(attributeAliasId);
- getAttributeAliases(cachedAlias.getDeviceId()).remove(cachedAlias);
- aliasesById.remove(attributeAliasId);
- }
-
- public AttributeAlias getAttributeAlias(long deviceId, String attribute) {
- for (AttributeAlias alias : getAttributeAliases(deviceId)) {
- if (alias.getAttribute().equals(attribute)) {
- return alias;
- }
- }
- return null;
- }
-
- public Collection<AttributeAlias> getAllAttributeAliases(long userId) {
- Collection<AttributeAlias> userDevicesAliases = new ArrayList<>();
- for (long deviceId : Context.getPermissionsManager().getDevicePermissions(userId)) {
- userDevicesAliases.addAll(getAttributeAliases(deviceId));
- }
- return userDevicesAliases;
- }
-
- public AttributeAlias getAttributeAlias(long id) {
- return aliasesById.get(id);
- }
-
-}
diff --git a/src/org/traccar/database/AttributesManager.java b/src/org/traccar/database/AttributesManager.java
index 362d6130f..28816645a 100644
--- a/src/org/traccar/database/AttributesManager.java
+++ b/src/org/traccar/database/AttributesManager.java
@@ -16,184 +16,21 @@
*/
package org.traccar.database;
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.traccar.Context;
-import org.traccar.helper.Log;
-import org.traccar.model.AttributePermission;
import org.traccar.model.Attribute;
-import org.traccar.model.Device;
-import org.traccar.model.DeviceAttribute;
-import org.traccar.model.GroupAttribute;
-
-public class AttributesManager {
-
- private final DataManager dataManager;
- private final Map<Long, Attribute> attributes = new ConcurrentHashMap<>();
- private final Map<Long, Set<Long>> deviceAttributes = new ConcurrentHashMap<>();
- private final Map<Long, Set<Long>> deviceAttributesWithGroups = new ConcurrentHashMap<>();
- private final Map<Long, Set<Long>> groupAttributes = new ConcurrentHashMap<>();
- private final Map<Long, Set<Long>> userAttributes = new ConcurrentHashMap<>();
+public class AttributesManager extends ExtendedObjectManager<Attribute> {
public AttributesManager(DataManager dataManager) {
- this.dataManager = dataManager;
- refreshAttributes();
- }
-
- public Set<Long> getUserAttributes(long userId) {
- if (!userAttributes.containsKey(userId)) {
- userAttributes.put(userId, new HashSet<Long>());
- }
- return userAttributes.get(userId);
- }
-
- public Set<Long> getGroupAttributes(long groupId) {
- if (!groupAttributes.containsKey(groupId)) {
- groupAttributes.put(groupId, new HashSet<Long>());
- }
- return groupAttributes.get(groupId);
- }
-
- public Set<Long> getDeviceAttributes(long deviceId) {
- return getDeviceAttributes(deviceAttributes, deviceId);
- }
-
- public Set<Long> getAllDeviceAttributes(long deviceId) {
- return getDeviceAttributes(deviceAttributesWithGroups, deviceId);
- }
-
- private Set<Long> getDeviceAttributes(Map<Long, Set<Long>> deviceAttributes, long deviceId) {
- if (!deviceAttributes.containsKey(deviceId)) {
- deviceAttributes.put(deviceId, new HashSet<Long>());
- }
- return deviceAttributes.get(deviceId);
- }
-
- public final void refreshAttributes() {
- if (dataManager != null) {
- try {
- attributes.clear();
- for (Attribute attribute : dataManager.getAttributes()) {
- attributes.put(attribute.getId(), attribute);
- }
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
- refreshUserAttributes();
- refresh();
- }
-
- public final void refreshUserAttributes() {
- if (dataManager != null) {
- try {
- userAttributes.clear();
- for (AttributePermission attributePermission : dataManager.getAttributePermissions()) {
- getUserAttributes(attributePermission.getUserId()).add(attributePermission.getAttributeId());
- }
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
- }
-
- public final void refresh() {
- if (dataManager != null) {
- try {
-
- Collection<GroupAttribute> databaseGroupAttributes = dataManager.getGroupAttributes();
-
- groupAttributes.clear();
- for (GroupAttribute groupAttribute : databaseGroupAttributes) {
- getGroupAttributes(groupAttribute.getGroupId()).add(groupAttribute.getAttributeId());
- }
-
- Collection<DeviceAttribute> databaseDeviceAttributes = dataManager.getDeviceAttributes();
- Collection<Device> allDevices = Context.getDeviceManager().getAllDevices();
-
- deviceAttributes.clear();
- deviceAttributesWithGroups.clear();
-
- for (DeviceAttribute deviceAttribute : databaseDeviceAttributes) {
- getDeviceAttributes(deviceAttribute.getDeviceId())
- .add(deviceAttribute.getAttributeId());
- getAllDeviceAttributes(deviceAttribute.getDeviceId())
- .add(deviceAttribute.getAttributeId());
- }
-
- for (Device device : allDevices) {
- long groupId = device.getGroupId();
- while (groupId != 0) {
- getAllDeviceAttributes(device.getId()).addAll(getGroupAttributes(groupId));
- if (Context.getDeviceManager().getGroupById(groupId) != null) {
- groupId = Context.getDeviceManager().getGroupById(groupId).getGroupId();
- } else {
- groupId = 0;
- }
- }
- }
-
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
- }
-
- public void addAttribute(Attribute attribute) throws SQLException {
- dataManager.addAttribute(attribute);
- attributes.put(attribute.getId(), attribute);
+ super(dataManager, Attribute.class);
}
- public void updateAttribute(Attribute attribute) throws SQLException {
- dataManager.updateAttribute(attribute);
- Attribute cachedAttribute = attributes.get(attribute.getId());
+ @Override
+ public void updateCachedItem(Attribute attribute) {
+ Attribute cachedAttribute = getById(attribute.getId());
cachedAttribute.setDescription(attribute.getDescription());
cachedAttribute.setAttribute(attribute.getAttribute());
cachedAttribute.setExpression(attribute.getExpression());
cachedAttribute.setType(attribute.getType());
}
- public void removeAttribute(long computedAttributeId) throws SQLException {
- dataManager.removeAttribute(computedAttributeId);
- attributes.remove(computedAttributeId);
- refreshUserAttributes();
- refresh();
- }
-
- public boolean checkAttribute(long userId, long attributeId) {
- return getUserAttributes(userId).contains(attributeId);
- }
-
- public Attribute getAttribute(long id) {
- return attributes.get(id);
- }
-
- public final Collection<Attribute> getAttributes(Set<Long> attributeIds) {
- Collection<Attribute> result = new LinkedList<>();
- for (long attributeId : attributeIds) {
- result.add(getAttribute(attributeId));
- }
- return result;
- }
-
- public final Set<Long> getAllAttributes() {
- return attributes.keySet();
- }
-
- public final Set<Long> getManagedAttributes(long userId) {
- Set<Long> attributes = new HashSet<>();
- attributes.addAll(getUserAttributes(userId));
- for (long managedUserId : Context.getPermissionsManager().getUserPermissions(userId)) {
- attributes.addAll(getUserAttributes(managedUserId));
- }
- return attributes;
- }
-
}
diff --git a/src/org/traccar/database/BaseObjectManager.java b/src/org/traccar/database/BaseObjectManager.java
new file mode 100644
index 000000000..cc1dbde5f
--- /dev/null
+++ b/src/org/traccar/database/BaseObjectManager.java
@@ -0,0 +1,124 @@
+/*
+ * 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.database;
+
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.traccar.helper.Log;
+import org.traccar.model.BaseModel;
+
+public class BaseObjectManager<T extends BaseModel> {
+
+ private final DataManager dataManager;
+
+ private Map<Long, T> items;
+ private Class<T> baseClass;
+
+ protected BaseObjectManager(DataManager dataManager, Class<T> baseClass) {
+ this.dataManager = dataManager;
+ this.baseClass = baseClass;
+ refreshItems();
+ }
+
+ protected final DataManager getDataManager() {
+ return dataManager;
+ }
+
+ protected final Class<T> getBaseClass() {
+ return baseClass;
+ }
+
+ public T getById(long itemId) {
+ return items.get(itemId);
+ }
+
+ public void refreshItems() {
+ if (dataManager != null) {
+ try {
+ Collection<T> databaseItems = dataManager.getObjects(baseClass);
+ if (items == null) {
+ items = new ConcurrentHashMap<>(databaseItems.size());
+ }
+ Set<Long> databaseItemIds = new HashSet<>();
+ for (T item : databaseItems) {
+ databaseItemIds.add(item.getId());
+ if (items.containsKey(item.getId())) {
+ updateCachedItem(item);
+ } else {
+ addNewItem(item);
+ }
+ }
+ for (Long cachedItemId : items.keySet()) {
+ if (!databaseItemIds.contains(cachedItemId)) {
+ removeCachedItem(cachedItemId);
+ }
+ }
+ } catch (SQLException error) {
+ Log.warning(error);
+ }
+ }
+ }
+
+ protected void addNewItem(T item) {
+ items.put(item.getId(), item);
+ }
+
+ public void addItem(T item) throws SQLException {
+ dataManager.addObject(item);
+ addNewItem(item);
+ }
+
+ protected void updateCachedItem(T item) {
+ items.put(item.getId(), item);
+ }
+
+ public void updateItem(T item) throws SQLException {
+ dataManager.updateObject(item);
+ updateCachedItem(item);
+ }
+
+ protected void removeCachedItem(long itemId) {
+ items.remove(itemId);
+ }
+
+ public void removeItem(long itemId) throws SQLException {
+ BaseModel item = getById(itemId);
+ if (item != null) {
+ dataManager.removeObject(baseClass, itemId);
+ removeCachedItem(itemId);
+ }
+ }
+
+ public final Collection<T> getItems(Set<Long> itemIds) {
+ Collection<T> result = new LinkedList<>();
+ for (long itemId : itemIds) {
+ result.add(getById(itemId));
+ }
+ return result;
+ }
+
+ public Set<Long> getAllItems() {
+ return items.keySet();
+ }
+
+}
diff --git a/src/org/traccar/database/CalendarManager.java b/src/org/traccar/database/CalendarManager.java
index 31d484327..44ced1082 100644
--- a/src/org/traccar/database/CalendarManager.java
+++ b/src/org/traccar/database/CalendarManager.java
@@ -16,107 +16,12 @@
*/
package org.traccar.database;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.traccar.Context;
-import org.traccar.helper.Log;
import org.traccar.model.Calendar;
-import org.traccar.model.CalendarPermission;
-
-public class CalendarManager {
-
- private final DataManager dataManager;
- private final Map<Long, Calendar> calendars = new ConcurrentHashMap<>();
- private final Map<Long, Set<Long>> userCalendars = new ConcurrentHashMap<>();
+public class CalendarManager extends SimpleObjectManager<Calendar> {
public CalendarManager(DataManager dataManager) {
- this.dataManager = dataManager;
- refreshCalendars();
- }
-
- public final void refreshCalendars() {
- if (dataManager != null) {
- try {
- calendars.clear();
- for (Calendar calendar : dataManager.getCalendars()) {
- calendars.put(calendar.getId(), calendar);
- }
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
- refreshUserCalendars();
- }
-
- private Set<Long> getUserCalendarIds(long userId) {
- if (!userCalendars.containsKey(userId)) {
- userCalendars.put(userId, new HashSet<Long>());
- }
- return userCalendars.get(userId);
- }
-
- public Collection<Calendar> getUserCalendars(long userId) {
- ArrayList<Calendar> result = new ArrayList<>();
- for (long calendarId : getUserCalendarIds(userId)) {
- result.add(calendars.get(calendarId));
- }
- return result;
- }
-
- public Collection<Calendar> getManagedCalendars(long userId) {
- ArrayList<Calendar> result = new ArrayList<>();
- result.addAll(getUserCalendars(userId));
- for (long managedUserId : Context.getPermissionsManager().getUserPermissions(userId)) {
- result.addAll(getUserCalendars(managedUserId));
- }
- return result;
- }
-
- public final void refreshUserCalendars() {
- if (dataManager != null) {
- try {
- userCalendars.clear();
- for (CalendarPermission calendarsPermission : dataManager.getCalendarPermissions()) {
- getUserCalendarIds(calendarsPermission.getUserId()).add(calendarsPermission.getCalendarId());
- }
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
- }
-
- public Calendar getCalendar(long calendarId) {
- return calendars.get(calendarId);
- }
-
- public final void addCalendar(Calendar calendar) throws SQLException {
- dataManager.addCalendar(calendar);
- calendars.put(calendar.getId(), calendar);
- }
-
- public final void updateCalendar(Calendar calendar) throws SQLException {
- dataManager.updateCalendar(calendar);
- calendars.put(calendar.getId(), calendar);
- }
-
- public final void removeCalendar(long calendarId) throws SQLException {
- dataManager.removeCalendar(calendarId);
- calendars.remove(calendarId);
- refreshUserCalendars();
+ super(dataManager, Calendar.class);
}
- public Collection<Calendar> getAllCalendars() {
- return calendars.values();
- }
-
- public boolean checkCalendar(long userId, long calendarId) {
- return getUserCalendarIds(userId).contains(calendarId);
- }
}
diff --git a/src/org/traccar/database/CommandsManager.java b/src/org/traccar/database/CommandsManager.java
new file mode 100644
index 000000000..9ceb995ef
--- /dev/null
+++ b/src/org/traccar/database/CommandsManager.java
@@ -0,0 +1,149 @@
+/*
+ * 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.database;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.traccar.BaseProtocol;
+import org.traccar.Context;
+import org.traccar.helper.Log;
+import org.traccar.model.Command;
+import org.traccar.model.Typed;
+import org.traccar.model.Position;
+
+public class CommandsManager extends ExtendedObjectManager<Command> {
+
+ private final Map<Long, Queue<Command>> deviceQueues = new ConcurrentHashMap<>();
+
+ public CommandsManager(DataManager dataManager) {
+ super(dataManager, Command.class);
+ }
+
+ public boolean checkDeviceCommand(long deviceId, long commandId) {
+ return !getAllDeviceItems(deviceId).contains(commandId);
+ }
+
+ public boolean sendCommand(Command command) throws Exception {
+ long deviceId = command.getDeviceId();
+ if (command.getId() != 0) {
+ command = getById(command.getId()).clone();
+ command.setDeviceId(deviceId);
+ }
+ if (command.getTextChannel()) {
+ Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
+ String phone = Context.getIdentityManager().getById(deviceId).getPhone();
+ if (lastPosition != null) {
+ BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
+ protocol.sendTextCommand(phone, command);
+ } else if (command.getType().equals(Command.TYPE_CUSTOM)) {
+ if (Context.getSmppManager() != null) {
+ Context.getSmppManager().sendMessageSync(phone, command.getString(Command.KEY_DATA), true);
+ } else {
+ throw new RuntimeException("SMPP client is not enabled");
+ }
+ } else {
+ throw new RuntimeException("Command " + command.getType() + " is not supported");
+ }
+ } else {
+ ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(deviceId);
+ if (activeDevice != null) {
+ activeDevice.sendCommand(command);
+ } else {
+ getDeviceQueue(deviceId).add(command);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public Collection<Long> getSupportedCommands(long deviceId) {
+ List<Long> result = new ArrayList<>();
+ Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
+ for (long commandId : getAllDeviceItems(deviceId)) {
+ Command command = getById(commandId);
+ if (lastPosition != null) {
+ BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
+ if (command.getTextChannel() && protocol.getSupportedTextCommands().contains(command.getType())
+ || !command.getTextChannel()
+ && protocol.getSupportedDataCommands().contains(command.getType())) {
+ result.add(commandId);
+ }
+ } else if (command.getType().equals(Command.TYPE_CUSTOM)) {
+ result.add(commandId);
+ }
+ }
+ return result;
+ }
+
+ public Collection<Typed> getCommandTypes(long deviceId, boolean textChannel) {
+ List<Typed> result = new ArrayList<>();
+ Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
+ if (lastPosition != null) {
+ BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
+ Collection<String> commands;
+ commands = textChannel ? protocol.getSupportedTextCommands() : protocol.getSupportedDataCommands();
+ for (String commandKey : commands) {
+ result.add(new Typed(commandKey));
+ }
+ } else {
+ result.add(new Typed(Command.TYPE_CUSTOM));
+ }
+ return result;
+ }
+
+ public Collection<Typed> getAllCommandTypes() {
+ List<Typed> result = new ArrayList<>();
+ Field[] fields = Command.class.getDeclaredFields();
+ for (Field field : fields) {
+ if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) {
+ try {
+ result.add(new Typed(field.get(null).toString()));
+ } catch (IllegalArgumentException | IllegalAccessException error) {
+ Log.warning(error);
+ }
+ }
+ }
+ return result;
+ }
+
+ private Queue<Command> getDeviceQueue(long deviceId) {
+ if (!deviceQueues.containsKey(deviceId)) {
+ deviceQueues.put(deviceId, new ConcurrentLinkedQueue<Command>());
+ }
+ return deviceQueues.get(deviceId);
+ }
+
+ public void sendQueuedCommands(ActiveDevice activeDevice) {
+ Queue<Command> deviceQueue = deviceQueues.get(activeDevice.getDeviceId());
+ if (deviceQueue != null) {
+ Command command = deviceQueue.poll();
+ while (command != null) {
+ activeDevice.sendCommand(command);
+ command = deviceQueue.poll();
+ }
+ }
+ }
+
+}
diff --git a/src/org/traccar/database/ConnectionManager.java b/src/org/traccar/database/ConnectionManager.java
index 0baafb578..e5a7a272f 100644
--- a/src/org/traccar/database/ConnectionManager.java
+++ b/src/org/traccar/database/ConnectionManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,14 +21,17 @@ import org.jboss.netty.util.TimerTask;
import org.traccar.Context;
import org.traccar.GlobalTimer;
import org.traccar.Protocol;
+import org.traccar.events.OverspeedEventHandler;
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 java.net.SocketAddress;
import java.sql.SQLException;
import java.util.Date;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -41,6 +44,7 @@ public class ConnectionManager {
private final long deviceTimeout;
private final boolean enableStatusEvents;
+ private final boolean updateDeviceState;
private final Map<Long, ActiveDevice> activeDevices = new ConcurrentHashMap<>();
private final Map<Long, Set<UpdateListener>> listeners = new ConcurrentHashMap<>();
@@ -49,10 +53,13 @@ public class ConnectionManager {
public ConnectionManager() {
deviceTimeout = Context.getConfig().getLong("status.timeout", DEFAULT_TIMEOUT) * 1000;
enableStatusEvents = Context.getConfig().getBoolean("event.enable");
+ updateDeviceState = Context.getConfig().getBoolean("status.updateDeviceState");
}
public void addActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) {
- activeDevices.put(deviceId, new ActiveDevice(deviceId, protocol, channel, remoteAddress));
+ ActiveDevice activeDevice = new ActiveDevice(deviceId, protocol, channel, remoteAddress);
+ activeDevices.put(deviceId, activeDevice);
+ Context.getCommandsManager().sendQueuedCommands(activeDevice);
}
public void removeActiveDevice(Channel channel) {
@@ -70,7 +77,7 @@ public class ConnectionManager {
}
public void updateDevice(final long deviceId, String status, Date time) {
- Device device = Context.getIdentityManager().getDeviceById(deviceId);
+ Device device = Context.getIdentityManager().getById(deviceId);
if (device == null) {
return;
}
@@ -80,21 +87,26 @@ public class ConnectionManager {
if (enableStatusEvents && !status.equals(oldStatus)) {
String eventType;
+ Map<Event, Position> events = new HashMap<>();
switch (status) {
case Device.STATUS_ONLINE:
eventType = Event.TYPE_DEVICE_ONLINE;
break;
case Device.STATUS_UNKNOWN:
eventType = Event.TYPE_DEVICE_UNKNOWN;
+ if (updateDeviceState) {
+ events.putAll(updateDeviceState(deviceId));
+ }
break;
default:
eventType = Event.TYPE_DEVICE_OFFLINE;
+ if (updateDeviceState) {
+ events.putAll(updateDeviceState(deviceId));
+ }
break;
}
- Event event = new Event(eventType, deviceId);
- if (Context.getNotificationManager() != null) {
- Context.getNotificationManager().updateEvent(event, null);
- }
+ events.put(new Event(eventType, deviceId), null);
+ Context.getNotificationManager().updateEvents(events);
}
Timeout timeout = timeouts.remove(deviceId);
@@ -112,6 +124,7 @@ public class ConnectionManager {
public void run(Timeout timeout) throws Exception {
if (!timeout.isCancelled()) {
updateDevice(deviceId, Device.STATUS_UNKNOWN, null);
+ activeDevices.remove(deviceId);
}
}
}, deviceTimeout, TimeUnit.MILLISECONDS));
@@ -126,6 +139,24 @@ public class ConnectionManager {
updateDevice(device);
}
+ public Map<Event, Position> updateDeviceState(long deviceId) {
+ DeviceState deviceState = Context.getDeviceManager().getDeviceState(deviceId);
+ Map<Event, Position> result = new HashMap<>();
+
+ Map<Event, Position> event = Context.getMotionEventHandler().updateMotionState(deviceState);
+ if (event != null) {
+ result.putAll(event);
+ }
+
+ event = Context.getOverspeedEventHandler().updateOverspeedState(deviceState, Context.getDeviceManager().
+ lookupAttributeDouble(deviceId, OverspeedEventHandler.ATTRIBUTE_SPEED_LIMIT, 0, false));
+ if (event != null) {
+ result.putAll(event);
+ }
+
+ 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/DataManager.java b/src/org/traccar/database/DataManager.java
index dd65289e4..e88ff7f0d 100644
--- a/src/org/traccar/database/DataManager.java
+++ b/src/org/traccar/database/DataManager.java
@@ -15,14 +15,18 @@
*/
package org.traccar.database;
+import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
import javax.naming.InitialContext;
import javax.sql.DataSource;
@@ -37,38 +41,40 @@ import liquibase.resource.ResourceAccessor;
import org.traccar.Config;
import org.traccar.helper.Log;
-import org.traccar.model.AttributeAlias;
-import org.traccar.model.AttributePermission;
-import org.traccar.model.Calendar;
-import org.traccar.model.CalendarPermission;
import org.traccar.model.Attribute;
import org.traccar.model.Device;
-import org.traccar.model.DeviceAttribute;
-import org.traccar.model.DevicePermission;
+import org.traccar.model.Driver;
import org.traccar.model.Event;
import org.traccar.model.Geofence;
import org.traccar.model.Group;
-import org.traccar.model.GroupAttribute;
-import org.traccar.model.GroupGeofence;
-import org.traccar.model.GroupPermission;
+import org.traccar.model.ManagedUser;
import org.traccar.model.Notification;
+import org.traccar.model.Permission;
+import org.traccar.model.BaseModel;
+import org.traccar.model.Calendar;
+import org.traccar.model.Command;
import org.traccar.model.Position;
import org.traccar.model.Server;
import org.traccar.model.Statistics;
import org.traccar.model.User;
-import org.traccar.model.UserPermission;
-import org.traccar.model.DeviceGeofence;
-import org.traccar.model.GeofencePermission;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
public class DataManager {
+ public static final String ACTION_SELECT_ALL = "selectAll";
+ public static final String ACTION_SELECT = "select";
+ public static final String ACTION_INSERT = "insert";
+ public static final String ACTION_UPDATE = "update";
+ public static final String ACTION_DELETE = "delete";
+
private final Config config;
private DataSource dataSource;
+ private boolean generateQueries;
+
public DataManager(Config config) throws Exception {
this.config = config;
@@ -117,11 +123,83 @@ public class DataManager {
hikariConfig.setMaximumPoolSize(maxPoolSize);
}
+ generateQueries = config.getBoolean("database.generateQueries");
+
dataSource = new HikariDataSource(hikariConfig);
}
}
+ public static String constructObjectQuery(String action, Class<?> clazz, boolean extended) {
+ switch (action) {
+ case ACTION_INSERT:
+ case ACTION_UPDATE:
+ StringBuilder result = new StringBuilder();
+ StringBuilder fields = new StringBuilder();
+ StringBuilder values = new StringBuilder();
+
+ Set<Method> methods = new HashSet<>(Arrays.asList(clazz.getMethods()));
+ methods.removeAll(Arrays.asList(Object.class.getMethods()));
+ methods.removeAll(Arrays.asList(BaseModel.class.getMethods()));
+ for (Method method : methods) {
+ boolean skip;
+ if (extended) {
+ skip = !method.isAnnotationPresent(QueryExtended.class);
+ } else {
+ skip = method.isAnnotationPresent(QueryIgnore.class)
+ || method.isAnnotationPresent(QueryExtended.class) && !action.equals(ACTION_INSERT);
+ }
+ if (!skip && method.getName().startsWith("get") && method.getParameterTypes().length == 0) {
+ String name = Introspector.decapitalize(method.getName().substring(3));
+ if (action.equals(ACTION_INSERT)) {
+ fields.append(name).append(", ");
+ values.append(":").append(name).append(", ");
+ } else {
+ fields.append(name).append(" = :").append(name).append(", ");
+ }
+ }
+ }
+ fields.setLength(fields.length() - 2);
+ if (action.equals(ACTION_INSERT)) {
+ values.setLength(values.length() - 2);
+ result.append("INSERT INTO ").append(getObjectsTableName(clazz)).append(" (");
+ result.append(fields).append(") ");
+ result.append("VALUES (").append(values).append(")");
+ } else {
+ result.append("UPDATE ").append(getObjectsTableName(clazz)).append(" SET ");
+ result.append(fields);
+ result.append(" WHERE id = :id");
+ }
+ return result.toString();
+ case ACTION_SELECT_ALL:
+ return "SELECT * FROM " + getObjectsTableName(clazz);
+ case ACTION_SELECT:
+ return "SELECT * FROM " + getObjectsTableName(clazz) + " WHERE id = :id";
+ case ACTION_DELETE:
+ return "DELETE FROM " + getObjectsTableName(clazz) + " WHERE id = :id";
+ default:
+ throw new IllegalArgumentException("Unknown action");
+ }
+ }
+
+ public static String constructPermissionQuery(String action, Class<?> owner, Class<?> property) {
+ switch (action) {
+ case ACTION_SELECT_ALL:
+ return "SELECT " + makeNameId(owner) + ", " + makeNameId(property) + " FROM "
+ + getPermissionsTableName(owner, property);
+ case ACTION_INSERT:
+ return "INSERT INTO " + getPermissionsTableName(owner, property)
+ + " (" + makeNameId(owner) + ", " + makeNameId(property) + ") VALUES (:"
+ + makeNameId(owner) + ", :" + makeNameId(property) + ")";
+ case ACTION_DELETE:
+ return "DELETE FROM " + getPermissionsTableName(owner, property)
+ + " WHERE " + makeNameId(owner) + " = :" + makeNameId(owner)
+ + " AND " + makeNameId(property) + " = :" + makeNameId(property);
+ default:
+ throw new IllegalArgumentException("Unknown action");
+ }
+ }
+
private String getQuery(String key) {
String query = config.getString(key);
if (query == null) {
@@ -130,6 +208,73 @@ public class DataManager {
return query;
}
+ public String getQuery(String action, Class<?> clazz) {
+ return getQuery(action, clazz, false);
+ }
+
+ public String getQuery(String action, Class<?> clazz, boolean extended) {
+ String queryName;
+ if (action.equals(ACTION_SELECT_ALL)) {
+ queryName = "database.select" + clazz.getSimpleName() + "s";
+ } else {
+ queryName = "database." + action.toLowerCase() + clazz.getSimpleName();
+ if (extended) {
+ queryName += "Extended";
+ }
+ }
+ String query = config.getString(queryName);
+ if (query == null) {
+ if (generateQueries) {
+ query = constructObjectQuery(action, clazz, extended);
+ config.setString(queryName, query);
+ } else {
+ Log.info("Query not provided: " + queryName);
+ }
+ }
+
+ return query;
+ }
+
+ public String getQuery(String action, Class<?> owner, Class<?> property) {
+ String queryName;
+ if (action.equals(ACTION_SELECT_ALL)) {
+ queryName = "database.select" + owner.getSimpleName() + property.getSimpleName() + "s";
+ } else if (action.equals(ACTION_INSERT)) {
+ queryName = "database.link" + owner.getSimpleName() + property.getSimpleName();
+ } else {
+ queryName = "database.unlink" + owner.getSimpleName() + property.getSimpleName();
+ }
+ String query = config.getString(queryName);
+ if (query == null) {
+ if (generateQueries) {
+ query = constructPermissionQuery(action, owner,
+ property.equals(User.class) ? ManagedUser.class : property);
+ config.setString(queryName, query);
+ } else {
+ Log.info("Query not provided: " + queryName);
+ }
+ }
+
+ return query;
+ }
+
+ private static String getPermissionsTableName(Class<?> owner, Class<?> property) {
+ String propertyName = property.getSimpleName();
+ if (propertyName.equals("ManagedUser")) {
+ propertyName = "User";
+ }
+ return Introspector.decapitalize(owner.getSimpleName()) + "_" + Introspector.decapitalize(propertyName);
+ }
+
+ private static String getObjectsTableName(Class<?> clazz) {
+ String result = Introspector.decapitalize(clazz.getSimpleName());
+ // Add "s" ending if object name is not plural already
+ if (!result.endsWith("s")) {
+ result += "s";
+ }
+ return result;
+ }
+
private void initDatabaseSchema() throws SQLException, LiquibaseException {
if (config.hasKey("database.changelog")) {
@@ -153,7 +298,7 @@ public class DataManager {
public User login(String email, String password) throws SQLException {
User user = QueryBuilder.create(dataSource, getQuery("database.loginUser"))
- .setString("email", email)
+ .setString("email", email.trim())
.executeQuerySingle(User.class);
if (user != null && user.isPasswordValid(password)) {
return user;
@@ -162,124 +307,12 @@ public class DataManager {
}
}
- public Collection<User> getUsers() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectUsersAll"))
- .executeQuery(User.class);
- }
-
- public void addUser(User user) throws SQLException {
- user.setId(QueryBuilder.create(dataSource, getQuery("database.insertUser"), true)
- .setObject(user)
- .executeUpdate());
- }
-
- public void updateUser(User user) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.updateUser"))
- .setObject(user)
- .executeUpdate();
- if (user.getHashedPassword() != null) {
- QueryBuilder.create(dataSource, getQuery("database.updateUserPassword"))
- .setObject(user)
- .executeUpdate();
- }
- }
-
- public void removeUser(long userId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.deleteUser"))
- .setLong("id", userId)
- .executeUpdate();
- }
-
- public Collection<DevicePermission> getDevicePermissions() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectDevicePermissions"))
- .executeQuery(DevicePermission.class);
- }
-
- public Collection<GroupPermission> getGroupPermissions() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectGroupPermissions"))
- .executeQuery(GroupPermission.class);
- }
-
- public Collection<Device> getAllDevices() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectDevicesAll"))
- .executeQuery(Device.class);
- }
-
- public void addDevice(Device device) throws SQLException {
- device.setId(QueryBuilder.create(dataSource, getQuery("database.insertDevice"), true)
- .setObject(device)
- .executeUpdate());
- }
-
- public void updateDevice(Device device) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.updateDevice"))
- .setObject(device)
- .executeUpdate();
- }
-
public void updateDeviceStatus(Device device) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.updateDeviceStatus"))
+ QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, Device.class, true))
.setObject(device)
.executeUpdate();
}
- public void removeDevice(long deviceId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.deleteDevice"))
- .setLong("id", deviceId)
- .executeUpdate();
- }
-
- public void linkDevice(long userId, long deviceId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.linkDevice"))
- .setLong("userId", userId)
- .setLong("deviceId", deviceId)
- .executeUpdate();
- }
-
- public void unlinkDevice(long userId, long deviceId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.unlinkDevice"))
- .setLong("userId", userId)
- .setLong("deviceId", deviceId)
- .executeUpdate();
- }
-
- public Collection<Group> getAllGroups() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectGroupsAll"))
- .executeQuery(Group.class);
- }
-
- public void addGroup(Group group) throws SQLException {
- group.setId(QueryBuilder.create(dataSource, getQuery("database.insertGroup"), true)
- .setObject(group)
- .executeUpdate());
- }
-
- public void updateGroup(Group group) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.updateGroup"))
- .setObject(group)
- .executeUpdate();
- }
-
- public void removeGroup(long groupId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.deleteGroup"))
- .setLong("id", groupId)
- .executeUpdate();
- }
-
- public void linkGroup(long userId, long groupId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.linkGroup"))
- .setLong("userId", userId)
- .setLong("groupId", groupId)
- .executeUpdate();
- }
-
- public void unlinkGroup(long userId, long groupId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.unlinkGroup"))
- .setLong("userId", userId)
- .setLong("groupId", groupId)
- .executeUpdate();
- }
-
public Collection<Position> getPositions(long deviceId, Date from, Date to) throws SQLException {
return QueryBuilder.create(dataSource, getQuery("database.selectPositions"))
.setLong("deviceId", deviceId)
@@ -288,16 +321,10 @@ public class DataManager {
.executeQuery(Position.class);
}
- public Position getPosition(long positionId) throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectPosition"))
- .setLong("id", positionId)
- .executeQuerySingle(Position.class);
- }
-
public void addPosition(Position position) throws SQLException {
- position.setId(QueryBuilder.create(dataSource, getQuery("database.insertPosition"), true)
- .setDate("now", new Date())
+ position.setId(QueryBuilder.create(dataSource, getQuery(ACTION_INSERT, Position.class), true)
.setObject(position)
+ .setDate("serverTime", new Date())
.executeUpdate());
}
@@ -328,28 +355,10 @@ public class DataManager {
}
public Server getServer() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectServers"))
+ return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, Server.class))
.executeQuerySingle(Server.class);
}
- public void updateServer(Server server) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.updateServer"))
- .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, Date from, Date to) throws SQLException {
return QueryBuilder.create(dataSource, getQuery("database.selectEvents"))
.setLong("deviceId", deviceId)
@@ -358,132 +367,6 @@ public class DataManager {
.executeQuery(Event.class);
}
- public Collection<Geofence> getGeofences() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectGeofencesAll"))
- .executeQuery(Geofence.class);
- }
-
- public void addGeofence(Geofence geofence) throws SQLException {
- geofence.setId(QueryBuilder.create(dataSource, getQuery("database.insertGeofence"), true)
- .setObject(geofence)
- .executeUpdate());
- }
-
- public void updateGeofence(Geofence geofence) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.updateGeofence"))
- .setObject(geofence)
- .executeUpdate();
- }
-
- public void removeGeofence(long geofenceId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.deleteGeofence"))
- .setLong("id", geofenceId)
- .executeUpdate();
- }
-
- public Collection<GeofencePermission> getGeofencePermissions() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectGeofencePermissions"))
- .executeQuery(GeofencePermission.class);
- }
-
- public void linkGeofence(long userId, long geofenceId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.linkGeofence"))
- .setLong("userId", userId)
- .setLong("geofenceId", geofenceId)
- .executeUpdate();
- }
-
- public void unlinkGeofence(long userId, long geofenceId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.unlinkGeofence"))
- .setLong("userId", userId)
- .setLong("geofenceId", geofenceId)
- .executeUpdate();
- }
-
- public Collection<GroupGeofence> getGroupGeofences() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectGroupGeofences"))
- .executeQuery(GroupGeofence.class);
- }
-
- public void linkGroupGeofence(long groupId, long geofenceId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.linkGroupGeofence"))
- .setLong("groupId", groupId)
- .setLong("geofenceId", geofenceId)
- .executeUpdate();
- }
-
- public void unlinkGroupGeofence(long groupId, long geofenceId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.unlinkGroupGeofence"))
- .setLong("groupId", groupId)
- .setLong("geofenceId", geofenceId)
- .executeUpdate();
- }
-
- public Collection<DeviceGeofence> getDeviceGeofences() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectDeviceGeofences"))
- .executeQuery(DeviceGeofence.class);
- }
-
- public void linkDeviceGeofence(long deviceId, long geofenceId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.linkDeviceGeofence"))
- .setLong("deviceId", deviceId)
- .setLong("geofenceId", geofenceId)
- .executeUpdate();
- }
-
- public void unlinkDeviceGeofence(long deviceId, long geofenceId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.unlinkDeviceGeofence"))
- .setLong("deviceId", deviceId)
- .setLong("geofenceId", geofenceId)
- .executeUpdate();
- }
-
- public Collection<Notification> getNotifications() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectNotifications"))
- .executeQuery(Notification.class);
- }
-
- public void addNotification(Notification notification) throws SQLException {
- notification.setId(QueryBuilder.create(dataSource, getQuery("database.insertNotification"), true)
- .setObject(notification)
- .executeUpdate());
- }
-
- public void updateNotification(Notification notification) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.updateNotification"))
- .setObject(notification)
- .executeUpdate();
- }
-
- public void removeNotification(Notification notification) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.deleteNotification"))
- .setLong("id", notification.getId())
- .executeUpdate();
- }
-
- public Collection<AttributeAlias> getAttributeAliases() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectAttributeAliases"))
- .executeQuery(AttributeAlias.class);
- }
-
- public void addAttributeAlias(AttributeAlias attributeAlias) throws SQLException {
- attributeAlias.setId(QueryBuilder.create(dataSource, getQuery("database.insertAttributeAlias"), true)
- .setObject(attributeAlias)
- .executeUpdate());
- }
-
- public void updateAttributeAlias(AttributeAlias attributeAlias) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.updateAttributeAlias"))
- .setObject(attributeAlias)
- .executeUpdate();
- }
-
- public void removeAttributeAlias(long attributeAliasId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.deleteAttributeAlias"))
- .setLong("id", attributeAliasId)
- .executeUpdate();
- }
-
public Collection<Statistics> getStatistics(Date from, Date to) throws SQLException {
return QueryBuilder.create(dataSource, getQuery("database.selectStatistics"))
.setDate("from", from)
@@ -491,150 +374,83 @@ public class DataManager {
.executeQuery(Statistics.class);
}
- public void addStatistics(Statistics statistics) throws SQLException {
- statistics.setId(QueryBuilder.create(dataSource, getQuery("database.insertStatistics"), true)
- .setObject(statistics)
- .executeUpdate());
- }
-
- public Collection<Calendar> getCalendars() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectCalendarsAll"))
- .executeQuery(Calendar.class);
- }
-
- public void addCalendar(Calendar calendar) throws SQLException {
- calendar.setId(QueryBuilder.create(dataSource, getQuery("database.insertCalendar"), true)
- .setObject(calendar)
- .executeUpdate());
- }
-
- public void updateCalendar(Calendar calendar) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.updateCalendar"))
- .setObject(calendar)
- .executeUpdate();
- }
-
- public void removeCalendar(long calendarId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.deleteCalendar"))
- .setLong("id", calendarId)
- .executeUpdate();
- }
-
- public Collection<CalendarPermission> getCalendarPermissions() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectCalendarPermissions"))
- .executeQuery(CalendarPermission.class);
- }
-
- public void linkCalendar(long userId, long calendarId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.linkCalendar"))
- .setLong("userId", userId)
- .setLong("calendarId", calendarId)
- .executeUpdate();
+ public static Class<?> getClassByName(String name) throws ClassNotFoundException {
+ switch (name.toLowerCase().replace("id", "")) {
+ case "device":
+ return Device.class;
+ case "group":
+ return Group.class;
+ case "user":
+ return User.class;
+ case "manageduser":
+ return ManagedUser.class;
+ case "geofence":
+ return Geofence.class;
+ case "driver":
+ return Driver.class;
+ case "attribute":
+ return Attribute.class;
+ case "calendar":
+ return Calendar.class;
+ case "command":
+ return Command.class;
+ case "notification":
+ return Notification.class;
+ default:
+ throw new ClassNotFoundException();
+ }
}
- public void unlinkCalendar(long userId, long calendarId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.unlinkCalendar"))
- .setLong("userId", userId)
- .setLong("calendarId", calendarId)
- .executeUpdate();
+ private static String makeNameId(Class<?> clazz) {
+ String name = clazz.getSimpleName();
+ return Introspector.decapitalize(name) + (!name.contains("Id") ? "Id" : "");
}
- public Collection<UserPermission> getUserPermissions() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectUserPermissions"))
- .executeQuery(UserPermission.class);
+ public Collection<Permission> getPermissions(Class<? extends BaseModel> owner, Class<? extends BaseModel> property)
+ throws SQLException, ClassNotFoundException {
+ return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, owner, property))
+ .executePermissionsQuery();
}
- public void linkUser(long userId, long managedUserId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.linkUser"))
- .setLong("userId", userId)
- .setLong("managedUserId", managedUserId)
+ public void linkObject(Class<?> owner, long ownerId, Class<?> property, long propertyId, boolean link)
+ throws SQLException {
+ QueryBuilder.create(dataSource, getQuery(link ? ACTION_INSERT : ACTION_DELETE, owner, property))
+ .setLong(makeNameId(owner), ownerId)
+ .setLong(makeNameId(property), propertyId)
.executeUpdate();
}
- public void unlinkUser(long userId, long managedUserId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.unlinkUser"))
- .setLong("userId", userId)
- .setLong("managedUserId", managedUserId)
- .executeUpdate();
+ public <T extends BaseModel> T getObject(Class<T> clazz, long entityId) throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT, clazz))
+ .setLong("id", entityId)
+ .executeQuerySingle(clazz);
}
- public Collection<Attribute> getAttributes() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectAttributes"))
- .executeQuery(Attribute.class);
+ public <T extends BaseModel> Collection<T> getObjects(Class<T> clazz) throws SQLException {
+ return QueryBuilder.create(dataSource, getQuery(ACTION_SELECT_ALL, clazz))
+ .executeQuery(clazz);
}
- public void addAttribute(Attribute attribute) throws SQLException {
- attribute.setId(QueryBuilder.create(dataSource, getQuery("database.insertAttribute"), true)
- .setObject(attribute)
+ public void addObject(BaseModel entity) throws SQLException {
+ entity.setId(QueryBuilder.create(dataSource, getQuery(ACTION_INSERT, entity.getClass()), true)
+ .setObject(entity)
.executeUpdate());
}
- public void updateAttribute(Attribute attribute) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.updateAttribute"))
- .setObject(attribute)
- .executeUpdate();
- }
-
- public void removeAttribute(long computedAttributeId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.deleteAttribute"))
- .setLong("id", computedAttributeId)
- .executeUpdate();
- }
-
- public Collection<AttributePermission> getAttributePermissions() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectAttributePermissions"))
- .executeQuery(AttributePermission.class);
- }
-
- public void linkAttribute(long userId, long attributeId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.linkAttribute"))
- .setLong("userId", userId)
- .setLong("attributeId", attributeId)
- .executeUpdate();
- }
-
- public void unlinkAttribute(long userId, long attributeId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.unlinkAttribute"))
- .setLong("userId", userId)
- .setLong("attributeId", attributeId)
- .executeUpdate();
- }
-
- public Collection<GroupAttribute> getGroupAttributes() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectGroupAttributes"))
- .executeQuery(GroupAttribute.class);
- }
-
- public void linkGroupAttribute(long groupId, long attributeId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.linkGroupAttribute"))
- .setLong("groupId", groupId)
- .setLong("attributeId", attributeId)
- .executeUpdate();
- }
-
- public void unlinkGroupAttribute(long groupId, long attributeId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.unlinkGroupAttribute"))
- .setLong("groupId", groupId)
- .setLong("attributeId", attributeId)
- .executeUpdate();
- }
-
- public Collection<DeviceAttribute> getDeviceAttributes() throws SQLException {
- return QueryBuilder.create(dataSource, getQuery("database.selectDeviceAttributes"))
- .executeQuery(DeviceAttribute.class);
- }
-
- public void linkDeviceAttribute(long deviceId, long attributeId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.linkDeviceAttribute"))
- .setLong("deviceId", deviceId)
- .setLong("attributeId", attributeId)
+ public void updateObject(BaseModel entity) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, entity.getClass()))
+ .setObject(entity)
.executeUpdate();
+ if (entity instanceof User && ((User) entity).getHashedPassword() != null) {
+ QueryBuilder.create(dataSource, getQuery(ACTION_UPDATE, User.class, true))
+ .setObject(entity)
+ .executeUpdate();
+ }
}
- public void unlinkDeviceAttribute(long deviceId, long attributeId) throws SQLException {
- QueryBuilder.create(dataSource, getQuery("database.unlinkDeviceAttribute"))
- .setLong("deviceId", deviceId)
- .setLong("attributeId", attributeId)
+ public void removeObject(Class<? extends BaseModel> clazz, long entityId) throws SQLException {
+ QueryBuilder.create(dataSource, getQuery(ACTION_DELETE, clazz))
+ .setLong("id", entityId)
.executeUpdate();
}
diff --git a/src/org/traccar/database/DeviceManager.java b/src/org/traccar/database/DeviceManager.java
index f2a2dd565..1eb90b7eb 100644
--- a/src/org/traccar/database/DeviceManager.java
+++ b/src/org/traccar/database/DeviceManager.java
@@ -16,10 +16,8 @@
package org.traccar.database;
import java.sql.SQLException;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -27,137 +25,56 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
-import org.traccar.BaseProtocol;
import org.traccar.Config;
import org.traccar.Context;
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;
import org.traccar.model.Server;
-public class DeviceManager implements IdentityManager {
+public class DeviceManager extends BaseObjectManager<Device> implements IdentityManager, ManagableObjects {
public static final long DEFAULT_REFRESH_DELAY = 300;
private final Config config;
- private final DataManager dataManager;
private final long dataRefreshDelay;
private boolean lookupGroupsAttribute;
- private Map<Long, Device> devicesById;
private Map<String, Device> devicesByUniqueId;
private Map<String, Device> devicesByPhone;
private AtomicLong devicesLastUpdate = new AtomicLong();
- private Map<Long, Group> groupsById;
- private AtomicLong groupsLastUpdate = new AtomicLong();
-
private final Map<Long, Position> positions = new ConcurrentHashMap<>();
- private boolean fallbackToText;
+ private final Map<Long, DeviceState> deviceStates = new ConcurrentHashMap<>();
public DeviceManager(DataManager dataManager) {
- this.dataManager = dataManager;
+ super(dataManager, Device.class);
this.config = Context.getConfig();
+ if (devicesByPhone == null) {
+ devicesByPhone = new ConcurrentHashMap<>();
+ }
+ if (devicesByUniqueId == null) {
+ devicesByUniqueId = new ConcurrentHashMap<>();
+ }
dataRefreshDelay = config.getLong("database.refreshDelay", DEFAULT_REFRESH_DELAY) * 1000;
lookupGroupsAttribute = config.getBoolean("deviceManager.lookupGroupsAttribute");
- fallbackToText = config.getBoolean("command.fallbackToSms");
- if (dataManager != null) {
- try {
- updateGroupCache(true);
- updateDeviceCache(true);
- for (Position position : dataManager.getLatestPositions()) {
- positions.put(position.getDeviceId(), position);
- }
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
+ refreshLastPositions();
}
private void updateDeviceCache(boolean force) throws SQLException {
-
long lastUpdate = devicesLastUpdate.get();
if ((force || System.currentTimeMillis() - lastUpdate > dataRefreshDelay)
&& devicesLastUpdate.compareAndSet(lastUpdate, System.currentTimeMillis())) {
- GeofenceManager geofenceManager = Context.getGeofenceManager();
- Collection<Device> databaseDevices = dataManager.getAllDevices();
- if (devicesById == null) {
- devicesById = new ConcurrentHashMap<>(databaseDevices.size());
- }
- if (devicesByUniqueId == null) {
- devicesByUniqueId = new ConcurrentHashMap<>(databaseDevices.size());
- }
- if (devicesByPhone == null) {
- devicesByPhone = new ConcurrentHashMap<>(databaseDevices.size());
- }
- Set<Long> databaseDevicesIds = new HashSet<>();
- Set<String> databaseDevicesUniqueIds = new HashSet<>();
- Set<String> databaseDevicesPhones = new HashSet<>();
- for (Device device : databaseDevices) {
- databaseDevicesIds.add(device.getId());
- databaseDevicesUniqueIds.add(device.getUniqueId());
- databaseDevicesPhones.add(device.getPhone());
- if (devicesById.containsKey(device.getId())) {
- Device cachedDevice = devicesById.get(device.getId());
- cachedDevice.setName(device.getName());
- cachedDevice.setGroupId(device.getGroupId());
- cachedDevice.setCategory(device.getCategory());
- cachedDevice.setContact(device.getContact());
- cachedDevice.setModel(device.getModel());
- cachedDevice.setAttributes(device.getAttributes());
- if (!device.getUniqueId().equals(cachedDevice.getUniqueId())) {
- devicesByUniqueId.put(device.getUniqueId(), cachedDevice);
- }
- cachedDevice.setUniqueId(device.getUniqueId());
- if (device.getPhone() != null && !device.getPhone().isEmpty()
- && !device.getPhone().equals(cachedDevice.getPhone())) {
- devicesByPhone.put(device.getPhone(), cachedDevice);
- }
- cachedDevice.setPhone(device.getPhone());
- } else {
- devicesById.put(device.getId(), device);
- devicesByUniqueId.put(device.getUniqueId(), device);
- if (device.getPhone() != null && !device.getPhone().isEmpty()) {
- devicesByPhone.put(device.getPhone(), device);
- }
- if (geofenceManager != null) {
- Position lastPosition = getLastPosition(device.getId());
- if (lastPosition != null) {
- device.setGeofenceIds(geofenceManager.getCurrentDeviceGeofences(lastPosition));
- }
- }
- }
- }
- for (Iterator<Long> iterator = devicesById.keySet().iterator(); iterator.hasNext();) {
- if (!databaseDevicesIds.contains(iterator.next())) {
- iterator.remove();
- }
- }
- for (Iterator<String> iterator = devicesByUniqueId.keySet().iterator(); iterator.hasNext();) {
- if (!databaseDevicesUniqueIds.contains(iterator.next())) {
- iterator.remove();
- }
- }
- for (Iterator<String> iterator = devicesByPhone.keySet().iterator(); iterator.hasNext();) {
- if (!databaseDevicesPhones.contains(iterator.next())) {
- iterator.remove();
- }
- }
+ refreshItems();
}
}
@Override
- public Device getDeviceById(long id) {
- return devicesById.get(id);
- }
-
- @Override
- public Device getDeviceByUniqueId(String uniqueId) throws SQLException {
+ public Device getByUniqueId(String uniqueId) throws SQLException {
boolean forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean("database.ignoreUnknown");
updateDeviceCache(forceUpdate);
@@ -169,76 +86,127 @@ public class DeviceManager implements IdentityManager {
return devicesByPhone.get(phone);
}
+ @Override
+ public Set<Long> getAllItems() {
+ Set<Long> result = super.getAllItems();
+ if (result.isEmpty()) {
+ try {
+ updateDeviceCache(true);
+ } catch (SQLException e) {
+ Log.warning(e);
+ }
+ result = super.getAllItems();
+ }
+ return result;
+ }
+
public Collection<Device> getAllDevices() {
- boolean forceUpdate = devicesById.isEmpty();
+ return getItems(getAllItems());
+ }
- try {
- updateDeviceCache(forceUpdate);
- } catch (SQLException e) {
- Log.warning(e);
+ @Override
+ public Set<Long> getUserItems(long userId) {
+ if (Context.getPermissionsManager() != null) {
+ return Context.getPermissionsManager().getDevicePermissions(userId);
+ } else {
+ return new HashSet<>();
}
-
- return devicesById.values();
}
- public Collection<Device> getDevices(long userId) throws SQLException {
- Collection<Device> devices = new ArrayList<>();
- for (long id : Context.getPermissionsManager().getDevicePermissions(userId)) {
- devices.add(devicesById.get(id));
+ @Override
+ public Set<Long> getManagedItems(long userId) {
+ Set<Long> result = new HashSet<>();
+ result.addAll(getUserItems(userId));
+ for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
+ result.addAll(getUserItems(managedUserId));
}
- return devices;
+ return result;
}
- public Collection<Device> getManagedDevices(long userId) throws SQLException {
- Collection<Device> devices = new HashSet<>();
- devices.addAll(getDevices(userId));
- for (long managedUserId : Context.getPermissionsManager().getUserPermissions(userId)) {
- devices.addAll(getDevices(managedUserId));
+ private void putUniqueDeviceId(Device device) {
+ if (devicesByUniqueId == null) {
+ devicesByUniqueId = new ConcurrentHashMap<>(getAllItems().size());
}
- return devices;
+ devicesByUniqueId.put(device.getUniqueId(), device);
}
- public void addDevice(Device device) throws SQLException {
- dataManager.addDevice(device);
+ private void putPhone(Device device) {
+ if (devicesByPhone == null) {
+ devicesByPhone = new ConcurrentHashMap<>(getAllItems().size());
+ }
+ devicesByPhone.put(device.getPhone(), device);
+ }
- devicesById.put(device.getId(), device);
- devicesByUniqueId.put(device.getUniqueId(), device);
+ @Override
+ protected void addNewItem(Device device) {
+ super.addNewItem(device);
+ putUniqueDeviceId(device);
if (device.getPhone() != null && !device.getPhone().isEmpty()) {
- devicesByPhone.put(device.getPhone(), device);
+ putPhone(device);
+ }
+ if (Context.getGeofenceManager() != null) {
+ Position lastPosition = getLastPosition(device.getId());
+ if (lastPosition != null) {
+ device.setGeofenceIds(Context.getGeofenceManager().getCurrentDeviceGeofences(lastPosition));
+ }
}
}
- public void updateDevice(Device device) throws SQLException {
- dataManager.updateDevice(device);
+ @Override
+ protected void updateCachedItem(Device device) {
+ Device cachedDevice = getById(device.getId());
+ cachedDevice.setName(device.getName());
+ cachedDevice.setGroupId(device.getGroupId());
+ cachedDevice.setCategory(device.getCategory());
+ cachedDevice.setContact(device.getContact());
+ cachedDevice.setModel(device.getModel());
+ cachedDevice.setAttributes(device.getAttributes());
+ if (!device.getUniqueId().equals(cachedDevice.getUniqueId())) {
+ devicesByUniqueId.remove(cachedDevice.getUniqueId());
+ cachedDevice.setUniqueId(device.getUniqueId());
+ putUniqueDeviceId(cachedDevice);
+ }
+ if (device.getPhone() != null && !device.getPhone().isEmpty()
+ && !device.getPhone().equals(cachedDevice.getPhone())) {
+ devicesByPhone.remove(cachedDevice.getPhone());
+ cachedDevice.setPhone(device.getPhone());
+ putPhone(cachedDevice);
+ }
+ }
- devicesById.put(device.getId(), device);
- devicesByUniqueId.put(device.getUniqueId(), device);
- if (device.getPhone() != null && !device.getPhone().isEmpty()) {
- devicesByPhone.put(device.getPhone(), device);
+ @Override
+ protected void removeCachedItem(long deviceId) {
+ Device cachedDevice = getById(deviceId);
+ if (cachedDevice != null) {
+ String deviceUniqueId = cachedDevice.getUniqueId();
+ String phone = cachedDevice.getPhone();
+ super.removeCachedItem(deviceId);
+ devicesByUniqueId.remove(deviceUniqueId);
+ if (phone != null && !phone.isEmpty()) {
+ devicesByPhone.remove(phone);
+ }
}
+ positions.remove(deviceId);
}
public void updateDeviceStatus(Device device) throws SQLException {
- dataManager.updateDeviceStatus(device);
- if (devicesById.containsKey(device.getId())) {
- Device cachedDevice = devicesById.get(device.getId());
+ getDataManager().updateDeviceStatus(device);
+ Device cachedDevice = getById(device.getId());
+ if (cachedDevice != null) {
cachedDevice.setStatus(device.getStatus());
}
}
- public void removeDevice(long deviceId) throws SQLException {
- dataManager.removeDevice(deviceId);
-
- if (devicesById.containsKey(deviceId)) {
- String deviceUniqueId = devicesById.get(deviceId).getUniqueId();
- String phone = devicesById.get(deviceId).getPhone();
- devicesById.remove(deviceId);
- devicesByUniqueId.remove(deviceUniqueId);
- if (phone != null && !phone.isEmpty()) {
- devicesByPhone.remove(phone);
+ private void refreshLastPositions() {
+ if (getDataManager() != null) {
+ try {
+ for (Position position : getDataManager().getLatestPositions()) {
+ positions.put(position.getDeviceId(), position);
+ }
+ } catch (SQLException error) {
+ Log.warning(error);
}
}
- positions.remove(deviceId);
}
public boolean isLatestPosition(Position position) {
@@ -250,10 +218,11 @@ public class DeviceManager implements IdentityManager {
if (isLatestPosition(position)) {
- dataManager.updateLatestPosition(position);
+ getDataManager().updateLatestPosition(position);
- if (devicesById.containsKey(position.getDeviceId())) {
- devicesById.get(position.getDeviceId()).setPositionId(position.getId());
+ Device device = getById(position.getDeviceId());
+ if (device != null) {
+ device.setPositionId(position.getId());
}
positions.put(position.getDeviceId(), position);
@@ -274,7 +243,7 @@ public class DeviceManager implements IdentityManager {
List<Position> result = new LinkedList<>();
if (Context.getPermissionsManager() != null) {
- for (long deviceId : Context.getPermissionsManager().getDevicePermissions(userId)) {
+ for (long deviceId : getUserItems(userId)) {
if (positions.containsKey(deviceId)) {
result.add(positions.get(deviceId));
}
@@ -284,154 +253,62 @@ public class DeviceManager implements IdentityManager {
return result;
}
- private void updateGroupCache(boolean force) throws SQLException {
-
- long lastUpdate = groupsLastUpdate.get();
- if ((force || System.currentTimeMillis() - lastUpdate > dataRefreshDelay)
- && groupsLastUpdate.compareAndSet(lastUpdate, System.currentTimeMillis())) {
- Collection<Group> databaseGroups = dataManager.getAllGroups();
- if (groupsById == null) {
- groupsById = new ConcurrentHashMap<>(databaseGroups.size());
- }
- Set<Long> databaseGroupsIds = new HashSet<>();
- for (Group group : databaseGroups) {
- databaseGroupsIds.add(group.getId());
- if (groupsById.containsKey(group.getId())) {
- Group cachedGroup = groupsById.get(group.getId());
- cachedGroup.setName(group.getName());
- cachedGroup.setGroupId(group.getGroupId());
- } else {
- groupsById.put(group.getId(), group);
- }
- }
- for (Long cachedGroupId : groupsById.keySet()) {
- if (!databaseGroupsIds.contains(cachedGroupId)) {
- devicesById.remove(cachedGroupId);
- }
- }
- databaseGroupsIds.clear();
- }
- }
-
- public Group getGroupById(long id) {
- return groupsById.get(id);
- }
-
- public Collection<Group> getAllGroups() {
- boolean forceUpdate = groupsById.isEmpty();
-
- try {
- updateGroupCache(forceUpdate);
- } catch (SQLException e) {
- Log.warning(e);
- }
-
- return groupsById.values();
- }
-
- public Collection<Group> getGroups(long userId) throws SQLException {
- Collection<Group> groups = new ArrayList<>();
- for (long id : Context.getPermissionsManager().getGroupPermissions(userId)) {
- groups.add(getGroupById(id));
- }
- return groups;
- }
-
- public Collection<Group> getManagedGroups(long userId) throws SQLException {
- Collection<Group> groups = new ArrayList<>();
- groups.addAll(getGroups(userId));
- for (long managedUserId : Context.getPermissionsManager().getUserPermissions(userId)) {
- groups.addAll(getGroups(managedUserId));
- }
- return groups;
- }
-
- private void checkGroupCycles(Group group) {
- Set<Long> groups = new HashSet<>();
- while (group != null) {
- if (groups.contains(group.getId())) {
- throw new IllegalArgumentException("Cycle in group hierarchy");
- }
- groups.add(group.getId());
- group = groupsById.get(group.getGroupId());
- }
- }
-
- public void addGroup(Group group) throws SQLException {
- checkGroupCycles(group);
- dataManager.addGroup(group);
- groupsById.put(group.getId(), group);
- }
-
- public void updateGroup(Group group) throws SQLException {
- checkGroupCycles(group);
- dataManager.updateGroup(group);
- groupsById.put(group.getId(), group);
- }
-
- public void removeGroup(long groupId) throws SQLException {
- dataManager.removeGroup(groupId);
- groupsById.remove(groupId);
- }
-
public boolean lookupAttributeBoolean(
long deviceId, String attributeName, boolean defaultValue, boolean lookupConfig) {
- String result = lookupAttribute(deviceId, attributeName, lookupConfig);
+ Object result = lookupAttribute(deviceId, attributeName, lookupConfig);
if (result != null) {
- return Boolean.parseBoolean(result);
+ return result instanceof String ? Boolean.parseBoolean((String) result) : (Boolean) result;
}
return defaultValue;
}
public String lookupAttributeString(
long deviceId, String attributeName, String defaultValue, boolean lookupConfig) {
- String result = lookupAttribute(deviceId, attributeName, lookupConfig);
- if (result != null) {
- return result;
- }
- return defaultValue;
+ Object result = lookupAttribute(deviceId, attributeName, lookupConfig);
+ return result != null ? (String) result : defaultValue;
}
public int lookupAttributeInteger(long deviceId, String attributeName, int defaultValue, boolean lookupConfig) {
- String result = lookupAttribute(deviceId, attributeName, lookupConfig);
+ Object result = lookupAttribute(deviceId, attributeName, lookupConfig);
if (result != null) {
- return Integer.parseInt(result);
+ return result instanceof String ? Integer.parseInt((String) result) : ((Number) result).intValue();
}
return defaultValue;
}
public long lookupAttributeLong(
long deviceId, String attributeName, long defaultValue, boolean lookupConfig) {
- String result = lookupAttribute(deviceId, attributeName, lookupConfig);
+ Object result = lookupAttribute(deviceId, attributeName, lookupConfig);
if (result != null) {
- return Long.parseLong(result);
+ return result instanceof String ? Long.parseLong((String) result) : ((Number) result).longValue();
}
return defaultValue;
}
public double lookupAttributeDouble(
long deviceId, String attributeName, double defaultValue, boolean lookupConfig) {
- String result = lookupAttribute(deviceId, attributeName, lookupConfig);
+ Object result = lookupAttribute(deviceId, attributeName, lookupConfig);
if (result != null) {
- return Double.parseDouble(result);
+ return result instanceof String ? Double.parseDouble((String) result) : ((Number) result).doubleValue();
}
return defaultValue;
}
- private String lookupAttribute(long deviceId, String attributeName, boolean lookupConfig) {
- String result = null;
- Device device = getDeviceById(deviceId);
+ private Object lookupAttribute(long deviceId, String attributeName, boolean lookupConfig) {
+ Object result = null;
+ Device device = getById(deviceId);
if (device != null) {
- result = device.getString(attributeName);
+ result = device.getAttributes().get(attributeName);
if (result == null && lookupGroupsAttribute) {
long groupId = device.getGroupId();
while (groupId != 0) {
- if (getGroupById(groupId) != null) {
- result = getGroupById(groupId).getString(attributeName);
+ Group group = Context.getGroupsManager().getById(groupId);
+ if (group != null) {
+ result = group.getAttributes().get(attributeName);
if (result != null) {
break;
}
- groupId = getGroupById(groupId).getGroupId();
+ groupId = group.getGroupId();
} else {
groupId = 0;
}
@@ -442,7 +319,7 @@ public class DeviceManager implements IdentityManager {
result = Context.getConfig().getString(attributeName);
} else {
Server server = Context.getPermissionsManager().getServer();
- result = server.getString(attributeName);
+ result = server.getAttributes().get(attributeName);
}
}
}
@@ -453,54 +330,24 @@ public class DeviceManager implements IdentityManager {
Position last = positions.get(deviceTotalDistance.getDeviceId());
if (last != null) {
last.getAttributes().put(Position.KEY_TOTAL_DISTANCE, deviceTotalDistance.getTotalDistance());
- dataManager.addPosition(last);
+ getDataManager().addPosition(last);
updateLatestPosition(last);
} else {
throw new IllegalArgumentException();
}
}
- public void sendCommand(Command command) throws Exception {
- long deviceId = command.getDeviceId();
- if (command.getTextChannel()) {
- Position lastPosition = getLastPosition(deviceId);
- if (lastPosition != null) {
- BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
- protocol.sendTextCommand(devicesById.get(deviceId).getPhone(), command);
- } else if (command.getType().equals(Command.TYPE_CUSTOM)) {
- Context.getSmppManager().sendMessageSync(devicesById.get(deviceId).getPhone(),
- command.getString(Command.KEY_DATA), true);
- } else {
- throw new RuntimeException("Command " + command.getType() + " is not supported");
- }
- } else {
- ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(deviceId);
- if (activeDevice != null) {
- activeDevice.sendCommand(command);
- } else {
- if (fallbackToText) {
- command.setTextChannel(true);
- sendCommand(command);
- } else {
- throw new RuntimeException("Device is not online");
- }
- }
+ public DeviceState getDeviceState(long deviceId) {
+ DeviceState deviceState = deviceStates.get(deviceId);
+ if (deviceState == null) {
+ deviceState = new DeviceState();
+ deviceStates.put(deviceId, deviceState);
}
+ return deviceState;
}
- public Collection<CommandType> getCommandTypes(long deviceId, boolean textChannel) {
- List<CommandType> result = new ArrayList<>();
- Position lastPosition = Context.getDeviceManager().getLastPosition(deviceId);
- if (lastPosition != null) {
- BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
- Collection<String> commands;
- commands = textChannel ? protocol.getSupportedTextCommands() : protocol.getSupportedDataCommands();
- for (String commandKey : commands) {
- result.add(new CommandType(commandKey));
- }
- } else {
- result.add(new CommandType(Command.TYPE_CUSTOM));
- }
- return result;
+ public void setDeviceState(long deviceId, DeviceState deviceState) {
+ deviceStates.put(deviceId, deviceState);
}
+
}
diff --git a/src/org/traccar/database/DriversManager.java b/src/org/traccar/database/DriversManager.java
new file mode 100644
index 000000000..930951460
--- /dev/null
+++ b/src/org/traccar/database/DriversManager.java
@@ -0,0 +1,73 @@
+/*
+ * 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.database;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.traccar.model.Driver;
+
+public class DriversManager extends ExtendedObjectManager<Driver> {
+
+ private Map<String, Driver> driversByUniqueId;
+
+ public DriversManager(DataManager dataManager) {
+ super(dataManager, Driver.class);
+ if (driversByUniqueId == null) {
+ driversByUniqueId = new ConcurrentHashMap<>();
+ }
+ }
+
+ private void putUniqueDriverId(Driver driver) {
+ if (driversByUniqueId == null) {
+ driversByUniqueId = new ConcurrentHashMap<>(getAllItems().size());
+ }
+ driversByUniqueId.put(driver.getUniqueId(), driver);
+ }
+
+ @Override
+ protected void addNewItem(Driver driver) {
+ super.addNewItem(driver);
+ putUniqueDriverId(driver);
+ }
+
+ @Override
+ protected void updateCachedItem(Driver driver) {
+ Driver cachedDriver = getById(driver.getId());
+ cachedDriver.setName(driver.getName());
+ if (!driver.getUniqueId().equals(cachedDriver.getUniqueId())) {
+ driversByUniqueId.remove(cachedDriver.getUniqueId());
+ cachedDriver.setUniqueId(driver.getUniqueId());
+ putUniqueDriverId(cachedDriver);
+ }
+ cachedDriver.setAttributes(driver.getAttributes());
+ }
+
+ @Override
+ protected void removeCachedItem(long driverId) {
+ Driver cachedDriver = getById(driverId);
+ if (cachedDriver != null) {
+ String driverUniqueId = cachedDriver.getUniqueId();
+ super.removeCachedItem(driverId);
+ driversByUniqueId.remove(driverUniqueId);
+ }
+ }
+
+ public Driver getDriverByUniqueId(String uniqueId) {
+ return driversByUniqueId.get(uniqueId);
+ }
+}
diff --git a/src/org/traccar/database/ExtendedObjectManager.java b/src/org/traccar/database/ExtendedObjectManager.java
new file mode 100644
index 000000000..16785cb37
--- /dev/null
+++ b/src/org/traccar/database/ExtendedObjectManager.java
@@ -0,0 +1,112 @@
+/*
+ * 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.database;
+
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.traccar.Context;
+import org.traccar.helper.Log;
+import org.traccar.model.Device;
+import org.traccar.model.Group;
+import org.traccar.model.Permission;
+import org.traccar.model.BaseModel;
+
+public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleObjectManager<T> {
+
+ private final Map<Long, Set<Long>> deviceItems = new ConcurrentHashMap<>();
+ private final Map<Long, Set<Long>> deviceItemsWithGroups = new ConcurrentHashMap<>();
+ private final Map<Long, Set<Long>> groupItems = new ConcurrentHashMap<>();
+
+ protected ExtendedObjectManager(DataManager dataManager, Class<T> baseClass) {
+ super(dataManager, baseClass);
+ refreshExtendedPermissions();
+ }
+
+ public final Set<Long> getGroupItems(long groupId) {
+ if (!groupItems.containsKey(groupId)) {
+ groupItems.put(groupId, new HashSet<Long>());
+ }
+ return groupItems.get(groupId);
+ }
+
+ public final Set<Long> getDeviceItems(long deviceId) {
+ if (!deviceItems.containsKey(deviceId)) {
+ deviceItems.put(deviceId, new HashSet<Long>());
+ }
+ return deviceItems.get(deviceId);
+ }
+
+ public Set<Long> getAllDeviceItems(long deviceId) {
+ if (!deviceItemsWithGroups.containsKey(deviceId)) {
+ deviceItemsWithGroups.put(deviceId, new HashSet<Long>());
+ }
+ return deviceItemsWithGroups.get(deviceId);
+ }
+
+ @Override
+ public void removeItem(long itemId) throws SQLException {
+ super.removeItem(itemId);
+ refreshExtendedPermissions();
+ }
+
+ public void refreshExtendedPermissions() {
+ if (getDataManager() != null) {
+ try {
+
+ Collection<Permission> databaseGroupPermissions =
+ getDataManager().getPermissions(Group.class, getBaseClass());
+
+ groupItems.clear();
+ for (Permission groupPermission : databaseGroupPermissions) {
+ getGroupItems(groupPermission.getOwnerId()).add(groupPermission.getPropertyId());
+ }
+
+ Collection<Permission> databaseDevicePermissions =
+ getDataManager().getPermissions(Device.class, getBaseClass());
+
+ deviceItems.clear();
+ deviceItemsWithGroups.clear();
+
+ for (Permission devicePermission : databaseDevicePermissions) {
+ getDeviceItems(devicePermission.getOwnerId()).add(devicePermission.getPropertyId());
+ getAllDeviceItems(devicePermission.getOwnerId()).add(devicePermission.getPropertyId());
+ }
+
+ for (Device device : Context.getDeviceManager().getAllDevices()) {
+ long groupId = device.getGroupId();
+ while (groupId != 0) {
+ getAllDeviceItems(device.getId()).addAll(getGroupItems(groupId));
+ Group group = (Group) Context.getGroupsManager().getById(groupId);
+ if (group != null) {
+ groupId = group.getGroupId();
+ } else {
+ groupId = 0;
+ }
+ }
+ }
+
+ } catch (SQLException | ClassNotFoundException error) {
+ Log.warning(error);
+ }
+ }
+ }
+}
diff --git a/src/org/traccar/database/GeofenceManager.java b/src/org/traccar/database/GeofenceManager.java
index b8e6a5d73..a32847cf9 100644
--- a/src/org/traccar/database/GeofenceManager.java
+++ b/src/org/traccar/database/GeofenceManager.java
@@ -15,290 +15,52 @@
*/
package org.traccar.database;
-import java.sql.SQLException;
import java.util.ArrayList;
-import java.util.Collection;
-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.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.traccar.Context;
-import org.traccar.helper.Log;
import org.traccar.model.Device;
-import org.traccar.model.DeviceGeofence;
import org.traccar.model.Geofence;
-import org.traccar.model.GeofencePermission;
-import org.traccar.model.GroupGeofence;
import org.traccar.model.Position;
-public class GeofenceManager {
-
- private final DataManager dataManager;
-
- private final Map<Long, Geofence> geofences = new HashMap<>();
- private final Map<Long, Set<Long>> userGeofences = new HashMap<>();
- private final Map<Long, Set<Long>> groupGeofences = new HashMap<>();
-
- private final Map<Long, Set<Long>> deviceGeofencesWithGroups = new HashMap<>();
- private final Map<Long, Set<Long>> deviceGeofences = new HashMap<>();
-
- private final ReadWriteLock deviceGeofencesLock = new ReentrantReadWriteLock();
- private final ReadWriteLock geofencesLock = new ReentrantReadWriteLock();
- private final ReadWriteLock groupGeofencesLock = new ReentrantReadWriteLock();
- private final ReadWriteLock userGeofencesLock = new ReentrantReadWriteLock();
+public class GeofenceManager extends ExtendedObjectManager<Geofence> {
public GeofenceManager(DataManager dataManager) {
- this.dataManager = dataManager;
- refreshGeofences();
- }
-
- private Set<Long> getUserGeofences(long userId) {
- if (!userGeofences.containsKey(userId)) {
- userGeofences.put(userId, new HashSet<Long>());
- }
- return userGeofences.get(userId);
- }
-
- public Set<Long> getUserGeofencesIds(long userId) {
- userGeofencesLock.readLock().lock();
- try {
- return getUserGeofences(userId);
- } finally {
- userGeofencesLock.readLock().unlock();
- }
- }
-
- private Set<Long> getGroupGeofences(long groupId) {
- if (!groupGeofences.containsKey(groupId)) {
- groupGeofences.put(groupId, new HashSet<Long>());
- }
- return groupGeofences.get(groupId);
- }
-
- public Set<Long> getGroupGeofencesIds(long groupId) {
- groupGeofencesLock.readLock().lock();
- try {
- return getGroupGeofences(groupId);
- } finally {
- groupGeofencesLock.readLock().unlock();
- }
- }
-
- public Set<Long> getAllDeviceGeofences(long deviceId) {
- deviceGeofencesLock.readLock().lock();
- try {
- return getDeviceGeofences(deviceGeofencesWithGroups, deviceId);
- } finally {
- deviceGeofencesLock.readLock().unlock();
- }
+ super(dataManager, Geofence.class);
}
- public Set<Long> getDeviceGeofencesIds(long deviceId) {
- deviceGeofencesLock.readLock().lock();
- try {
- return getDeviceGeofences(deviceGeofences, deviceId);
- } finally {
- deviceGeofencesLock.readLock().unlock();
- }
+ @Override
+ public final void refreshExtendedPermissions() {
+ super.refreshExtendedPermissions();
+ recalculateDevicesGeofences();
}
- private Set<Long> getDeviceGeofences(Map<Long, Set<Long>> deviceGeofences, long deviceId) {
- if (!deviceGeofences.containsKey(deviceId)) {
- deviceGeofences.put(deviceId, new HashSet<Long>());
- }
- return deviceGeofences.get(deviceId);
- }
-
- public final void refreshGeofences() {
- if (dataManager != null) {
- try {
- geofencesLock.writeLock().lock();
- try {
- geofences.clear();
- for (Geofence geofence : dataManager.getGeofences()) {
- geofences.put(geofence.getId(), geofence);
- }
- } finally {
- geofencesLock.writeLock().unlock();
- }
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
- refreshUserGeofences();
- refresh();
- }
-
- public final void refreshUserGeofences() {
- if (dataManager != null) {
- try {
- userGeofencesLock.writeLock().lock();
- try {
- userGeofences.clear();
- for (GeofencePermission geofencePermission : dataManager.getGeofencePermissions()) {
- getUserGeofences(geofencePermission.getUserId()).add(geofencePermission.getGeofenceId());
- }
- } finally {
- userGeofencesLock.writeLock().unlock();
- }
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
- }
-
- public final void refresh() {
- if (dataManager != null) {
- try {
-
- Collection<GroupGeofence> databaseGroupGeofences = dataManager.getGroupGeofences();
- groupGeofencesLock.writeLock().lock();
- try {
- groupGeofences.clear();
- for (GroupGeofence groupGeofence : databaseGroupGeofences) {
- getGroupGeofences(groupGeofence.getGroupId()).add(groupGeofence.getGeofenceId());
- }
- } finally {
- groupGeofencesLock.writeLock().unlock();
- }
-
- Collection<DeviceGeofence> databaseDeviceGeofences = dataManager.getDeviceGeofences();
- Collection<Device> allDevices = Context.getDeviceManager().getAllDevices();
-
- groupGeofencesLock.readLock().lock();
- deviceGeofencesLock.writeLock().lock();
- try {
- deviceGeofences.clear();
- deviceGeofencesWithGroups.clear();
-
- for (DeviceGeofence deviceGeofence : databaseDeviceGeofences) {
- getDeviceGeofences(deviceGeofences, deviceGeofence.getDeviceId())
- .add(deviceGeofence.getGeofenceId());
- getDeviceGeofences(deviceGeofencesWithGroups, deviceGeofence.getDeviceId())
- .add(deviceGeofence.getGeofenceId());
- }
-
- for (Device device : allDevices) {
- long groupId = device.getGroupId();
- while (groupId != 0) {
- getDeviceGeofences(deviceGeofencesWithGroups,
- device.getId()).addAll(getGroupGeofences(groupId));
- if (Context.getDeviceManager().getGroupById(groupId) != null) {
- groupId = Context.getDeviceManager().getGroupById(groupId).getGroupId();
- } else {
- groupId = 0;
- }
- }
- List<Long> deviceGeofenceIds = device.getGeofenceIds();
- if (deviceGeofenceIds == null) {
- deviceGeofenceIds = new ArrayList<>();
- } else {
- deviceGeofenceIds.clear();
- }
- 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);
- if (geofence != null && geofence.getGeometry()
- .containsPoint(lastPosition.getLatitude(), lastPosition.getLongitude())) {
- deviceGeofenceIds.add(geofenceId);
- }
- }
- }
- device.setGeofenceIds(deviceGeofenceIds);
- }
-
- } finally {
- deviceGeofencesLock.writeLock().unlock();
- groupGeofencesLock.readLock().unlock();
- }
-
- } catch (SQLException error) {
- Log.warning(error);
+ public List<Long> getCurrentDeviceGeofences(Position position) {
+ List<Long> result = new ArrayList<>();
+ for (long geofenceId : getAllDeviceItems(position.getDeviceId())) {
+ Geofence geofence = getById(geofenceId);
+ if (geofence != null && geofence.getGeometry()
+ .containsPoint(position.getLatitude(), position.getLongitude())) {
+ result.add(geofenceId);
}
}
+ return result;
}
- public final Collection<Geofence> getAllGeofences() {
- geofencesLock.readLock().lock();
- try {
- return geofences.values();
- } finally {
- geofencesLock.readLock().unlock();
- }
- }
-
- public final Set<Long> getAllGeofencesIds() {
- geofencesLock.readLock().lock();
- try {
- return geofences.keySet();
- } finally {
- geofencesLock.readLock().unlock();
- }
- }
-
- public final Set<Long> getManagedGeofencesIds(long userId) {
- Set<Long> geofences = new HashSet<>();
- geofences.addAll(getUserGeofencesIds(userId));
- for (long managedUserId : Context.getPermissionsManager().getUserPermissions(userId)) {
- geofences.addAll(getUserGeofencesIds(managedUserId));
- }
- return geofences;
- }
-
- public final Collection<Geofence> getGeofences(Set<Long> geofencesIds) {
- geofencesLock.readLock().lock();
- try {
- Collection<Geofence> result = new LinkedList<>();
- for (long geofenceId : geofencesIds) {
- result.add(getGeofence(geofenceId));
+ public void recalculateDevicesGeofences() {
+ for (Device device : Context.getDeviceManager().getAllDevices()) {
+ List<Long> deviceGeofenceIds = device.getGeofenceIds();
+ if (deviceGeofenceIds == null) {
+ deviceGeofenceIds = new ArrayList<>();
+ } else {
+ deviceGeofenceIds.clear();
}
- return result;
- } finally {
- geofencesLock.readLock().unlock();
- }
- }
-
- public final Geofence getGeofence(long geofenceId) {
- geofencesLock.readLock().lock();
- try {
- return geofences.get(geofenceId);
- } finally {
- geofencesLock.readLock().unlock();
- }
- }
-
- public final void updateGeofence(Geofence geofence) {
- geofencesLock.writeLock().lock();
- try {
- geofences.put(geofence.getId(), geofence);
- } finally {
- geofencesLock.writeLock().unlock();
- }
- try {
- dataManager.updateGeofence(geofence);
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
-
- public boolean checkGeofence(long userId, long geofenceId) {
- return getUserGeofencesIds(userId).contains(geofenceId);
- }
-
- public List<Long> getCurrentDeviceGeofences(Position position) {
- List<Long> result = new ArrayList<>();
- for (long geofenceId : getAllDeviceGeofences(position.getDeviceId())) {
- if (getGeofence(geofenceId).getGeometry().containsPoint(position.getLatitude(), position.getLongitude())) {
- result.add(geofenceId);
+ Position lastPosition = Context.getIdentityManager().getLastPosition(device.getId());
+ if (lastPosition != null && getAllDeviceItems(device.getId()) != null) {
+ deviceGeofenceIds.addAll(getCurrentDeviceGeofences(lastPosition));
}
+ device.setGeofenceIds(deviceGeofenceIds);
}
- return result;
}
}
diff --git a/src/org/traccar/database/GroupsManager.java b/src/org/traccar/database/GroupsManager.java
new file mode 100644
index 000000000..c0456085b
--- /dev/null
+++ b/src/org/traccar/database/GroupsManager.java
@@ -0,0 +1,103 @@
+/*
+ * 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.database;
+
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.traccar.Context;
+import org.traccar.helper.Log;
+import org.traccar.model.Group;
+
+public class GroupsManager extends BaseObjectManager<Group> implements ManagableObjects {
+
+ private AtomicLong groupsLastUpdate = new AtomicLong();
+ private final long dataRefreshDelay;
+
+ public GroupsManager(DataManager dataManager) {
+ super(dataManager, Group.class);
+ dataRefreshDelay = Context.getConfig().getLong("database.refreshDelay",
+ DeviceManager.DEFAULT_REFRESH_DELAY) * 1000;
+ }
+
+ private void checkGroupCycles(Group group) {
+ Set<Long> groups = new HashSet<>();
+ while (group != null) {
+ if (groups.contains(group.getId())) {
+ throw new IllegalArgumentException("Cycle in group hierarchy");
+ }
+ groups.add(group.getId());
+ group = getById(group.getGroupId());
+ }
+ }
+
+ private void updateGroupCache(boolean force) throws SQLException {
+ long lastUpdate = groupsLastUpdate.get();
+ if ((force || System.currentTimeMillis() - lastUpdate > dataRefreshDelay)
+ && groupsLastUpdate.compareAndSet(lastUpdate, System.currentTimeMillis())) {
+ refreshItems();
+ }
+ }
+
+ @Override
+ public Set<Long> getAllItems() {
+ Set<Long> result = super.getAllItems();
+ if (result.isEmpty()) {
+ try {
+ updateGroupCache(true);
+ } catch (SQLException e) {
+ Log.warning(e);
+ }
+ result = super.getAllItems();
+ }
+ return result;
+ }
+
+ @Override
+ protected void addNewItem(Group group) {
+ checkGroupCycles(group);
+ super.addNewItem(group);
+ }
+
+ @Override
+ protected void updateCachedItem(Group group) {
+ checkGroupCycles(group);
+ super.updateCachedItem(group);
+ }
+
+ @Override
+ public Set<Long> getUserItems(long userId) {
+ if (Context.getPermissionsManager() != null) {
+ return Context.getPermissionsManager().getGroupPermissions(userId);
+ } else {
+ return new HashSet<>();
+ }
+ }
+
+ @Override
+ public Set<Long> getManagedItems(long userId) {
+ Set<Long> result = new HashSet<>();
+ result.addAll(getUserItems(userId));
+ for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
+ result.addAll(getUserItems(managedUserId));
+ }
+ return result;
+ }
+
+}
diff --git a/src/org/traccar/database/IdentityManager.java b/src/org/traccar/database/IdentityManager.java
index c8c593a54..82d905963 100644
--- a/src/org/traccar/database/IdentityManager.java
+++ b/src/org/traccar/database/IdentityManager.java
@@ -20,9 +20,9 @@ import org.traccar.model.Position;
public interface IdentityManager {
- Device getDeviceById(long id);
+ Device getById(long id);
- Device getDeviceByUniqueId(String uniqueId) throws Exception;
+ Device getByUniqueId(String uniqueId) throws Exception;
Position getLastPosition(long deviceId);
diff --git a/src/org/traccar/database/ManagableObjects.java b/src/org/traccar/database/ManagableObjects.java
new file mode 100644
index 000000000..ec9549493
--- /dev/null
+++ b/src/org/traccar/database/ManagableObjects.java
@@ -0,0 +1,27 @@
+/*
+ * 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.database;
+
+import java.util.Set;
+
+public interface ManagableObjects {
+
+ Set<Long> getUserItems(long userId);
+
+ Set<Long> getManagedItems(long userId);
+
+}
diff --git a/src/org/traccar/database/NotificationManager.java b/src/org/traccar/database/NotificationManager.java
index 48caa615c..73041a23f 100644
--- a/src/org/traccar/database/NotificationManager.java
+++ b/src/org/traccar/database/NotificationManager.java
@@ -1,5 +1,6 @@
/*
* Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,56 +19,70 @@ package org.traccar.database;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
-import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.traccar.Context;
import org.traccar.helper.Log;
import org.traccar.model.Event;
import org.traccar.model.Notification;
import org.traccar.model.Position;
+import org.traccar.model.Typed;
import org.traccar.notification.NotificationMail;
import org.traccar.notification.NotificationSms;
-public class NotificationManager {
-
- private final DataManager dataManager;
-
- private final Map<Long, Set<Notification>> userNotifications = new HashMap<>();
-
- private final ReadWriteLock notificationsLock = new ReentrantReadWriteLock();
+public class NotificationManager extends ExtendedObjectManager<Notification> {
public NotificationManager(DataManager dataManager) {
- this.dataManager = dataManager;
- refresh();
+ super(dataManager, Notification.class);
+ }
+
+ private Set<Long> getEffectiveNotifications(long userId, long deviceId) {
+ Set<Long> result = new HashSet<>();
+ Set<Long> deviceNotifications = getAllDeviceItems(deviceId);
+ for (long itemId : getUserItems(userId)) {
+ if (getById(itemId).getAlways() || deviceNotifications.contains(itemId)) {
+ result.add(itemId);
+ }
+ }
+ return result;
}
public void updateEvent(Event event, Position position) {
try {
- dataManager.addEvent(event);
+ getDataManager().addObject(event);
} catch (SQLException error) {
Log.warning(error);
}
- Set<Long> users = Context.getPermissionsManager().getDeviceUsers(event.getDeviceId());
+ long deviceId = event.getDeviceId();
+ Set<Long> users = Context.getPermissionsManager().getDeviceUsers(deviceId);
for (long userId : users) {
if (event.getGeofenceId() == 0 || Context.getGeofenceManager() != null
- && Context.getGeofenceManager().checkGeofence(userId, event.getGeofenceId())) {
- Notification notification = getUserNotificationByType(userId, event.getType());
- if (notification != null) {
- if (notification.getWeb()) {
- Context.getConnectionManager().updateEvent(userId, event);
- }
- if (notification.getMail()) {
- NotificationMail.sendMailAsync(userId, event, position);
+ && Context.getGeofenceManager().checkItemPermission(userId, event.getGeofenceId())) {
+ boolean sentWeb = false;
+ boolean sentMail = false;
+ boolean sentSms = Context.getSmppManager() == null;
+ for (long notificationId : getEffectiveNotifications(userId, deviceId)) {
+ Notification notification = getById(notificationId);
+ if (getById(notificationId).getType().equals(event.getType())) {
+ if (!sentWeb && notification.getWeb()) {
+ Context.getConnectionManager().updateEvent(userId, event);
+ sentWeb = true;
+ }
+ if (!sentMail && notification.getMail()) {
+ NotificationMail.sendMailAsync(userId, event, position);
+ sentMail = true;
+ }
+ if (!sentSms && notification.getSms()) {
+ NotificationSms.sendSmsAsync(userId, event, position);
+ sentSms = true;
+ }
}
- if (notification.getSms()) {
- NotificationSms.sendSmsAsync(userId, event, position);
+ if (sentWeb && sentMail && sentSms) {
+ break;
}
}
}
@@ -77,141 +92,24 @@ public class NotificationManager {
}
}
- public void updateEvents(Collection<Event> events, Position position) {
- for (Event event : events) {
- updateEvent(event, position);
- }
- }
-
- private Set<Notification> getUserNotificationsUnsafe(long userId) {
- if (!userNotifications.containsKey(userId)) {
- userNotifications.put(userId, new HashSet<Notification>());
- }
- return userNotifications.get(userId);
- }
-
- public Set<Notification> getUserNotifications(long userId) {
- notificationsLock.readLock().lock();
- try {
- return getUserNotificationsUnsafe(userId);
- } finally {
- notificationsLock.readLock().unlock();
- }
- }
-
- public final void refresh() {
- if (dataManager != null) {
- try {
- notificationsLock.writeLock().lock();
- try {
- userNotifications.clear();
- for (Notification notification : dataManager.getNotifications()) {
- getUserNotificationsUnsafe(notification.getUserId()).add(notification);
- }
- } finally {
- notificationsLock.writeLock().unlock();
- }
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
- }
-
- public Notification getUserNotificationByType(long userId, String type) {
- notificationsLock.readLock().lock();
- try {
- for (Notification notification : getUserNotificationsUnsafe(userId)) {
- if (notification.getType().equals(type)) {
- return notification;
- }
- }
- } finally {
- notificationsLock.readLock().unlock();
- }
- return null;
- }
-
- public void updateNotification(Notification notification) {
- Notification cachedNotification = getUserNotificationByType(notification.getUserId(), notification.getType());
- if (cachedNotification != null) {
- if (cachedNotification.getWeb() != notification.getWeb()
- || cachedNotification.getMail() != notification.getMail()
- || cachedNotification.getSms() != notification.getSms()) {
- if (!notification.getWeb() && !notification.getMail() && !notification.getSms()) {
- try {
- dataManager.removeNotification(cachedNotification);
- } catch (SQLException error) {
- Log.warning(error);
- }
- notificationsLock.writeLock().lock();
- try {
- getUserNotificationsUnsafe(notification.getUserId()).remove(cachedNotification);
- } finally {
- notificationsLock.writeLock().unlock();
- }
- } else {
- notificationsLock.writeLock().lock();
- try {
- cachedNotification.setWeb(notification.getWeb());
- cachedNotification.setMail(notification.getMail());
- cachedNotification.setSms(notification.getSms());
- cachedNotification.setAttributes(notification.getAttributes());
- } finally {
- notificationsLock.writeLock().unlock();
- }
- try {
- dataManager.updateNotification(cachedNotification);
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
- } else {
- notification.setId(cachedNotification.getId());
- }
- } else if (notification.getWeb() || notification.getMail() || notification.getSms()) {
- try {
- dataManager.addNotification(notification);
- } catch (SQLException error) {
- Log.warning(error);
- }
- notificationsLock.writeLock().lock();
- try {
- getUserNotificationsUnsafe(notification.getUserId()).add(notification);
- } finally {
- notificationsLock.writeLock().unlock();
- }
+ public void updateEvents(Map<Event, Position> events) {
+ for (Entry<Event, Position> event : events.entrySet()) {
+ updateEvent(event.getKey(), event.getValue());
}
}
- public Set<Notification> getAllNotifications() {
- Set<Notification> notifications = new HashSet<>();
- long id = 1;
+ public Set<Typed> getAllNotificationTypes() {
+ Set<Typed> types = new HashSet<>();
Field[] fields = Event.class.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) {
try {
- Notification notification = new Notification();
- notification.setType(field.get(null).toString());
- notification.setId(id++);
- notifications.add(notification);
+ types.add(new Typed(field.get(null).toString()));
} catch (IllegalArgumentException | IllegalAccessException error) {
Log.warning(error);
}
}
}
- return notifications;
+ return types;
}
-
- public Collection<Notification> getAllUserNotifications(long userId) {
- Map<String, Notification> notifications = new HashMap<>();
- for (Notification notification : getAllNotifications()) {
- notification.setUserId(userId);
- notifications.put(notification.getType(), notification);
- }
- for (Notification notification : getUserNotifications(userId)) {
- notifications.put(notification.getType(), notification);
- }
- return notifications.values();
- }
-
}
diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java
index 9a82efd48..07b60ba58 100644
--- a/src/org/traccar/database/PermissionsManager.java
+++ b/src/org/traccar/database/PermissionsManager.java
@@ -17,39 +17,48 @@ package org.traccar.database;
import org.traccar.Context;
import org.traccar.helper.Log;
+import org.traccar.model.Attribute;
+import org.traccar.model.BaseModel;
+import org.traccar.model.Calendar;
+import org.traccar.model.Command;
import org.traccar.model.Device;
-import org.traccar.model.DevicePermission;
+import org.traccar.model.Driver;
+import org.traccar.model.Geofence;
import org.traccar.model.Group;
-import org.traccar.model.GroupPermission;
+import org.traccar.model.ManagedUser;
+import org.traccar.model.Notification;
+import org.traccar.model.Permission;
import org.traccar.model.Server;
import org.traccar.model.User;
-import org.traccar.model.UserPermission;
-import java.lang.reflect.Method;
import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
public class PermissionsManager {
private final DataManager dataManager;
+ private final UsersManager usersManager;
private volatile Server server;
- private final Map<Long, User> users = new ConcurrentHashMap<>();
- private final Map<String, Long> usersTokens = new HashMap<>();
-
private final Map<Long, Set<Long>> groupPermissions = new HashMap<>();
private final Map<Long, Set<Long>> devicePermissions = new HashMap<>();
private final Map<Long, Set<Long>> deviceUsers = new HashMap<>();
private final Map<Long, Set<Long>> groupDevices = new HashMap<>();
- private final Map<Long, Set<Long>> userPermissions = new HashMap<>();
+ public PermissionsManager(DataManager dataManager, UsersManager usersManager) {
+ this.dataManager = dataManager;
+ this.usersManager = usersManager;
+ refreshServer();
+ refreshDeviceAndGroupPermissions();
+ }
+
+ public User getUser(long userId) {
+ return (User) usersManager.getById(userId);
+ }
public Set<Long> getGroupPermissions(long userId) {
if (!groupPermissions.containsKey(userId)) {
@@ -79,76 +88,45 @@ public class PermissionsManager {
return groupDevices.get(groupId);
}
- public Set<Long> getUserPermissions(long userId) {
- if (!userPermissions.containsKey(userId)) {
- userPermissions.put(userId, new HashSet<Long>());
- }
- return userPermissions.get(userId);
- }
-
- public PermissionsManager(DataManager dataManager) {
- this.dataManager = dataManager;
- refreshUsers();
- refreshPermissions();
- refreshUserPermissions();
- }
-
- public final void refreshUsers() {
- users.clear();
- usersTokens.clear();
+ public void refreshServer() {
try {
server = dataManager.getServer();
- for (User user : dataManager.getUsers()) {
- users.put(user.getId(), user);
- if (user.getToken() != null) {
- usersTokens.put(user.getToken(), user.getId());
- }
- }
- } catch (SQLException error) {
- Log.warning(error);
- }
- }
-
- public final void refreshUserPermissions() {
- userPermissions.clear();
- try {
- for (UserPermission permission : dataManager.getUserPermissions()) {
- getUserPermissions(permission.getUserId()).add(permission.getManagedUserId());
- }
} catch (SQLException error) {
Log.warning(error);
}
}
- public final void refreshPermissions() {
+ public final void refreshDeviceAndGroupPermissions() {
groupPermissions.clear();
devicePermissions.clear();
try {
- GroupTree groupTree = new GroupTree(Context.getDeviceManager().getAllGroups(),
+ GroupTree groupTree = new GroupTree(Context.getGroupsManager().getItems(
+ Context.getGroupsManager().getAllItems()),
Context.getDeviceManager().getAllDevices());
- for (GroupPermission permission : dataManager.getGroupPermissions()) {
- Set<Long> userGroupPermissions = getGroupPermissions(permission.getUserId());
- Set<Long> userDevicePermissions = getDevicePermissions(permission.getUserId());
- userGroupPermissions.add(permission.getGroupId());
- for (Group group : groupTree.getGroups(permission.getGroupId())) {
+ for (Permission groupPermission : dataManager.getPermissions(User.class, Group.class)) {
+ Set<Long> userGroupPermissions = getGroupPermissions(groupPermission.getOwnerId());
+ Set<Long> userDevicePermissions = getDevicePermissions(groupPermission.getOwnerId());
+ userGroupPermissions.add(groupPermission.getPropertyId());
+ for (Group group : groupTree.getGroups(groupPermission.getPropertyId())) {
userGroupPermissions.add(group.getId());
}
- for (Device device : groupTree.getDevices(permission.getGroupId())) {
+ for (Device device : groupTree.getDevices(groupPermission.getPropertyId())) {
userDevicePermissions.add(device.getId());
}
}
- for (DevicePermission permission : dataManager.getDevicePermissions()) {
- getDevicePermissions(permission.getUserId()).add(permission.getDeviceId());
+
+ for (Permission devicePermission : dataManager.getPermissions(User.class, Device.class)) {
+ getDevicePermissions(devicePermission.getOwnerId()).add(devicePermission.getPropertyId());
}
groupDevices.clear();
- for (Group group : Context.getDeviceManager().getAllGroups()) {
- for (Device device : groupTree.getDevices(group.getId())) {
- getGroupDevices(group.getId()).add(device.getId());
+ for (long groupId : Context.getGroupsManager().getAllItems()) {
+ for (Device device : groupTree.getDevices(groupId)) {
+ getGroupDevices(groupId).add(device.getId());
}
}
- } catch (SQLException error) {
+ } catch (SQLException | ClassNotFoundException error) {
Log.warning(error);
}
@@ -160,48 +138,50 @@ public class PermissionsManager {
}
}
- public boolean isAdmin(long userId) {
- return users.containsKey(userId) && users.get(userId).getAdmin();
+ public boolean getUserAdmin(long userId) {
+ User user = getUser(userId);
+ return user != null && user.getAdmin();
}
public void checkAdmin(long userId) throws SecurityException {
- if (!isAdmin(userId)) {
+ if (!getUserAdmin(userId)) {
throw new SecurityException("Admin access required");
}
}
- public boolean isManager(long userId) {
- return users.containsKey(userId) && users.get(userId).getUserLimit() != 0;
+ public boolean getUserManager(long userId) {
+ User user = getUser(userId);
+ return user != null && user.getUserLimit() != 0;
}
public void checkManager(long userId) throws SecurityException {
- if (!isManager(userId)) {
+ if (!getUserManager(userId)) {
throw new SecurityException("Manager access required");
}
}
public void checkManager(long userId, long managedUserId) throws SecurityException {
checkManager(userId);
- if (!getUserPermissions(userId).contains(managedUserId)) {
+ if (!usersManager.getUserItems(userId).contains(managedUserId)) {
throw new SecurityException("User access denied");
}
}
public void checkUserLimit(long userId) throws SecurityException {
- int userLimit = users.get(userId).getUserLimit();
- if (userLimit != -1 && getUserPermissions(userId).size() >= userLimit) {
+ int userLimit = getUser(userId).getUserLimit();
+ if (userLimit != -1 && usersManager.getUserItems(userId).size() >= userLimit) {
throw new SecurityException("Manager user limit reached");
}
}
public void checkDeviceLimit(long userId) throws SecurityException, SQLException {
- int deviceLimit = users.get(userId).getDeviceLimit();
+ int deviceLimit = getUser(userId).getDeviceLimit();
if (deviceLimit != -1) {
int deviceCount = 0;
- if (isManager(userId)) {
- deviceCount = Context.getDeviceManager().getManagedDevices(userId).size();
+ if (getUserManager(userId)) {
+ deviceCount = Context.getDeviceManager().getManagedItems(userId).size();
} else {
- deviceCount = getDevicePermissions(userId).size();
+ deviceCount = Context.getDeviceManager().getUserItems(userId).size();
}
if (deviceCount >= deviceLimit) {
throw new SecurityException("User device limit reached");
@@ -209,28 +189,50 @@ public class PermissionsManager {
}
}
- public boolean isReadonly(long userId) {
- return users.containsKey(userId) && users.get(userId).getReadonly();
+ public boolean getUserReadonly(long userId) {
+ User user = getUser(userId);
+ return user != null && user.getReadonly();
}
- public boolean isDeviceReadonly(long userId) {
- return users.containsKey(userId) && users.get(userId).getDeviceReadonly();
+ public boolean getUserDeviceReadonly(long userId) {
+ User user = getUser(userId);
+ return user != null && user.getDeviceReadonly();
+ }
+
+ public boolean getUserLimitCommands(long userId) {
+ User user = getUser(userId);
+ return user != null && user.getLimitCommands();
}
public void checkReadonly(long userId) throws SecurityException {
- if (!isAdmin(userId) && (server.getReadonly() || isReadonly(userId))) {
+ if (!getUserAdmin(userId) && (server.getReadonly() || getUserReadonly(userId))) {
throw new SecurityException("Account is readonly");
}
}
public void checkDeviceReadonly(long userId) throws SecurityException {
- if (!isAdmin(userId) && (server.getDeviceReadonly() || isDeviceReadonly(userId))) {
+ if (!getUserAdmin(userId) && (server.getDeviceReadonly() || getUserDeviceReadonly(userId))) {
throw new SecurityException("Account is device readonly");
}
}
+ public void checkLimitCommands(long userId) throws SecurityException {
+ if (!getUserAdmin(userId) && (server.getLimitCommands() || getUserLimitCommands(userId))) {
+ throw new SecurityException("Account has limit sending commands");
+ }
+ }
+
+ public void checkUserDeviceCommand(long userId, long deviceId, long commandId) throws SecurityException {
+ if (!getUserAdmin(userId) && Context.getCommandsManager().checkDeviceCommand(deviceId, commandId)) {
+ throw new SecurityException("Command can not be sent to this device");
+ }
+ }
+
public void checkUserEnabled(long userId) throws SecurityException {
User user = getUser(userId);
+ if (user == null) {
+ throw new SecurityException("Unknown account");
+ }
if (user.getDisabled()) {
throw new SecurityException("Account is disabled");
}
@@ -245,9 +247,10 @@ public class PermissionsManager {
|| before.getUserLimit() != after.getUserLimit()) {
checkAdmin(userId);
}
- if (users.containsKey(userId) && users.get(userId).getExpirationTime() != null
+ User user = getUser(userId);
+ if (user != null && user.getExpirationTime() != null
&& (after.getExpirationTime() == null
- || users.get(userId).getExpirationTime().compareTo(after.getExpirationTime()) < 0)) {
+ || user.getExpirationTime().compareTo(after.getExpirationTime()) < 0)) {
checkAdmin(userId);
}
if (before.getReadonly() != after.getReadonly()
@@ -256,22 +259,22 @@ public class PermissionsManager {
if (userId == after.getId()) {
checkAdmin(userId);
}
- if (!isAdmin(userId)) {
+ if (!getUserAdmin(userId)) {
checkManager(userId);
}
}
}
public void checkUser(long userId, long managedUserId) throws SecurityException {
- if (userId != managedUserId && !isAdmin(userId)) {
+ if (userId != managedUserId && !getUserAdmin(userId)) {
checkManager(userId, managedUserId);
}
}
public void checkGroup(long userId, long groupId) throws SecurityException {
- if (!getGroupPermissions(userId).contains(groupId) && !isAdmin(userId)) {
+ if (!getGroupPermissions(userId).contains(groupId) && !getUserAdmin(userId)) {
checkManager(userId);
- for (long managedUserId : getUserPermissions(userId)) {
+ for (long managedUserId : usersManager.getUserItems(userId)) {
if (getGroupPermissions(managedUserId).contains(groupId)) {
return;
}
@@ -281,10 +284,10 @@ public class PermissionsManager {
}
public void checkDevice(long userId, long deviceId) throws SecurityException {
- if (!getDevicePermissions(userId).contains(deviceId) && !isAdmin(userId)) {
+ if (!Context.getDeviceManager().getUserItems(userId).contains(deviceId) && !getUserAdmin(userId)) {
checkManager(userId);
- for (long managedUserId : getUserPermissions(userId)) {
- if (getDevicePermissions(managedUserId).contains(deviceId)) {
+ for (long managedUserId : usersManager.getUserItems(userId)) {
+ if (Context.getDeviceManager().getUserItems(managedUserId).contains(deviceId)) {
return;
}
}
@@ -293,44 +296,105 @@ public class PermissionsManager {
}
public void checkRegistration(long userId) {
- if (!server.getRegistration() && !isAdmin(userId)) {
+ if (!server.getRegistration() && !getUserAdmin(userId)) {
throw new SecurityException("Registration disabled");
}
}
- public void checkGeofence(long userId, long geofenceId) throws SecurityException {
- if (!Context.getGeofenceManager().checkGeofence(userId, geofenceId) && !isAdmin(userId)) {
- checkManager(userId);
- for (long managedUserId : getUserPermissions(userId)) {
- if (Context.getGeofenceManager().checkGeofence(managedUserId, geofenceId)) {
- return;
- }
- }
- throw new SecurityException("Geofence access denied");
+ public void checkPermission(Class<?> object, long userId, long objectId)
+ throws SecurityException {
+ SimpleObjectManager<? extends BaseModel> manager = null;
+
+ if (object.equals(Device.class)) {
+ checkDevice(userId, objectId);
+ } else if (object.equals(Group.class)) {
+ checkGroup(userId, objectId);
+ } else if (object.equals(User.class) || object.equals(ManagedUser.class)) {
+ checkUser(userId, objectId);
+ } else if (object.equals(Geofence.class)) {
+ manager = Context.getGeofenceManager();
+ } else if (object.equals(Attribute.class)) {
+ manager = Context.getAttributesManager();
+ } else if (object.equals(Driver.class)) {
+ manager = Context.getDriversManager();
+ } else if (object.equals(Calendar.class)) {
+ manager = Context.getCalendarManager();
+ } else if (object.equals(Command.class)) {
+ manager = Context.getCommandsManager();
+ } else if (object.equals(Notification.class)) {
+ manager = Context.getNotificationManager();
+ } else {
+ throw new IllegalArgumentException("Unknown object type");
}
- }
- public void checkAttribute(long userId, long attributeId) throws SecurityException {
- if (!Context.getAttributesManager().checkAttribute(userId, attributeId) && !isAdmin(userId)) {
+ if (manager != null && !manager.checkItemPermission(userId, objectId) && !getUserAdmin(userId)) {
checkManager(userId);
- for (long managedUserId : getUserPermissions(userId)) {
- if (Context.getAttributesManager().checkAttribute(managedUserId, attributeId)) {
+ for (long managedUserId : usersManager.getManagedItems(userId)) {
+ if (manager.checkItemPermission(managedUserId, objectId)) {
return;
}
}
- throw new SecurityException("Attribute access denied");
- }
- }
-
- public void checkCalendar(long userId, long calendarId) throws SecurityException {
- if (!Context.getCalendarManager().checkCalendar(userId, calendarId) && !isAdmin(userId)) {
- checkManager(userId);
- for (long managedUserId : getUserPermissions(userId)) {
- if (Context.getCalendarManager().checkCalendar(managedUserId, calendarId)) {
- return;
- }
+ throw new SecurityException("Type " + object + " access denied");
+ }
+ }
+
+ public void refreshAllUsersPermissions() {
+ if (Context.getGeofenceManager() != null) {
+ Context.getGeofenceManager().refreshUserItems();
+ }
+ Context.getCalendarManager().refreshUserItems();
+ Context.getDriversManager().refreshUserItems();
+ Context.getAttributesManager().refreshUserItems();
+ Context.getCommandsManager().refreshUserItems();
+ if (Context.getNotificationManager() != null) {
+ Context.getNotificationManager().refreshUserItems();
+ }
+ }
+
+ public void refreshAllExtendedPermissions() {
+ if (Context.getGeofenceManager() != null) {
+ Context.getGeofenceManager().refreshExtendedPermissions();
+ }
+ Context.getDriversManager().refreshExtendedPermissions();
+ Context.getAttributesManager().refreshExtendedPermissions();
+ Context.getCommandsManager().refreshExtendedPermissions();
+ }
+
+ public void refreshPermissions(Permission permission) {
+ if (permission.getOwnerClass().equals(User.class)) {
+ if (permission.getPropertyClass().equals(Device.class)
+ || permission.getPropertyClass().equals(Group.class)) {
+ refreshDeviceAndGroupPermissions();
+ refreshAllExtendedPermissions();
+ } else if (permission.getPropertyClass().equals(ManagedUser.class)) {
+ usersManager.refreshUserItems();
+ } else if (permission.getPropertyClass().equals(Geofence.class) && Context.getGeofenceManager() != null) {
+ Context.getGeofenceManager().refreshUserItems();
+ } else if (permission.getPropertyClass().equals(Driver.class)) {
+ Context.getDriversManager().refreshUserItems();
+ } else if (permission.getPropertyClass().equals(Attribute.class)) {
+ Context.getAttributesManager().refreshUserItems();
+ } else if (permission.getPropertyClass().equals(Calendar.class)) {
+ Context.getCalendarManager().refreshUserItems();
+ } else if (permission.getPropertyClass().equals(Command.class)) {
+ Context.getCommandsManager().refreshUserItems();
+ } else if (permission.getPropertyClass().equals(Notification.class)
+ && Context.getNotificationManager() != null) {
+ Context.getNotificationManager().refreshUserItems();
+ }
+ } else if (permission.getOwnerClass().equals(Device.class) || permission.getOwnerClass().equals(Group.class)) {
+ if (permission.getPropertyClass().equals(Geofence.class) && Context.getGeofenceManager() != null) {
+ Context.getGeofenceManager().refreshExtendedPermissions();
+ } else if (permission.getPropertyClass().equals(Driver.class)) {
+ Context.getDriversManager().refreshExtendedPermissions();
+ } else if (permission.getPropertyClass().equals(Attribute.class)) {
+ Context.getAttributesManager().refreshExtendedPermissions();
+ } else if (permission.getPropertyClass().equals(Command.class)) {
+ Context.getCommandsManager().refreshExtendedPermissions();
+ } else if (permission.getPropertyClass().equals(Notification.class)
+ && Context.getNotificationManager() != null) {
+ Context.getNotificationManager().refreshExtendedPermissions();
}
- throw new SecurityException("Calendar access denied");
}
}
@@ -339,94 +403,23 @@ public class PermissionsManager {
}
public void updateServer(Server server) throws SQLException {
- dataManager.updateServer(server);
+ dataManager.updateObject(server);
this.server = server;
}
- public Collection<User> getAllUsers() {
- return users.values();
- }
-
- public Collection<User> getUsers(long userId) {
- Collection<User> result = new ArrayList<>();
- for (long managedUserId : getUserPermissions(userId)) {
- result.add(users.get(managedUserId));
- }
- return result;
- }
-
- public Collection<User> getManagedUsers(long userId) {
- Collection<User> result = getUsers(userId);
- result.add(users.get(userId));
- return result;
- }
-
- public User getUser(long userId) {
- return users.get(userId);
- }
-
- public void addUser(User user) throws SQLException {
- dataManager.addUser(user);
- users.put(user.getId(), user);
- if (user.getToken() != null) {
- usersTokens.put(user.getToken(), user.getId());
- }
- refreshPermissions();
- }
-
- public void updateUser(User user) throws SQLException {
- dataManager.updateUser(user);
- User old = users.get(user.getId());
- users.put(user.getId(), user);
- if (user.getToken() != null) {
- usersTokens.put(user.getToken(), user.getId());
- }
- if (old.getToken() != null && !old.getToken().equals(user.getToken())) {
- usersTokens.remove(old.getToken());
- }
- refreshPermissions();
- }
-
- public void removeUser(long userId) throws SQLException {
- dataManager.removeUser(userId);
- usersTokens.remove(users.get(userId).getToken());
- users.remove(userId);
- refreshPermissions();
- refreshUserPermissions();
- }
-
public User login(String email, String password) throws SQLException {
User user = dataManager.login(email, password);
if (user != null) {
checkUserEnabled(user.getId());
- return users.get(user.getId());
+ return getUser(user.getId());
}
return null;
}
- public User getUserByToken(String token) {
- return users.get(usersTokens.get(token));
- }
-
- public Object lookupPreference(long userId, String key, Object defaultValue) {
- String methodName = "get" + key.substring(0, 1).toUpperCase() + key.substring(1);
+ public Object lookupAttribute(long userId, String key, Object defaultValue) {
Object preference;
- Object serverPreference = null;
- Object userPreference = null;
- try {
- Method method = null;
- method = User.class.getMethod(methodName, (Class<?>[]) null);
- if (method != null) {
- userPreference = method.invoke(users.get(userId), (Object[]) null);
- }
- method = null;
- method = Server.class.getMethod(methodName, (Class<?>[]) null);
- if (method != null) {
- serverPreference = method.invoke(server, (Object[]) null);
- }
- } catch (ReflectiveOperationException | SecurityException | IllegalArgumentException exception) {
- return defaultValue;
- }
+ Object serverPreference = server.getAttributes().get(key);
+ Object userPreference = getUser(userId).getAttributes().get(key);
if (server.getForceSettings()) {
preference = serverPreference != null ? serverPreference : userPreference;
} else {
diff --git a/src/org/traccar/database/QueryBuilder.java b/src/org/traccar/database/QueryBuilder.java
index a24e6f0bf..af33458a8 100644
--- a/src/org/traccar/database/QueryBuilder.java
+++ b/src/org/traccar/database/QueryBuilder.java
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import org.traccar.Context;
import org.traccar.helper.Log;
import org.traccar.model.MiscFormatter;
+import org.traccar.model.Permission;
import javax.sql.DataSource;
import java.io.IOException;
@@ -35,6 +36,7 @@ import java.sql.Types;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -487,4 +489,28 @@ public final class QueryBuilder {
return 0;
}
+ public Collection<Permission> executePermissionsQuery() throws SQLException, ClassNotFoundException {
+ List<Permission> result = new LinkedList<>();
+ if (query != null) {
+ try {
+ try (ResultSet resultSet = statement.executeQuery()) {
+ ResultSetMetaData resultMetaData = resultSet.getMetaData();
+ while (resultSet.next()) {
+ LinkedHashMap<String, Long> map = new LinkedHashMap<>();
+ for (int i = 1; i <= resultMetaData.getColumnCount(); i++) {
+ String label = resultMetaData.getColumnLabel(i);
+ map.put(label, resultSet.getLong(label));
+ }
+ result.add(new Permission(map));
+ }
+ }
+ } finally {
+ statement.close();
+ connection.close();
+ }
+ }
+
+ return result;
+ }
+
}
diff --git a/src/org/traccar/model/UserPermission.java b/src/org/traccar/database/QueryExtended.java
index 39ead5ef1..07bc2c211 100644
--- a/src/org/traccar/model/UserPermission.java
+++ b/src/org/traccar/database/QueryExtended.java
@@ -14,28 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.traccar.model;
+package org.traccar.database;
-public class UserPermission {
-
- private long userId;
-
- public long getUserId() {
- return userId;
- }
-
- public void setUserId(long userId) {
- this.userId = userId;
- }
-
- private long managedUserId;
-
- public long getManagedUserId() {
- return managedUserId;
- }
-
- public void setManagedUserId(long managedUserId) {
- this.managedUserId = managedUserId;
- }
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface QueryExtended {
}
diff --git a/src/org/traccar/database/SimpleObjectManager.java b/src/org/traccar/database/SimpleObjectManager.java
new file mode 100644
index 000000000..0b4d11378
--- /dev/null
+++ b/src/org/traccar/database/SimpleObjectManager.java
@@ -0,0 +1,91 @@
+/*
+ * 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.database;
+
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.traccar.Context;
+import org.traccar.helper.Log;
+import org.traccar.model.BaseModel;
+import org.traccar.model.Permission;
+import org.traccar.model.User;
+
+public abstract class SimpleObjectManager<T extends BaseModel> extends BaseObjectManager<T>
+ implements ManagableObjects {
+
+ private Map<Long, Set<Long>> userItems;
+
+ protected SimpleObjectManager(DataManager dataManager, Class<T> baseClass) {
+ super(dataManager, baseClass);
+ }
+
+ @Override
+ public final Set<Long> getUserItems(long userId) {
+ if (!userItems.containsKey(userId)) {
+ userItems.put(userId, new HashSet<Long>());
+ }
+ return userItems.get(userId);
+ }
+
+ @Override
+ public Set<Long> getManagedItems(long userId) {
+ Set<Long> result = new HashSet<>();
+ result.addAll(getUserItems(userId));
+ for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
+ result.addAll(getUserItems(managedUserId));
+ }
+ return result;
+ }
+
+ public final boolean checkItemPermission(long userId, long itemId) {
+ return getUserItems(userId).contains(itemId);
+ }
+
+ @Override
+ public void refreshItems() {
+ super.refreshItems();
+ refreshUserItems();
+ }
+
+ public final void refreshUserItems() {
+ if (getDataManager() != null) {
+ try {
+ if (userItems != null) {
+ userItems.clear();
+ } else {
+ userItems = new ConcurrentHashMap<>();
+ }
+ for (Permission permission : getDataManager().getPermissions(User.class, getBaseClass())) {
+ getUserItems(permission.getOwnerId()).add(permission.getPropertyId());
+ }
+ } catch (SQLException | ClassNotFoundException error) {
+ Log.warning(error);
+ }
+ }
+ }
+
+ @Override
+ public void removeItem(long itemId) throws SQLException {
+ super.removeItem(itemId);
+ refreshUserItems();
+ }
+
+}
diff --git a/src/org/traccar/database/StatisticsManager.java b/src/org/traccar/database/StatisticsManager.java
index 5b42416ad..06a3e7b35 100644
--- a/src/org/traccar/database/StatisticsManager.java
+++ b/src/org/traccar/database/StatisticsManager.java
@@ -61,7 +61,7 @@ public class StatisticsManager {
statistics.setGeolocationRequests(geolocationRequests);
try {
- Context.getDataManager().addStatistics(statistics);
+ Context.getDataManager().addObject(statistics);
} catch (SQLException e) {
Log.warning(e);
}
diff --git a/src/org/traccar/database/UsersManager.java b/src/org/traccar/database/UsersManager.java
new file mode 100644
index 000000000..576a9e6c7
--- /dev/null
+++ b/src/org/traccar/database/UsersManager.java
@@ -0,0 +1,86 @@
+/*
+ * 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.database;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.traccar.model.User;
+
+public class UsersManager extends SimpleObjectManager<User> {
+
+ private Map<String, User> usersTokens;
+
+ public UsersManager(DataManager dataManager) {
+ super(dataManager, User.class);
+ if (usersTokens == null) {
+ usersTokens = new ConcurrentHashMap<>();
+ }
+ }
+
+ private void putToken(User user) {
+ if (usersTokens == null) {
+ usersTokens = new ConcurrentHashMap<>();
+ }
+ if (user.getToken() != null) {
+ usersTokens.put(user.getToken(), user);
+ }
+ }
+
+ @Override
+ protected void addNewItem(User user) {
+ super.addNewItem(user);
+ putToken(user);
+ }
+
+ @Override
+ protected void updateCachedItem(User user) {
+ User cachedUser = getById(user.getId());
+ super.updateCachedItem(user);
+ putToken(user);
+ if (cachedUser.getToken() != null && !cachedUser.getToken().equals(user.getToken())) {
+ usersTokens.remove(cachedUser.getToken());
+ }
+ }
+
+ @Override
+ protected void removeCachedItem(long userId) {
+ User cachedUser = getById(userId);
+ if (cachedUser != null) {
+ String userToken = cachedUser.getToken();
+ super.removeCachedItem(userId);
+ if (userToken != null) {
+ usersTokens.remove(userToken);
+ }
+ }
+ }
+
+ @Override
+ public Set<Long> getManagedItems(long userId) {
+ Set<Long> result = new HashSet<>();
+ result.addAll(getUserItems(userId));
+ result.add(userId);
+ return result;
+ }
+
+ public User getUserByToken(String token) {
+ return usersTokens.get(token);
+ }
+
+}
diff --git a/src/org/traccar/events/AlertEventHandler.java b/src/org/traccar/events/AlertEventHandler.java
index 7d0bd669b..003ccb662 100644
--- a/src/org/traccar/events/AlertEventHandler.java
+++ b/src/org/traccar/events/AlertEventHandler.java
@@ -15,8 +15,8 @@
*/
package org.traccar.events;
-import java.util.Collection;
import java.util.Collections;
+import java.util.Map;
import org.traccar.BaseEventHandler;
import org.traccar.model.Event;
@@ -25,12 +25,12 @@ import org.traccar.model.Position;
public class AlertEventHandler extends BaseEventHandler {
@Override
- protected Collection<Event> analyzePosition(Position position) {
+ protected Map<Event, Position> analyzePosition(Position position) {
Object alarm = position.getAttributes().get(Position.KEY_ALARM);
if (alarm != null) {
Event event = new Event(Event.TYPE_ALARM, position.getDeviceId(), position.getId());
event.set(Position.KEY_ALARM, (String) alarm);
- return Collections.singleton(event);
+ return Collections.singletonMap(event, position);
}
return null;
}
diff --git a/src/org/traccar/events/CommandResultEventHandler.java b/src/org/traccar/events/CommandResultEventHandler.java
index 077c389c9..775aa903f 100644
--- a/src/org/traccar/events/CommandResultEventHandler.java
+++ b/src/org/traccar/events/CommandResultEventHandler.java
@@ -15,8 +15,8 @@
*/
package org.traccar.events;
-import java.util.Collection;
import java.util.Collections;
+import java.util.Map;
import org.traccar.BaseEventHandler;
import org.traccar.model.Event;
@@ -25,12 +25,12 @@ import org.traccar.model.Position;
public class CommandResultEventHandler extends BaseEventHandler {
@Override
- protected Collection<Event> analyzePosition(Position position) {
+ protected Map<Event, Position> analyzePosition(Position position) {
Object commandResult = position.getAttributes().get(Position.KEY_RESULT);
if (commandResult != null) {
Event event = new Event(Event.TYPE_COMMAND_RESULT, position.getDeviceId(), position.getId());
event.set(Position.KEY_RESULT, (String) commandResult);
- return Collections.singleton(event);
+ return Collections.singletonMap(event, position);
}
return null;
}
diff --git a/src/org/traccar/events/DriverEventHandler.java b/src/org/traccar/events/DriverEventHandler.java
new file mode 100644
index 000000000..39b8eb9c0
--- /dev/null
+++ b/src/org/traccar/events/DriverEventHandler.java
@@ -0,0 +1,50 @@
+/*
+ * 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.events;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.traccar.BaseEventHandler;
+import org.traccar.Context;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+public class DriverEventHandler extends BaseEventHandler {
+
+ @Override
+ protected Map<Event, Position> analyzePosition(Position position) {
+ if (!Context.getIdentityManager().isLatestPosition(position)) {
+ return null;
+ }
+ String driverUniqueId = position.getString(Position.KEY_DRIVER_UNIQUE_ID);
+ if (driverUniqueId != null) {
+ String oldDriverUniqueId = null;
+ Position lastPosition = Context.getIdentityManager().getLastPosition(position.getDeviceId());
+ if (lastPosition != null) {
+ oldDriverUniqueId = lastPosition.getString(Position.KEY_DRIVER_UNIQUE_ID);
+ }
+ if (!driverUniqueId.equals(oldDriverUniqueId)) {
+ Event event = new Event(Event.TYPE_DRIVER_CHANGED, position.getDeviceId(), position.getId());
+ event.set(Position.KEY_DRIVER_UNIQUE_ID, driverUniqueId);
+ return Collections.singletonMap(event, position);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/events/FuelDropEventHandler.java b/src/org/traccar/events/FuelDropEventHandler.java
index e9a261aea..2ee3e1a58 100644
--- a/src/org/traccar/events/FuelDropEventHandler.java
+++ b/src/org/traccar/events/FuelDropEventHandler.java
@@ -21,21 +21,21 @@ import org.traccar.model.Device;
import org.traccar.model.Event;
import org.traccar.model.Position;
-import java.util.Collection;
import java.util.Collections;
+import java.util.Map;
public class FuelDropEventHandler extends BaseEventHandler {
public static final String ATTRIBUTE_FUEL_DROP_THRESHOLD = "fuelDropThreshold";
@Override
- protected Collection<Event> analyzePosition(Position position) {
+ protected Map<Event, Position> analyzePosition(Position position) {
- Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId());
+ Device device = Context.getIdentityManager().getById(position.getDeviceId());
if (device == null) {
return null;
}
- if (!Context.getIdentityManager().isLatestPosition(position) || !position.getValid()) {
+ if (!Context.getIdentityManager().isLatestPosition(position)) {
return null;
}
@@ -52,7 +52,7 @@ public class FuelDropEventHandler extends BaseEventHandler {
if (drop >= fuelDropThreshold) {
Event event = new Event(Event.TYPE_DEVICE_FUEL_DROP, position.getDeviceId(), position.getId());
event.set(ATTRIBUTE_FUEL_DROP_THRESHOLD, fuelDropThreshold);
- return Collections.singleton(event);
+ return Collections.singletonMap(event, position);
}
}
}
diff --git a/src/org/traccar/events/GeofenceEventHandler.java b/src/org/traccar/events/GeofenceEventHandler.java
index fbec932b1..31d82a81e 100644
--- a/src/org/traccar/events/GeofenceEventHandler.java
+++ b/src/org/traccar/events/GeofenceEventHandler.java
@@ -16,12 +16,14 @@
package org.traccar.events;
import java.util.ArrayList;
-import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import org.traccar.BaseEventHandler;
import org.traccar.Context;
import org.traccar.database.GeofenceManager;
+import org.traccar.model.Calendar;
import org.traccar.model.Device;
import org.traccar.model.Event;
import org.traccar.model.Position;
@@ -35,8 +37,8 @@ public class GeofenceEventHandler extends BaseEventHandler {
}
@Override
- protected Collection<Event> analyzePosition(Position position) {
- Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId());
+ protected Map<Event, Position> analyzePosition(Position position) {
+ Device device = Context.getIdentityManager().getById(position.getDeviceId());
if (device == null) {
return null;
}
@@ -55,23 +57,23 @@ public class GeofenceEventHandler extends BaseEventHandler {
device.setGeofenceIds(currentGeofences);
- Collection<Event> events = new ArrayList<>();
+ Map<Event, Position> events = new HashMap<>();
for (long geofenceId : newGeofences) {
- long calendarId = geofenceManager.getGeofence(geofenceId).getCalendarId();
- if (calendarId == 0 || Context.getCalendarManager().getCalendar(calendarId) == null
- || Context.getCalendarManager().getCalendar(calendarId).checkMoment(position.getFixTime())) {
+ long calendarId = geofenceManager.getById(geofenceId).getCalendarId();
+ Calendar calendar = calendarId != 0 ? Context.getCalendarManager().getById(calendarId) : null;
+ if (calendar == null || calendar.checkMoment(position.getFixTime())) {
Event event = new Event(Event.TYPE_GEOFENCE_ENTER, position.getDeviceId(), position.getId());
event.setGeofenceId(geofenceId);
- events.add(event);
+ events.put(event, position);
}
}
for (long geofenceId : oldGeofences) {
- long calendarId = geofenceManager.getGeofence(geofenceId).getCalendarId();
- if (calendarId == 0 || Context.getCalendarManager().getCalendar(calendarId) == null
- || Context.getCalendarManager().getCalendar(calendarId).checkMoment(position.getFixTime())) {
+ long calendarId = geofenceManager.getById(geofenceId).getCalendarId();
+ Calendar calendar = calendarId != 0 ? Context.getCalendarManager().getById(calendarId) : null;
+ if (calendar == null || calendar.checkMoment(position.getFixTime())) {
Event event = new Event(Event.TYPE_GEOFENCE_EXIT, position.getDeviceId(), position.getId());
event.setGeofenceId(geofenceId);
- events.add(event);
+ events.put(event, position);
}
}
return events;
diff --git a/src/org/traccar/events/IgnitionEventHandler.java b/src/org/traccar/events/IgnitionEventHandler.java
index c628cc107..cc53b216c 100644
--- a/src/org/traccar/events/IgnitionEventHandler.java
+++ b/src/org/traccar/events/IgnitionEventHandler.java
@@ -16,8 +16,8 @@
*/
package org.traccar.events;
-import java.util.Collection;
import java.util.Collections;
+import java.util.Map;
import org.traccar.BaseEventHandler;
import org.traccar.Context;
@@ -28,13 +28,13 @@ import org.traccar.model.Position;
public class IgnitionEventHandler extends BaseEventHandler {
@Override
- protected Collection<Event> analyzePosition(Position position) {
- Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId());
+ protected Map<Event, Position> analyzePosition(Position position) {
+ Device device = Context.getIdentityManager().getById(position.getDeviceId());
if (device == null || !Context.getIdentityManager().isLatestPosition(position)) {
return null;
}
- Collection<Event> result = null;
+ Map<Event, Position> result = null;
if (position.getAttributes().containsKey(Position.KEY_IGNITION)) {
boolean ignition = position.getBoolean(Position.KEY_IGNITION);
@@ -44,11 +44,11 @@ public class IgnitionEventHandler extends BaseEventHandler {
boolean oldIgnition = lastPosition.getBoolean(Position.KEY_IGNITION);
if (ignition && !oldIgnition) {
- result = Collections.singleton(
- new Event(Event.TYPE_IGNITION_ON, position.getDeviceId(), position.getId()));
+ result = Collections.singletonMap(
+ new Event(Event.TYPE_IGNITION_ON, position.getDeviceId(), position.getId()), position);
} else if (!ignition && oldIgnition) {
- result = Collections.singleton(
- new Event(Event.TYPE_IGNITION_OFF, position.getDeviceId(), position.getId()));
+ result = Collections.singletonMap(
+ new Event(Event.TYPE_IGNITION_OFF, position.getDeviceId(), position.getId()), position);
}
}
}
diff --git a/src/org/traccar/events/MaintenanceEventHandler.java b/src/org/traccar/events/MaintenanceEventHandler.java
index 86836f6af..86abf7c17 100644
--- a/src/org/traccar/events/MaintenanceEventHandler.java
+++ b/src/org/traccar/events/MaintenanceEventHandler.java
@@ -16,8 +16,8 @@
*/
package org.traccar.events;
-import java.util.Collection;
import java.util.Collections;
+import java.util.Map;
import org.traccar.BaseEventHandler;
import org.traccar.Context;
@@ -31,8 +31,8 @@ public class MaintenanceEventHandler extends BaseEventHandler {
public static final String ATTRIBUTE_MAINTENANCE_INTERVAL = "maintenance.interval";
@Override
- protected Collection<Event> analyzePosition(Position position) {
- Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId());
+ protected Map<Event, Position> analyzePosition(Position position) {
+ Device device = Context.getIdentityManager().getById(position.getDeviceId());
if (device == null || !Context.getIdentityManager().isLatestPosition(position)) {
return null;
}
@@ -60,7 +60,7 @@ public class MaintenanceEventHandler extends BaseEventHandler {
if ((long) (oldTotalDistance / maintenanceInterval) < (long) (newTotalDistance / maintenanceInterval)) {
Event event = new Event(Event.TYPE_MAINTENANCE, position.getDeviceId(), position.getId());
event.set(Position.KEY_TOTAL_DISTANCE, newTotalDistance);
- return Collections.singleton(event);
+ return Collections.singletonMap(event, position);
}
return null;
diff --git a/src/org/traccar/events/MotionEventHandler.java b/src/org/traccar/events/MotionEventHandler.java
index e6fd10f3e..0c1c4848f 100644
--- a/src/org/traccar/events/MotionEventHandler.java
+++ b/src/org/traccar/events/MotionEventHandler.java
@@ -1,5 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 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.
@@ -15,42 +16,114 @@
*/
package org.traccar.events;
-import java.util.Collection;
import java.util.Collections;
+import java.util.Map;
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(TripsConfig tripsConfig) {
+ this.tripsConfig = tripsConfig;
+ }
+
+ private Map<Event, Position> newEvent(DeviceState deviceState, boolean newMotion) {
+ String eventType = newMotion ? Event.TYPE_DEVICE_MOVING : Event.TYPE_DEVICE_STOPPED;
+ Position position = deviceState.getMotionPosition();
+ Event event = new Event(eventType, position.getDeviceId(), position.getId());
+ deviceState.setMotionState(newMotion);
+ deviceState.setMotionPosition(null);
+ return Collections.singletonMap(event, position);
+ }
+
+ public Map<Event, Position> updateMotionState(DeviceState deviceState) {
+ Map<Event, Position> result = null;
+ if (deviceState.getMotionState() != null && deviceState.getMotionPosition() != null) {
+ boolean newMotion = !deviceState.getMotionState();
+ Position motionPosition = deviceState.getMotionPosition();
+ long currentTime = System.currentTimeMillis();
+ long motionTime = motionPosition.getFixTime().getTime()
+ + (newMotion ? tripsConfig.getMinimalTripDuration() : tripsConfig.getMinimalParkingDuration());
+ if (motionTime <= currentTime) {
+ result = newEvent(deviceState, newMotion);
+ }
+ }
+ return result;
+ }
+
+ public Map<Event, Position> updateMotionState(DeviceState deviceState, Position position) {
+ return updateMotionState(deviceState, position, position.getBoolean(Position.KEY_MOTION));
+ }
+
+ public Map<Event, Position> updateMotionState(DeviceState deviceState, Position position, boolean newMotion) {
+ Map<Event, Position> result = null;
+ Boolean oldMotion = deviceState.getMotionState();
+
+ long currentTime = position.getFixTime().getTime();
+ if (newMotion != oldMotion) {
+ if (deviceState.getMotionPosition() == null) {
+ deviceState.setMotionPosition(position);
+ }
+ } else {
+ deviceState.setMotionPosition(null);
+ }
+
+ Position motionPosition = deviceState.getMotionPosition();
+ if (motionPosition != null) {
+ long motionTime = motionPosition.getFixTime().getTime();
+ double distance = ReportUtils.calculateDistance(motionPosition, position, false);
+ Boolean ignition = null;
+ if (tripsConfig.getUseIgnition()
+ && position.getAttributes().containsKey(Position.KEY_IGNITION)) {
+ ignition = position.getBoolean(Position.KEY_IGNITION);
+ }
+ if (newMotion) {
+ if (motionTime + tripsConfig.getMinimalTripDuration() <= currentTime
+ || distance >= tripsConfig.getMinimalTripDistance()) {
+ result = newEvent(deviceState, newMotion);
+ }
+ } else {
+ if (motionTime + tripsConfig.getMinimalParkingDuration() <= currentTime
+ || ignition != null && !ignition) {
+ result = newEvent(deviceState, newMotion);
+ }
+ }
+ }
+ return result;
+ }
+
@Override
- protected Collection<Event> analyzePosition(Position position) {
+ protected Map<Event, Position> analyzePosition(Position position) {
- Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId());
+ long deviceId = position.getDeviceId();
+ Device device = Context.getIdentityManager().getById(deviceId);
if (device == null) {
return null;
}
- if (!Context.getIdentityManager().isLatestPosition(position) || !position.getValid()) {
+ if (!Context.getIdentityManager().isLatestPosition(position)
+ || !tripsConfig.getProcessInvalidPositions() && !position.getValid()) {
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);
- }
- 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()));
+ Map<Event, Position> result = null;
+ DeviceState deviceState = Context.getDeviceManager().getDeviceState(deviceId);
+
+ if (deviceState.getMotionState() == null) {
+ deviceState.setMotionState(position.getBoolean(Position.KEY_MOTION));
+ } else {
+ result = updateMotionState(deviceState, position);
}
- return null;
+ Context.getDeviceManager().setDeviceState(deviceId, deviceState);
+ return result;
}
}
diff --git a/src/org/traccar/events/OverspeedEventHandler.java b/src/org/traccar/events/OverspeedEventHandler.java
index 00c3845d2..cb658415c 100644
--- a/src/org/traccar/events/OverspeedEventHandler.java
+++ b/src/org/traccar/events/OverspeedEventHandler.java
@@ -15,12 +15,13 @@
*/
package org.traccar.events;
-import java.util.Collection;
import java.util.Collections;
+import java.util.Map;
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;
@@ -29,15 +30,69 @@ public class OverspeedEventHandler extends BaseEventHandler {
public static final String ATTRIBUTE_SPEED_LIMIT = "speedLimit";
private boolean notRepeat;
+ private long minimalDuration;
- public OverspeedEventHandler() {
- notRepeat = Context.getConfig().getBoolean("event.overspeed.notRepeat");
+ public OverspeedEventHandler(long minimalDuration, boolean notRepeat) {
+ this.notRepeat = notRepeat;
+ this.minimalDuration = minimalDuration;
+ }
+
+ private Map<Event, Position> newEvent(DeviceState deviceState, double speedLimit) {
+ Position position = deviceState.getOverspeedPosition();
+ Event event = new Event(Event.TYPE_DEVICE_OVERSPEED, position.getDeviceId(), position.getId());
+ event.set("speed", deviceState.getOverspeedPosition().getSpeed());
+ event.set(ATTRIBUTE_SPEED_LIMIT, speedLimit);
+ deviceState.setOverspeedState(notRepeat);
+ deviceState.setOverspeedPosition(null);
+ return Collections.singletonMap(event, position);
+ }
+
+ public Map<Event, Position> updateOverspeedState(DeviceState deviceState, double speedLimit) {
+ Map<Event, Position> result = null;
+ if (deviceState.getOverspeedState() != null && !deviceState.getOverspeedState()
+ && deviceState.getOverspeedPosition() != null && speedLimit != 0) {
+ long currentTime = System.currentTimeMillis();
+ Position overspeedPosition = deviceState.getOverspeedPosition();
+ long overspeedTime = overspeedPosition.getFixTime().getTime();
+ if (overspeedTime + minimalDuration <= currentTime) {
+ result = newEvent(deviceState, speedLimit);
+ }
+ }
+ return result;
+ }
+
+ public Map<Event, Position> updateOverspeedState(DeviceState deviceState, Position position, double speedLimit) {
+ Map<Event, Position> result = null;
+
+ Boolean oldOverspeed = deviceState.getOverspeedState();
+
+ long currentTime = position.getFixTime().getTime();
+ boolean newOverspeed = position.getSpeed() > speedLimit;
+ if (newOverspeed && !oldOverspeed) {
+ if (deviceState.getOverspeedPosition() == null) {
+ deviceState.setOverspeedPosition(position);
+ }
+ } else if (oldOverspeed && !newOverspeed) {
+ deviceState.setOverspeedState(false);
+ deviceState.setOverspeedPosition(null);
+ } else {
+ deviceState.setOverspeedPosition(null);
+ }
+ Position overspeedPosition = deviceState.getOverspeedPosition();
+ if (overspeedPosition != null) {
+ long overspeedTime = overspeedPosition.getFixTime().getTime();
+ if (newOverspeed && overspeedTime + minimalDuration <= currentTime) {
+ result = newEvent(deviceState, speedLimit);
+ }
+ }
+ return result;
}
@Override
- protected Collection<Event> analyzePosition(Position position) {
+ protected Map<Event, Position> analyzePosition(Position position) {
- Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId());
+ long deviceId = position.getDeviceId();
+ Device device = Context.getIdentityManager().getById(deviceId);
if (device == null) {
return null;
}
@@ -45,26 +100,22 @@ public class OverspeedEventHandler extends BaseEventHandler {
return null;
}
- double speed = position.getSpeed();
- double speedLimit = Context.getDeviceManager()
- .lookupAttributeDouble(device.getId(), ATTRIBUTE_SPEED_LIMIT, 0, false);
+ double speedLimit = Context.getDeviceManager().lookupAttributeDouble(deviceId, ATTRIBUTE_SPEED_LIMIT, 0, false);
if (speedLimit == 0) {
return null;
}
- double oldSpeed = 0;
- if (notRepeat) {
- Position lastPosition = Context.getIdentityManager().getLastPosition(position.getDeviceId());
- if (lastPosition != null) {
- oldSpeed = lastPosition.getSpeed();
- }
- }
- if (speed > speedLimit && oldSpeed <= speedLimit) {
- Event event = new Event(Event.TYPE_DEVICE_OVERSPEED, position.getDeviceId(), position.getId());
- event.set("speed", speed);
- event.set(ATTRIBUTE_SPEED_LIMIT, speedLimit);
- return Collections.singleton(event);
+
+ Map<Event, Position> result = null;
+ DeviceState deviceState = Context.getDeviceManager().getDeviceState(deviceId);
+
+ if (deviceState.getOverspeedState() == null) {
+ deviceState.setOverspeedState(position.getSpeed() > speedLimit);
+ } else {
+ result = updateOverspeedState(deviceState, position, speedLimit);
}
- return null;
+
+ Context.getDeviceManager().setDeviceState(deviceId, deviceState);
+ return result;
}
}
diff --git a/src/org/traccar/helper/BitBuffer.java b/src/org/traccar/helper/BitBuffer.java
index 7626988cc..ac307efdf 100644
--- a/src/org/traccar/helper/BitBuffer.java
+++ b/src/org/traccar/helper/BitBuffer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import org.jboss.netty.buffer.ChannelBuffers;
public class BitBuffer {
- private ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
+ private final ChannelBuffer buffer;
private int writeByte;
private int writeCount;
@@ -28,6 +28,14 @@ public class BitBuffer {
private int readByte;
private int readCount;
+ public BitBuffer() {
+ buffer = ChannelBuffers.dynamicBuffer();
+ }
+
+ public BitBuffer(ChannelBuffer buffer) {
+ this.buffer = buffer;
+ }
+
public void writeEncoded(byte[] bytes) {
for (byte b : bytes) {
b -= 48;
diff --git a/src/org/traccar/helper/PatternUtil.java b/src/org/traccar/helper/PatternUtil.java
index 12536eaef..1bbb166a6 100644
--- a/src/org/traccar/helper/PatternUtil.java
+++ b/src/org/traccar/helper/PatternUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package org.traccar.helper;
+import java.lang.management.ManagementFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@@ -49,6 +50,10 @@ public final class PatternUtil {
public static MatchResult checkPattern(String pattern, String input) {
+ if (!ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("-agentlib:jdwp")) {
+ throw new RuntimeException("PatternUtil usage detected");
+ }
+
MatchResult result = new MatchResult();
for (int i = 0; i < pattern.length(); i++) {
diff --git a/src/org/traccar/model/Attribute.java b/src/org/traccar/model/Attribute.java
index 9c3b5e43b..45d40b3ec 100644
--- a/src/org/traccar/model/Attribute.java
+++ b/src/org/traccar/model/Attribute.java
@@ -16,17 +16,7 @@
*/
package org.traccar.model;
-public class Attribute {
-
- private long id;
-
- public long getId() {
- return id;
- }
-
- public void setId(long id) {
- this.id = id;
- }
+public class Attribute extends BaseModel {
private String description;
diff --git a/src/org/traccar/model/AttributeAlias.java b/src/org/traccar/model/AttributeAlias.java
deleted file mode 100644
index 2835c0558..000000000
--- a/src/org/traccar/model/AttributeAlias.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.model;
-
-public class AttributeAlias {
-
- private long id;
-
- public long getId() {
- return id;
- }
-
- public void setId(long id) {
- this.id = id;
- }
-
- private long deviceId;
-
- public long getDeviceId() {
- return deviceId;
- }
-
- public void setDeviceId(long deviceId) {
- this.deviceId = deviceId;
- }
-
- private String attribute;
-
- public String getAttribute() {
- return attribute;
- }
-
- public void setAttribute(String attribute) {
- this.attribute = attribute;
- }
-
- private String alias;
-
- public String getAlias() {
- return alias;
- }
-
- public void setAlias(String alias) {
- this.alias = alias;
- }
-
-}
diff --git a/src/org/traccar/model/GroupAttribute.java b/src/org/traccar/model/BaseModel.java
index a7e8a80bc..8bdb916e8 100644
--- a/src/org/traccar/model/GroupAttribute.java
+++ b/src/org/traccar/model/BaseModel.java
@@ -16,25 +16,16 @@
*/
package org.traccar.model;
-public class GroupAttribute {
+public class BaseModel {
- private long groupId;
+ private long id;
- public long getGroupId() {
- return groupId;
+ public final long getId() {
+ return id;
}
- public void setGroupId(long groupId) {
- this.groupId = groupId;
+ public final void setId(long id) {
+ this.id = id;
}
- private long attributeId;
-
- public long getAttributeId() {
- return attributeId;
- }
-
- public void setAttributeId(long attributeId) {
- this.attributeId = attributeId;
- }
}
diff --git a/src/org/traccar/model/Calendar.java b/src/org/traccar/model/Calendar.java
index 55f696d50..56d3eb74c 100644
--- a/src/org/traccar/model/Calendar.java
+++ b/src/org/traccar/model/Calendar.java
@@ -27,14 +27,14 @@ import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.filter.Filter;
import net.fortuna.ical4j.filter.PeriodRule;
-import net.fortuna.ical4j.filter.Rule;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Dur;
import net.fortuna.ical4j.model.Period;
import net.fortuna.ical4j.model.component.CalendarComponent;
+import org.apache.commons.collections4.Predicate;
import org.traccar.database.QueryIgnore;
-public class Calendar extends Extensible {
+public class Calendar extends ExtendedModel {
private String name;
@@ -69,8 +69,8 @@ public class Calendar extends Extensible {
public boolean checkMoment(Date date) {
if (calendar != null) {
Period period = new Period(new DateTime(date), new Dur(0, 0, 0, 0));
- Rule<CalendarComponent> periodRule = new PeriodRule<>(period);
- Filter<CalendarComponent> filter = new Filter<>(new Rule[] {periodRule}, Filter.MATCH_ANY);
+ Predicate<CalendarComponent> periodRule = new PeriodRule<>(period);
+ Filter<CalendarComponent> filter = new Filter<>(new Predicate[] {periodRule}, Filter.MATCH_ANY);
Collection<CalendarComponent> events = filter.filter(calendar.getComponents(CalendarComponent.VEVENT));
if (events != null && !events.isEmpty()) {
return true;
diff --git a/src/org/traccar/model/CalendarPermission.java b/src/org/traccar/model/CalendarPermission.java
deleted file mode 100644
index 59f54e07b..000000000
--- a/src/org/traccar/model/CalendarPermission.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
- * Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.traccar.model;
-
-public class CalendarPermission {
-
- private long userId;
-
- public long getUserId() {
- return userId;
- }
-
- public void setUserId(long userId) {
- this.userId = userId;
- }
-
- private long calendarId;
-
- public long getCalendarId() {
- return calendarId;
- }
-
- public void setCalendarId(long calendarId) {
- this.calendarId = calendarId;
- }
-}
diff --git a/src/org/traccar/model/CellTower.java b/src/org/traccar/model/CellTower.java
index 2eb56dd33..6d1dfbd7f 100644
--- a/src/org/traccar/model/CellTower.java
+++ b/src/org/traccar/model/CellTower.java
@@ -106,4 +106,10 @@ public class CellTower {
this.signalStrength = signalStrength;
}
+ public void setOperator(long operator) {
+ String operatorString = String.valueOf(operator);
+ mobileCountryCode = Integer.parseInt(operatorString.substring(0, 3));
+ mobileNetworkCode = Integer.parseInt(operatorString.substring(3));
+ }
+
}
diff --git a/src/org/traccar/model/Command.java b/src/org/traccar/model/Command.java
index 6a48b14e9..16205ede1 100644
--- a/src/org/traccar/model/Command.java
+++ b/src/org/traccar/model/Command.java
@@ -15,10 +15,12 @@
*/
package org.traccar.model;
+import org.traccar.database.QueryIgnore;
+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
-public class Command extends Message {
+public class Command extends Message implements Cloneable {
public static final String TYPE_CUSTOM = "custom";
public static final String TYPE_IDENTIFICATION = "deviceIdentification";
@@ -75,6 +77,11 @@ public class Command extends Message {
public static final String KEY_SERVER = "server";
public static final String KEY_PORT = "port";
+ @Override
+ public Command clone() throws CloneNotSupportedException {
+ return (Command) super.clone();
+ }
+
private boolean textChannel;
public boolean getTextChannel() {
@@ -85,4 +92,20 @@ public class Command extends Message {
this.textChannel = textChannel;
}
+ @QueryIgnore
+ @Override
+ public long getDeviceId() {
+ return super.getDeviceId();
+ }
+
+ private String description;
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
}
diff --git a/src/org/traccar/model/Device.java b/src/org/traccar/model/Device.java
index 2324da532..c8a28404c 100644
--- a/src/org/traccar/model/Device.java
+++ b/src/org/traccar/model/Device.java
@@ -18,7 +18,10 @@ package org.traccar.model;
import java.util.Date;
import java.util.List;
-public class Device extends Extensible {
+import org.traccar.database.QueryExtended;
+import org.traccar.database.QueryIgnore;
+
+public class Device extends ExtendedModel {
private String name;
@@ -46,6 +49,7 @@ public class Device extends Extensible {
private String status;
+ @QueryIgnore
public String getStatus() {
return status != null ? status : STATUS_OFFLINE;
}
@@ -56,6 +60,7 @@ public class Device extends Extensible {
private Date lastUpdate;
+ @QueryExtended
public Date getLastUpdate() {
if (lastUpdate != null) {
return new Date(lastUpdate.getTime());
@@ -74,6 +79,7 @@ public class Device extends Extensible {
private long positionId;
+ @QueryIgnore
public long getPositionId() {
return positionId;
}
@@ -94,6 +100,7 @@ public class Device extends Extensible {
private List<Long> geofenceIds;
+ @QueryIgnore
public List<Long> getGeofenceIds() {
return geofenceIds;
}
diff --git a/src/org/traccar/model/DeviceGeofence.java b/src/org/traccar/model/DeviceGeofence.java
deleted file mode 100644
index 00c99add6..000000000
--- a/src/org/traccar/model/DeviceGeofence.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@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 DeviceGeofence {
-
- private long deviceId;
-
- public long getDeviceId() {
- return deviceId;
- }
-
- public void setDeviceId(long deviceId) {
- this.deviceId = deviceId;
- }
-
- private long geofenceId;
-
- public long getGeofenceId() {
- return geofenceId;
- }
-
- public void setGeofenceId(long geofenceId) {
- this.geofenceId = geofenceId;
- }
-
-}
diff --git a/src/org/traccar/model/DevicePermission.java b/src/org/traccar/model/DevicePermission.java
deleted file mode 100644
index c62173132..000000000
--- a/src/org/traccar/model/DevicePermission.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@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 DevicePermission {
-
- private long userId;
-
- public long getUserId() {
- return userId;
- }
-
- public void setUserId(long userId) {
- this.userId = userId;
- }
-
- private long deviceId;
-
- public long getDeviceId() {
- return deviceId;
- }
-
- public void setDeviceId(long deviceId) {
- this.deviceId = deviceId;
- }
-
-}
diff --git a/src/org/traccar/model/DeviceState.java b/src/org/traccar/model/DeviceState.java
new file mode 100644
index 000000000..f2d0ff614
--- /dev/null
+++ b/src/org/traccar/model/DeviceState.java
@@ -0,0 +1,61 @@
+/*
+ * 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;
+ }
+
+ private Boolean overspeedState;
+
+ public void setOverspeedState(boolean overspeedState) {
+ this.overspeedState = overspeedState;
+ }
+
+ public Boolean getOverspeedState() {
+ return overspeedState;
+ }
+
+ private Position overspeedPosition;
+
+ public void setOverspeedPosition(Position overspeedPosition) {
+ this.overspeedPosition = overspeedPosition;
+ }
+
+ public Position getOverspeedPosition() {
+ return overspeedPosition;
+ }
+
+}
diff --git a/src/org/traccar/model/AttributePermission.java b/src/org/traccar/model/Driver.java
index fe2fe7b6e..05f52fd4d 100644
--- a/src/org/traccar/model/AttributePermission.java
+++ b/src/org/traccar/model/Driver.java
@@ -16,26 +16,25 @@
*/
package org.traccar.model;
-public class AttributePermission {
+public class Driver extends ExtendedModel {
- private long userId;
+ private String name;
- public long getUserId() {
- return userId;
+ public String getName() {
+ return name;
}
- public void setUserId(long userId) {
- this.userId = userId;
+ public void setName(String name) {
+ this.name = name;
}
- private long attributeId;
+ private String uniqueId;
- public long getAttributeId() {
- return attributeId;
+ public String getUniqueId() {
+ return uniqueId;
}
- public void setAttributeId(long attributeId) {
- this.attributeId = attributeId;
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
}
-
}
diff --git a/src/org/traccar/model/Event.java b/src/org/traccar/model/Event.java
index 6a24d91c6..47b60af01 100644
--- a/src/org/traccar/model/Event.java
+++ b/src/org/traccar/model/Event.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,15 +20,13 @@ 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();
+ this(type, deviceId);
+ setPositionId(positionId);
}
public Event(String type, long deviceId) {
- this.setType(type);
- this.setDeviceId(deviceId);
+ setType(type);
+ setDeviceId(deviceId);
this.serverTime = new Date();
}
@@ -61,6 +59,8 @@ public class Event extends Message {
public static final String TYPE_TEXT_MESSAGE = "textMessage";
+ public static final String TYPE_DRIVER_CHANGED = "driverChanged";
+
private Date serverTime;
public Date getServerTime() {
diff --git a/src/org/traccar/model/Extensible.java b/src/org/traccar/model/ExtendedModel.java
index b7953d8a6..8353d0e66 100644
--- a/src/org/traccar/model/Extensible.java
+++ b/src/org/traccar/model/ExtendedModel.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,17 +18,7 @@ package org.traccar.model;
import java.util.LinkedHashMap;
import java.util.Map;
-public class Extensible {
-
- private long id;
-
- public long getId() {
- return id;
- }
-
- public void setId(long id) {
- this.id = id;
- }
+public class ExtendedModel extends BaseModel {
private Map<String, Object> attributes = new LinkedHashMap<>();
@@ -41,7 +31,9 @@ public class Extensible {
}
public void set(String key, Boolean value) {
- attributes.put(key, value);
+ if (value != null) {
+ attributes.put(key, value);
+ }
}
public void set(String key, Byte value) {
@@ -110,7 +102,7 @@ public class Extensible {
public boolean getBoolean(String key) {
if (attributes.containsKey(key)) {
- return Boolean.parseBoolean(attributes.get(key).toString());
+ return (Boolean) attributes.get(key);
} else {
return false;
}
diff --git a/src/org/traccar/model/Geofence.java b/src/org/traccar/model/Geofence.java
index dfb888852..21c196da9 100644
--- a/src/org/traccar/model/Geofence.java
+++ b/src/org/traccar/model/Geofence.java
@@ -26,7 +26,7 @@ import org.traccar.geofence.GeofencePolyline;
import com.fasterxml.jackson.annotation.JsonIgnore;
-public class Geofence extends Extensible {
+public class Geofence extends ExtendedModel {
public static final String TYPE_GEOFENCE_CILCLE = "geofenceCircle";
public static final String TYPE_GEOFENCE_POLYGON = "geofencePolygon";
diff --git a/src/org/traccar/model/GeofencePermission.java b/src/org/traccar/model/GeofencePermission.java
deleted file mode 100644
index 464f4e9eb..000000000
--- a/src/org/traccar/model/GeofencePermission.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@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 GeofencePermission {
-
- private long userId;
-
- public long getUserId() {
- return userId;
- }
-
- public void setUserId(long userId) {
- this.userId = userId;
- }
-
- private long geofenceId;
-
- public long getGeofenceId() {
- return geofenceId;
- }
-
- public void setGeofenceId(long geofenceId) {
- this.geofenceId = geofenceId;
- }
-
-}
diff --git a/src/org/traccar/model/Group.java b/src/org/traccar/model/Group.java
index c21d43127..aad206aad 100644
--- a/src/org/traccar/model/Group.java
+++ b/src/org/traccar/model/Group.java
@@ -15,7 +15,7 @@
*/
package org.traccar.model;
-public class Group extends Extensible {
+public class Group extends ExtendedModel {
private String name;
diff --git a/src/org/traccar/model/GroupGeofence.java b/src/org/traccar/model/GroupGeofence.java
deleted file mode 100644
index 736e6c704..000000000
--- a/src/org/traccar/model/GroupGeofence.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@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 GroupGeofence {
-
- private long groupId;
-
- public long getGroupId() {
- return groupId;
- }
-
- public void setGroupId(long groupId) {
- this.groupId = groupId;
- }
-
- private long geofenceId;
-
- public long getGeofenceId() {
- return geofenceId;
- }
-
- public void setGeofenceId(long geofenceId) {
- this.geofenceId = geofenceId;
- }
-
-}
diff --git a/src/org/traccar/model/GroupPermission.java b/src/org/traccar/model/GroupPermission.java
deleted file mode 100644
index 59b41b049..000000000
--- a/src/org/traccar/model/GroupPermission.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2016 Anton Tananaev (anton@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 GroupPermission {
-
- private long userId;
-
- public long getUserId() {
- return userId;
- }
-
- public void setUserId(long userId) {
- this.userId = userId;
- }
-
- private long groupId;
-
- public long getGroupId() {
- return groupId;
- }
-
- public void setGroupId(long groupId) {
- this.groupId = groupId;
- }
-
-}
diff --git a/src/org/traccar/model/DeviceAttribute.java b/src/org/traccar/model/ManagedUser.java
index e0ac6dd98..03c5ef48d 100644
--- a/src/org/traccar/model/DeviceAttribute.java
+++ b/src/org/traccar/model/ManagedUser.java
@@ -16,25 +16,6 @@
*/
package org.traccar.model;
-public class DeviceAttribute {
+public class ManagedUser extends User {
- private long deviceId;
-
- public long getDeviceId() {
- return deviceId;
- }
-
- public void setDeviceId(long deviceId) {
- this.deviceId = deviceId;
- }
-
- private long attributeId;
-
- public long getAttributeId() {
- return attributeId;
- }
-
- public void setAttributeId(long attributeId) {
- this.attributeId = attributeId;
- }
}
diff --git a/src/org/traccar/model/Message.java b/src/org/traccar/model/Message.java
index ab472202b..dad9c20f0 100644
--- a/src/org/traccar/model/Message.java
+++ b/src/org/traccar/model/Message.java
@@ -15,7 +15,7 @@
*/
package org.traccar.model;
-public class Message extends Extensible {
+public class Message extends ExtendedModel {
private long deviceId;
diff --git a/src/org/traccar/model/Notification.java b/src/org/traccar/model/Notification.java
index 6c61cafaf..9d6034fff 100644
--- a/src/org/traccar/model/Notification.java
+++ b/src/org/traccar/model/Notification.java
@@ -15,16 +15,16 @@
*/
package org.traccar.model;
-public class Notification extends Extensible {
+public class Notification extends ExtendedModel {
- private long userId;
+ private boolean always;
- public long getUserId() {
- return userId;
+ public boolean getAlways() {
+ return always;
}
- public void setUserId(long userId) {
- this.userId = userId;
+ public void setAlways(boolean always) {
+ this.always = always;
}
private String type;
diff --git a/src/org/traccar/model/Permission.java b/src/org/traccar/model/Permission.java
new file mode 100644
index 000000000..1006b1c47
--- /dev/null
+++ b/src/org/traccar/model/Permission.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.traccar.database.DataManager;
+
+public class Permission {
+
+ private Class<?> ownerClass;
+ private long ownerId;
+ private Class<?> propertyClass;
+ private long propertyId;
+
+ public Permission(LinkedHashMap<String, Long> permissionMap) throws ClassNotFoundException {
+ Iterator<Map.Entry<String, Long>> iterator = permissionMap.entrySet().iterator();
+ String owner = iterator.next().getKey();
+ ownerClass = DataManager.getClassByName(owner);
+ String property = iterator.next().getKey();
+ propertyClass = DataManager.getClassByName(property);
+ ownerId = permissionMap.get(owner);
+ propertyId = permissionMap.get(property);
+ }
+
+ public Class<?> getOwnerClass() {
+ return ownerClass;
+ }
+
+ public long getOwnerId() {
+ return ownerId;
+ }
+
+ public Class<?> getPropertyClass() {
+ return propertyClass;
+ }
+
+ public long getPropertyId() {
+ return propertyId;
+ }
+}
diff --git a/src/org/traccar/model/Position.java b/src/org/traccar/model/Position.java
index 5835310ae..099e6d686 100644
--- a/src/org/traccar/model/Position.java
+++ b/src/org/traccar/model/Position.java
@@ -17,6 +17,8 @@ package org.traccar.model;
import java.util.Date;
+import org.traccar.database.QueryIgnore;
+
public class Position extends Message {
public static final String KEY_ORIGINAL = "raw";
@@ -28,6 +30,7 @@ public class Position extends Message {
public static final String KEY_SATELLITES_VISIBLE = "satVisible";
public static final String KEY_RSSI = "rssi";
public static final String KEY_GPS = "gps";
+ public static final String KEY_ROAMING = "roaming";
public static final String KEY_EVENT = "event";
public static final String KEY_ALARM = "alarm";
public static final String KEY_STATUS = "status";
@@ -35,6 +38,7 @@ public class Position extends Message {
public static final String KEY_ODOMETER_SERVICE = "serviceOdometer"; // meters
public static final String KEY_ODOMETER_TRIP = "tripOdometer"; // meters
public static final String KEY_HOURS = "hours";
+ public static final String KEY_STEPS = "steps";
public static final String KEY_INPUT = "input";
public static final String KEY_OUTPUT = "output";
public static final String KEY_IMAGE = "image";
@@ -49,7 +53,6 @@ public class Position extends Message {
public static final String KEY_FUEL_LEVEL = "fuel"; // liters
public static final String KEY_FUEL_CONSUMPTION = "fuelConsumption"; // liters/hour
- public static final String KEY_RFID = "rfid";
public static final String KEY_VERSION_FW = "versionFw";
public static final String KEY_VERSION_HW = "versionHw";
public static final String KEY_TYPE = "type";
@@ -74,6 +77,7 @@ public class Position extends Message {
public static final String KEY_OPERATOR = "operator";
public static final String KEY_COMMAND = "command";
public static final String KEY_BLOCKED = "blocked";
+ public static final String KEY_DOOR = "door";
public static final String KEY_DTCS = "dtcs";
public static final String KEY_OBD_SPEED = "obdSpeed"; // knots
@@ -81,6 +85,8 @@ public class Position extends Message {
public static final String KEY_RESULT = "result";
+ public static final String KEY_DRIVER_UNIQUE_ID = "driverUniqueId";
+
// Start with 1 not 0
public static final String PREFIX_TEMP = "temp";
public static final String PREFIX_ADC = "adc";
@@ -109,7 +115,8 @@ public class Position extends Message {
public static final String ALARM_ACCIDENT = "accident";
public static final String ALARM_TOW = "tow";
public static final String ALARM_ACCELERATION = "hardAcceleration";
- public static final String ALARM_BREAKING = "hardBreaking";
+ public static final String ALARM_BRAKING = "hardBraking";
+ public static final String ALARM_CORNERING = "hardCornering";
public static final String ALARM_FATIGUE_DRIVING = "fatigueDriving";
public static final String ALARM_POWER_CUT = "powerCut";
public static final String ALARM_POWER_RESTORED = "powerRestored";
@@ -194,6 +201,7 @@ public class Position extends Message {
private boolean outdated;
+ @QueryIgnore
public boolean getOutdated() {
return outdated;
}
@@ -292,4 +300,10 @@ public class Position extends Message {
this.network = network;
}
+ @Override
+ @QueryIgnore
+ public String getType() {
+ return super.getType();
+ }
+
}
diff --git a/src/org/traccar/model/Server.java b/src/org/traccar/model/Server.java
index 4ded65204..072e85d55 100644
--- a/src/org/traccar/model/Server.java
+++ b/src/org/traccar/model/Server.java
@@ -15,12 +15,12 @@
*/
package org.traccar.model;
-import java.util.TimeZone;
-
+import org.traccar.database.QueryIgnore;
import org.traccar.helper.Log;
-public class Server extends Extensible {
+public class Server extends ExtendedModel {
+ @QueryIgnore
public String getVersion() {
return Log.getAppVersion();
}
@@ -88,26 +88,6 @@ public class Server extends Extensible {
this.mapUrl = mapUrl;
}
- private String distanceUnit;
-
- public String getDistanceUnit() {
- return distanceUnit;
- }
-
- public void setDistanceUnit(String distanceUnit) {
- this.distanceUnit = distanceUnit;
- }
-
- private String speedUnit;
-
- public String getSpeedUnit() {
- return speedUnit;
- }
-
- public void setSpeedUnit(String speedUnit) {
- this.speedUnit = speedUnit;
- }
-
private double latitude;
public double getLatitude() {
@@ -168,13 +148,13 @@ public class Server extends Extensible {
this.coordinateFormat = coordinateFormat;
}
- private String timezone;
+ private boolean limitCommands;
- public void setTimezone(String timezone) {
- this.timezone = timezone != null ? TimeZone.getTimeZone(timezone).getID() : null;
+ public boolean getLimitCommands() {
+ return limitCommands;
}
- public String getTimezone() {
- return timezone;
+ public void setLimitCommands(boolean limitCommands) {
+ this.limitCommands = limitCommands;
}
}
diff --git a/src/org/traccar/model/Statistics.java b/src/org/traccar/model/Statistics.java
index c7ae5af7a..2acf8514f 100644
--- a/src/org/traccar/model/Statistics.java
+++ b/src/org/traccar/model/Statistics.java
@@ -17,7 +17,7 @@ package org.traccar.model;
import java.util.Date;
-public class Statistics extends Extensible {
+public class Statistics extends ExtendedModel {
private Date captureTime;
diff --git a/src/org/traccar/model/CommandType.java b/src/org/traccar/model/Typed.java
index 210316f71..313ec7bcd 100644
--- a/src/org/traccar/model/CommandType.java
+++ b/src/org/traccar/model/Typed.java
@@ -15,11 +15,11 @@
*/
package org.traccar.model;
-public class CommandType {
+public class Typed {
private String type;
- public CommandType(String type) {
+ public Typed(String type) {
this.type = type;
}
diff --git a/src/org/traccar/model/User.java b/src/org/traccar/model/User.java
index 366ced503..5d89dcfae 100644
--- a/src/org/traccar/model/User.java
+++ b/src/org/traccar/model/User.java
@@ -16,12 +16,14 @@
package org.traccar.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import org.traccar.database.QueryExtended;
+import org.traccar.database.QueryIgnore;
import org.traccar.helper.Hashing;
import java.util.Date;
-import java.util.TimeZone;
-public class User extends Extensible {
+public class User extends ExtendedModel {
private String name;
@@ -40,7 +42,7 @@ public class User extends Extensible {
}
public void setEmail(String email) {
- this.email = email;
+ this.email = email.trim();
}
private String phone;
@@ -83,26 +85,6 @@ public class User extends Extensible {
this.map = map;
}
- private String distanceUnit;
-
- public String getDistanceUnit() {
- return distanceUnit;
- }
-
- public void setDistanceUnit(String distanceUnit) {
- this.distanceUnit = distanceUnit;
- }
-
- private String speedUnit;
-
- public String getSpeedUnit() {
- return speedUnit;
- }
-
- public void setSpeedUnit(String speedUnit) {
- this.speedUnit = speedUnit;
- }
-
private double latitude;
public double getLatitude() {
@@ -228,6 +210,17 @@ public class User extends Extensible {
}
}
+ private boolean limitCommands;
+
+ public boolean getLimitCommands() {
+ return limitCommands;
+ }
+
+ public void setLimitCommands(boolean limitCommands) {
+ this.limitCommands = limitCommands;
+ }
+
+ @QueryIgnore
public String getPassword() {
return null;
}
@@ -243,6 +236,7 @@ public class User extends Extensible {
private String hashedPassword;
@JsonIgnore
+ @QueryExtended
public String getHashedPassword() {
return hashedPassword;
}
@@ -254,6 +248,7 @@ public class User extends Extensible {
private String salt;
@JsonIgnore
+ @QueryExtended
public String getSalt() {
return salt;
}
@@ -266,13 +261,4 @@ public class User extends Extensible {
return Hashing.validatePassword(password, hashedPassword, salt);
}
- private String timezone;
-
- public void setTimezone(String timezone) {
- this.timezone = timezone != null ? TimeZone.getTimeZone(timezone).getID() : null;
- }
-
- public String getTimezone() {
- return timezone;
- }
}
diff --git a/src/org/traccar/notification/EventForwarder.java b/src/org/traccar/notification/EventForwarder.java
index bd7cfc0c5..ac37f980c 100644
--- a/src/org/traccar/notification/EventForwarder.java
+++ b/src/org/traccar/notification/EventForwarder.java
@@ -69,13 +69,13 @@ public final class EventForwarder {
data.put(KEY_POSITION, position);
}
if (event.getDeviceId() != 0) {
- Device device = Context.getIdentityManager().getDeviceById(event.getDeviceId());
+ Device device = Context.getIdentityManager().getById(event.getDeviceId());
if (device != null) {
data.put(KEY_DEVICE, device);
}
}
if (event.getGeofenceId() != 0) {
- Geofence geofence = Context.getGeofenceManager().getGeofence(event.getGeofenceId());
+ Geofence geofence = (Geofence) Context.getGeofenceManager().getById(event.getGeofenceId());
if (geofence != null) {
data.put(KEY_GEOFENCE, geofence);
}
diff --git a/src/org/traccar/notification/NotificationFormatter.java b/src/org/traccar/notification/NotificationFormatter.java
index 96337ecaa..8da819430 100644
--- a/src/org/traccar/notification/NotificationFormatter.java
+++ b/src/org/traccar/notification/NotificationFormatter.java
@@ -30,6 +30,7 @@ import org.traccar.helper.Log;
import org.traccar.model.Device;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.model.User;
import org.traccar.reports.ReportUtils;
public final class NotificationFormatter {
@@ -38,17 +39,25 @@ public final class NotificationFormatter {
}
public static VelocityContext prepareContext(long userId, Event event, Position position) {
- Device device = Context.getIdentityManager().getDeviceById(event.getDeviceId());
+ User user = Context.getPermissionsManager().getUser(userId);
+ Device device = Context.getIdentityManager().getById(event.getDeviceId());
VelocityContext velocityContext = new VelocityContext();
+ velocityContext.put("user", user);
velocityContext.put("device", device);
velocityContext.put("event", event);
if (position != null) {
velocityContext.put("position", position);
- velocityContext.put("speedUnits", ReportUtils.getSpeedUnit(userId));
+ velocityContext.put("speedUnit", ReportUtils.getSpeedUnit(userId));
+ velocityContext.put("distanceUnit", ReportUtils.getDistanceUnit(userId));
+ velocityContext.put("volumeUnit", ReportUtils.getVolumeUnit(userId));
}
if (event.getGeofenceId() != 0) {
- velocityContext.put("geofence", Context.getGeofenceManager().getGeofence(event.getGeofenceId()));
+ velocityContext.put("geofence", Context.getGeofenceManager().getById(event.getGeofenceId()));
+ }
+ String driverUniqueId = event.getString(Position.KEY_DRIVER_UNIQUE_ID);
+ if (driverUniqueId != null) {
+ velocityContext.put("driver", Context.getDriversManager().getDriverByUniqueId(driverUniqueId));
}
velocityContext.put("webUrl", Context.getVelocityEngine().getProperty("web.url"));
velocityContext.put("dateTool", new DateTool());
diff --git a/src/org/traccar/notification/NotificationMail.java b/src/org/traccar/notification/NotificationMail.java
index 115b109e6..8707b10da 100644
--- a/src/org/traccar/notification/NotificationMail.java
+++ b/src/org/traccar/notification/NotificationMail.java
@@ -43,7 +43,7 @@ public final class NotificationMail {
if (host != null) {
properties.put("mail.transport.protocol", provider.getString("mail.transport.protocol", "smtp"));
properties.put("mail.smtp.host", host);
- properties.put("mail.smtp.port", provider.getString("mail.smtp.port", "25"));
+ properties.put("mail.smtp.port", String.valueOf(provider.getInteger("mail.smtp.port", 25)));
String starttlsEnable = provider.getString("mail.smtp.starttls.enable");
if (starttlsEnable != null) {
@@ -68,8 +68,6 @@ public final class NotificationMail {
properties.put("mail.smtp.ssl.protocols", sslProtocols);
}
- properties.put("mail.smtp.auth", provider.getString("mail.smtp.auth"));
-
String username = provider.getString("mail.smtp.username");
if (username != null) {
properties.put("mail.smtp.username", username);
@@ -89,13 +87,16 @@ public final class NotificationMail {
public static void sendMailSync(long userId, Event event, Position position) throws MessagingException {
User user = Context.getPermissionsManager().getUser(userId);
- Properties properties = getProperties(new PropertiesProvider(Context.getConfig()));
- if (!properties.containsKey("mail.smtp.host")) {
+ Properties properties = null;
+ if (!Context.getConfig().getBoolean("mail.smtp.ignoreUserConfig")) {
properties = getProperties(new PropertiesProvider(user));
- if (!properties.containsKey("mail.smtp.host")) {
- Log.warning("No SMTP configuration found");
- return;
- }
+ }
+ if (properties == null || !properties.containsKey("mail.smtp.host")) {
+ properties = getProperties(new PropertiesProvider(Context.getConfig()));
+ }
+ if (!properties.containsKey("mail.smtp.host")) {
+ Log.warning("No SMTP configuration found");
+ return;
}
Session session = Session.getInstance(properties);
diff --git a/src/org/traccar/notification/PropertiesProvider.java b/src/org/traccar/notification/PropertiesProvider.java
index e7cac8d0f..c5ba688e8 100644
--- a/src/org/traccar/notification/PropertiesProvider.java
+++ b/src/org/traccar/notification/PropertiesProvider.java
@@ -16,27 +16,27 @@
package org.traccar.notification;
import org.traccar.Config;
-import org.traccar.model.Extensible;
+import org.traccar.model.ExtendedModel;
public class PropertiesProvider {
private Config config;
- private Extensible extensible;
+ private ExtendedModel extendedModel;
public PropertiesProvider(Config config) {
this.config = config;
}
- public PropertiesProvider(Extensible extensible) {
- this.extensible = extensible;
+ public PropertiesProvider(ExtendedModel extendedModel) {
+ this.extendedModel = extendedModel;
}
public String getString(String key) {
if (config != null) {
return config.getString(key);
} else {
- return extensible.getString(key);
+ return extendedModel.getString(key);
}
}
@@ -48,4 +48,17 @@ public class PropertiesProvider {
return value;
}
+ public int getInteger(String key, int defaultValue) {
+ if (config != null) {
+ return config.getInteger(key, defaultValue);
+ } else {
+ Object result = extendedModel.getAttributes().get(key);
+ if (result != null) {
+ return result instanceof String ? Integer.parseInt((String) result) : (Integer) result;
+ } else {
+ return defaultValue;
+ }
+ }
+ }
+
}
diff --git a/src/org/traccar/processing/ComputedAttributesHandler.java b/src/org/traccar/processing/ComputedAttributesHandler.java
index 8689c5a58..f1f371475 100644
--- a/src/org/traccar/processing/ComputedAttributesHandler.java
+++ b/src/org/traccar/processing/ComputedAttributesHandler.java
@@ -31,19 +31,33 @@ import org.traccar.BaseDataHandler;
import org.traccar.Context;
import org.traccar.helper.Log;
import org.traccar.model.Attribute;
+import org.traccar.model.Device;
import org.traccar.model.Position;
public class ComputedAttributesHandler extends BaseDataHandler {
private JexlEngine engine;
+ private boolean mapDeviceAttributes;
+
public ComputedAttributesHandler() {
engine = new JexlEngine();
engine.setStrict(true);
+ if (Context.getConfig() != null) {
+ mapDeviceAttributes = Context.getConfig().getBoolean("processing.computedAttributes.deviceAttributes");
+ }
}
private MapContext prepareContext(Position position) {
MapContext result = new MapContext();
+ if (mapDeviceAttributes) {
+ Device device = Context.getIdentityManager().getById(position.getDeviceId());
+ if (device != null) {
+ for (Object key : device.getAttributes().keySet()) {
+ result.set((String) key, device.getAttributes().get(key));
+ }
+ }
+ }
Set<Method> methods = new HashSet<>(Arrays.asList(position.getClass().getMethods()));
methods.removeAll(Arrays.asList(Object.class.getMethods()));
for (Method method : methods) {
@@ -72,8 +86,8 @@ public class ComputedAttributesHandler extends BaseDataHandler {
@Override
protected Position handlePosition(Position position) {
- Collection<Attribute> attributes = Context.getAttributesManager().getAttributes(
- Context.getAttributesManager().getAllDeviceAttributes(position.getDeviceId()));
+ Collection<Attribute> attributes = Context.getAttributesManager().getItems(
+ Context.getAttributesManager().getAllDeviceItems(position.getDeviceId()));
for (Attribute attribute : attributes) {
if (attribute.getAttribute() != null) {
Object result = null;
diff --git a/src/org/traccar/processing/CopyAttributesHandler.java b/src/org/traccar/processing/CopyAttributesHandler.java
index 3a96ca98d..9fbcfa73f 100644
--- a/src/org/traccar/processing/CopyAttributesHandler.java
+++ b/src/org/traccar/processing/CopyAttributesHandler.java
@@ -32,9 +32,14 @@ public class CopyAttributesHandler extends BaseDataHandler {
@Override
protected Position handlePosition(Position position) {
String attributesString = Context.getDeviceManager().lookupAttributeString(
- position.getDeviceId(), "processing.copyAttributes", null, true);
+ position.getDeviceId(), "processing.copyAttributes", "", true);
Position last = getLastPosition(position.getDeviceId());
- if (attributesString != null && last != null) {
+ if (attributesString.isEmpty()) {
+ attributesString = Position.KEY_DRIVER_UNIQUE_ID;
+ } else {
+ attributesString += "," + Position.KEY_DRIVER_UNIQUE_ID;
+ }
+ if (last != null) {
for (String attribute : attributesString.split("[ ,]")) {
if (last.getAttributes().containsKey(attribute) && !position.getAttributes().containsKey(attribute)) {
position.getAttributes().put(attribute, last.getAttributes().get(attribute));
diff --git a/src/org/traccar/protocol/AdmProtocol.java b/src/org/traccar/protocol/AdmProtocol.java
index 442121f0a..4d2cbe7b3 100644
--- a/src/org/traccar/protocol/AdmProtocol.java
+++ b/src/org/traccar/protocol/AdmProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,10 @@ package org.traccar.protocol;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder;
+import org.jboss.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.TrackerServer;
+import org.traccar.model.Command;
import java.nio.ByteOrder;
import java.util.List;
@@ -28,6 +30,9 @@ public class AdmProtocol extends BaseProtocol {
public AdmProtocol() {
super("adm");
+ setSupportedDataCommands(
+ Command.TYPE_GET_DEVICE_STATUS,
+ Command.TYPE_CUSTOM);
}
@Override
@@ -36,6 +41,8 @@ public class AdmProtocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 1, -3, 0));
+ pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("objectEncoder", new AdmProtocolEncoder());
pipeline.addLast("objectDecoder", new AdmProtocolDecoder(AdmProtocol.this));
}
};
diff --git a/src/org/traccar/protocol/AdmProtocolDecoder.java b/src/org/traccar/protocol/AdmProtocolDecoder.java
index f4a21cad0..f93c55e18 100644
--- a/src/org/traccar/protocol/AdmProtocolDecoder.java
+++ b/src/org/traccar/protocol/AdmProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,76 +33,75 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
+ public static final int CMD_RESPONSE_SIZE = 0x84;
public static final int MSG_IMEI = 0x03;
public static final int MSG_PHOTO = 0x0A;
public static final int MSG_ADM5 = 0x01;
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- ChannelBuffer buf = (ChannelBuffer) msg;
-
- buf.readUnsignedShort(); // device id
- buf.readUnsignedByte(); // length
-
- int type = buf.readUnsignedByte();
-
- DeviceSession deviceSession;
- if (type == MSG_IMEI) {
- deviceSession = getDeviceSession(
- channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII));
- } else {
- deviceSession = getDeviceSession(channel, remoteAddress);
- }
+ private Position decodeData(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf, int type) {
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
return null;
}
if (BitUtil.to(type, 2) == 0) {
-
Position position = new Position();
position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
- buf.readUnsignedShort(); // index
+ position.set(Position.KEY_INDEX, buf.readUnsignedShort());
- position.set(Position.KEY_STATUS, buf.readUnsignedShort());
-
- position.setValid(true);
+ int status = buf.readUnsignedShort();
+ position.set(Position.KEY_STATUS, status);
+ position.setValid(!BitUtil.check(status, 5));
position.setLatitude(buf.readFloat());
position.setLongitude(buf.readFloat());
position.setCourse(buf.readUnsignedShort() * 0.1);
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
- position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte());
-
+ position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte() * 0.1);
position.setAltitude(buf.readUnsignedShort());
-
position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1);
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte() & 0x0f);
position.setTime(new Date(buf.readUnsignedInt() * 1000));
- position.set(Position.KEY_POWER, buf.readUnsignedShort());
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
if (BitUtil.check(type, 2)) {
- buf.skipBytes(4);
+ buf.readUnsignedByte(); // vib
+ buf.readUnsignedByte(); // vib_count
+
+ int out = buf.readUnsignedByte();
+ for (int i = 0; i <= 3; i++) {
+ position.set(Position.PREFIX_OUT + (i + 1), BitUtil.check(out, i) ? 1 : 0);
+ }
+
+ buf.readUnsignedByte(); // in_alarm
}
if (BitUtil.check(type, 3)) {
- buf.skipBytes(12);
+ for (int i = 1; i <= 6; i++) {
+ position.set(Position.PREFIX_ADC + i, buf.readUnsignedShort() * 0.001);
+ }
}
if (BitUtil.check(type, 4)) {
- buf.skipBytes(8);
+ for (int i = 1; i <= 2; i++) {
+ position.set(Position.PREFIX_COUNT + i, buf.readUnsignedInt());
+ }
}
if (BitUtil.check(type, 5)) {
- buf.skipBytes(9);
+ for (int i = 1; i <= 3; i++) {
+ buf.readUnsignedShort(); // fuel level
+ }
+ for (int i = 1; i <= 3; i++) {
+ position.set(Position.PREFIX_TEMP + i, buf.readUnsignedByte());
+ }
}
if (BitUtil.check(type, 6)) {
@@ -119,4 +118,46 @@ public class AdmProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ private Position parseCommandResponse(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ int responseTextLength = buf.bytesBefore((byte) 0);
+ if (responseTextLength < 0) {
+ responseTextLength = CMD_RESPONSE_SIZE - 3;
+ }
+ position.set(Position.KEY_RESULT, buf.readBytes(responseTextLength).toString(StandardCharsets.UTF_8));
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ buf.readUnsignedShort(); // device id
+
+ int size = buf.readUnsignedByte();
+ if (size != CMD_RESPONSE_SIZE) {
+ int type = buf.readUnsignedByte();
+ if (type == MSG_IMEI) {
+ getDeviceSession(channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.UTF_8));
+ } else {
+ return decodeData(channel, remoteAddress, buf, type);
+ }
+ } else {
+ return parseCommandResponse(channel, remoteAddress, buf);
+ }
+
+ return null;
+ }
+
}
diff --git a/src/org/traccar/protocol/AdmProtocolEncoder.java b/src/org/traccar/protocol/AdmProtocolEncoder.java
new file mode 100644
index 000000000..8cbd8618d
--- /dev/null
+++ b/src/org/traccar/protocol/AdmProtocolEncoder.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Anatoliy Golubev (darth.naihil@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 AdmProtocolEncoder extends StringProtocolEncoder {
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_GET_DEVICE_STATUS:
+ return formatCommand(command, "STATUS\r\n");
+
+ case Command.TYPE_CUSTOM:
+ return formatCommand(command, "{%s}\r\n", Command.KEY_DATA);
+
+ default:
+ Log.warning(new UnsupportedOperationException(command.getType()));
+ break;
+ }
+
+ return null;
+ }
+}
diff --git a/src/org/traccar/protocol/AplicomProtocolDecoder.java b/src/org/traccar/protocol/AplicomProtocolDecoder.java
index eb8d77011..154451b5b 100644
--- a/src/org/traccar/protocol/AplicomProtocolDecoder.java
+++ b/src/org/traccar/protocol/AplicomProtocolDecoder.java
@@ -263,7 +263,8 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
}
if ((selector & 0x0200) != 0) {
- position.set(Position.KEY_RFID, (((long) buf.readUnsignedShort()) << 32) + buf.readUnsignedInt());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID,
+ String.valueOf(((long) buf.readUnsignedShort()) << 32) + buf.readUnsignedInt());
}
if ((selector & 0x0400) != 0) {
@@ -351,15 +352,28 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_VIN, buf.readBytes(18).toString(StandardCharsets.US_ASCII).trim());
}
+ if ((selector & 0x2000) != 0) {
+ buf.readUnsignedByte(); // card 1 type
+ buf.readUnsignedByte(); // card 1 country code
+ String card = buf.readBytes(20).toString(StandardCharsets.US_ASCII).trim();
+ if (!card.isEmpty()) {
+ position.set("card1", card);
+ }
+ }
+
+ if ((selector & 0x4000) != 0) {
+ buf.readUnsignedByte(); // card 2 type
+ buf.readUnsignedByte(); // card 2 country code
+ String card = buf.readBytes(20).toString(StandardCharsets.US_ASCII).trim();
+ if (!card.isEmpty()) {
+ position.set("card2", card);
+ }
+ }
+
if ((selector & 0x10000) != 0) {
int count = buf.readUnsignedByte();
for (int i = 1; i <= count; i++) {
- ChannelBuffer driver = buf.readBytes(22);
- int endIndex = driver.indexOf(0, driver.writerIndex(), (byte) 0);
- if (endIndex < 0) {
- endIndex = driver.writerIndex();
- }
- position.set("driver" + i, driver.toString(0, endIndex, StandardCharsets.US_ASCII).trim());
+ position.set("driver" + i, buf.readBytes(22).toString(StandardCharsets.US_ASCII).trim());
position.set("driverTime" + i, buf.readUnsignedInt());
}
}
@@ -456,59 +470,59 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShort(); // length
while (buf.readableBytes() > 0) {
- position.set("towedPosition", buf.readUnsignedByte());
+ buf.readUnsignedByte(); // towed position
int type = buf.readUnsignedByte();
int length = buf.readUnsignedByte();
+ int end = buf.readerIndex() + length;
- if (type == 0x01) {
- position.set("brakeFlags", ChannelBuffers.hexDump(buf.readBytes(length)));
- } else if (type == 0x02) {
- position.set("wheelSpeed", buf.readUnsignedShort() / 256.0);
- position.set("wheelSpeedDifference", buf.readUnsignedShort() / 256.0 - 125.0);
- position.set("lateralAcceleration", buf.readUnsignedByte() / 10.0 - 12.5);
- position.set("vehicleSpeed", buf.readUnsignedShort() / 256.0);
- } else if (type == 0x03) {
- position.set("axleLoadSum", buf.readUnsignedShort() * 2);
- } else if (type == 0x04) {
- position.set("tyrePressure", buf.readUnsignedByte() * 10);
- position.set("pneumaticPressure", buf.readUnsignedByte() * 5);
- } else if (type == 0x05) {
- position.set("brakeLining", buf.readUnsignedByte() * 0.4);
- position.set("brakeTemperature", buf.readUnsignedByte() * 10);
- } else if (type == 0x06) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 5);
- position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt() * 5);
- position.set(Position.KEY_ODOMETER_SERVICE, (buf.readUnsignedInt() - 2105540607) * 5);
- } else if (type == 0x0A) {
- ChannelBuffer brakeData = buf.readBytes(length);
- position.set("absStatusCounter", brakeData.readUnsignedShort());
- position.set("atvbStatusCounter", brakeData.readUnsignedShort());
- position.set("vdcActiveCounter", brakeData.readUnsignedShort());
- } else if (type == 0x0B) {
- position.set("brakeMinMaxData", ChannelBuffers.hexDump(buf.readBytes(length)));
- } else if (type == 0x0C) {
- position.set("missingPgn", ChannelBuffers.hexDump(buf.readBytes(length)));
- } else if (type == 0x0D) {
- switch (buf.readUnsignedByte()) {
- case 1:
- position.set("brakeManufacturer", "Wabco");
- break;
- case 2:
- position.set("brakeManufacturer", "Knorr");
- break;
- case 3:
- position.set("brakeManufacturer", "Haldex");
- break;
- default:
- position.set("brakeManufacturer", "Unknown");
- break;
- }
- buf.readUnsignedByte();
- position.set(Position.KEY_VIN, buf.readBytes(17).toString(StandardCharsets.US_ASCII));
- position.set("towedDetectionStatus", buf.readUnsignedByte());
- } else if (type == 0x0E) {
- buf.skipBytes(length);
+ switch (type) {
+ case 0x01:
+ position.set("brakeFlags", ChannelBuffers.hexDump(buf.readBytes(length)));
+ break;
+ case 0x02:
+ position.set("wheelSpeed", buf.readUnsignedShort() / 256.0);
+ position.set("wheelSpeedDifference", buf.readUnsignedShort() / 256.0 - 125.0);
+ position.set("lateralAcceleration", buf.readUnsignedByte() / 10.0 - 12.5);
+ position.set("vehicleSpeed", buf.readUnsignedShort() / 256.0);
+ break;
+ case 0x03:
+ position.set("axleLoadSum", buf.readUnsignedShort() * 2);
+ break;
+ case 0x04:
+ position.set("tyrePressure", buf.readUnsignedByte() * 10);
+ position.set("pneumaticPressure", buf.readUnsignedByte() * 5);
+ break;
+ case 0x05:
+ position.set("brakeLining", buf.readUnsignedByte() * 0.4);
+ position.set("brakeTemperature", buf.readUnsignedByte() * 10);
+ break;
+ case 0x06:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 5L);
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt() * 5L);
+ position.set(Position.KEY_ODOMETER_SERVICE, (buf.readUnsignedInt() - 2105540607) * 5L);
+ break;
+ case 0x0A:
+ position.set("absStatusCounter", buf.readUnsignedShort());
+ position.set("atvbStatusCounter", buf.readUnsignedShort());
+ position.set("vdcActiveCounter", buf.readUnsignedShort());
+ break;
+ case 0x0B:
+ position.set("brakeMinMaxData", ChannelBuffers.hexDump(buf.readBytes(length)));
+ break;
+ case 0x0C:
+ position.set("missingPgn", ChannelBuffers.hexDump(buf.readBytes(length)));
+ break;
+ case 0x0D:
+ buf.readUnsignedByte();
+ position.set("towedDetectionStatus", buf.readUnsignedInt());
+ buf.skipBytes(17); // vin
+ break;
+ case 0x0E:
+ default:
+ break;
}
+
+ buf.readerIndex(end);
}
}
@@ -554,7 +568,7 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte());
position.set("speedMax", buf.readUnsignedByte());
position.set("speedMin", buf.readUnsignedByte());
- position.set("hardBreaking", buf.readUnsignedByte());
+ position.set("hardBraking", buf.readUnsignedByte());
}
if ((selector & 0x0200) != 0) {
diff --git a/src/org/traccar/protocol/AquilaProtocolDecoder.java b/src/org/traccar/protocol/AquilaProtocolDecoder.java
index 5ff974a7d..773210b04 100644
--- a/src/org/traccar/protocol/AquilaProtocolDecoder.java
+++ b/src/org/traccar/protocol/AquilaProtocolDecoder.java
@@ -81,7 +81,7 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder {
.number("[01],") // corner packet
.number("(?:d+,){6}") // reserved
.number("[01],") // hard acceleration
- .number("[01],") // hard breaking
+ .number("[01],") // hard braking
.number("[01],[01],[01],[01],") // course bits
.number("(d+),") // external voltage
.number("(d+),") // internal voltage
@@ -115,7 +115,7 @@ public class AquilaProtocolDecoder extends BaseProtocolDecoder {
.number("[01],") // do 1
.number("[01],") // reserved
.number("[01],") // hard acceleration
- .number("[01],") // hard breaking
+ .number("[01],") // hard braking
.number("(?:[01],){4}") // reserved
.number("(d+),") // external voltage
.number("(d+),") // internal voltage
diff --git a/src/org/traccar/protocol/AstraProtocolDecoder.java b/src/org/traccar/protocol/AstraProtocolDecoder.java
index ea6aa7b30..8d86cd2be 100644
--- a/src/org/traccar/protocol/AstraProtocolDecoder.java
+++ b/src/org/traccar/protocol/AstraProtocolDecoder.java
@@ -105,7 +105,7 @@ public class AstraProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // geofence events
if (BitUtil.check(status, 8)) {
- position.set(Position.KEY_RFID, buf.readBytes(7).toString(StandardCharsets.US_ASCII));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, buf.readBytes(7).toString(StandardCharsets.US_ASCII));
position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium() * 1000);
position.set(Position.KEY_HOURS, buf.readUnsignedShort());
}
diff --git a/src/org/traccar/protocol/AtrackProtocolDecoder.java b/src/org/traccar/protocol/AtrackProtocolDecoder.java
index 79b3c36cc..23cb67e15 100644
--- a/src/org/traccar/protocol/AtrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/AtrackProtocolDecoder.java
@@ -327,7 +327,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() * 0.001);
- position.set("driver", readString(buf));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, readString(buf));
position.set(Position.PREFIX_TEMP + 1, buf.readShort() * 0.1);
position.set(Position.PREFIX_TEMP + 2, buf.readShort() * 0.1);
diff --git a/src/org/traccar/protocol/CarscopProtocolDecoder.java b/src/org/traccar/protocol/CarscopProtocolDecoder.java
index ac3df1cd7..2a081bcdd 100644
--- a/src/org/traccar/protocol/CarscopProtocolDecoder.java
+++ b/src/org/traccar/protocol/CarscopProtocolDecoder.java
@@ -44,8 +44,10 @@ public class CarscopProtocolDecoder extends BaseProtocolDecoder {
.number("(ddd.d)") // speed
.number("(dd)(dd)(dd)") // date (yymmdd)
.number("(ddd.dd)") // course
+ .groupBegin()
.number("(d{8})") // state
.number("L(d{6})") // odometer
+ .groupEnd("?")
.compile();
@Override
@@ -88,8 +90,10 @@ public class CarscopProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(parser.nextDouble(0));
- position.set(Position.KEY_STATUS, parser.next());
- position.set(Position.KEY_ODOMETER, parser.nextInt(0));
+ if (parser.hasNext(2)) {
+ position.set(Position.KEY_STATUS, parser.next());
+ position.set(Position.KEY_ODOMETER, parser.nextInt(0));
+ }
return position;
}
diff --git a/src/org/traccar/protocol/CastelProtocolDecoder.java b/src/org/traccar/protocol/CastelProtocolDecoder.java
index 83664fa5a..3a0ccea78 100644
--- a/src/org/traccar/protocol/CastelProtocolDecoder.java
+++ b/src/org/traccar/protocol/CastelProtocolDecoder.java
@@ -440,7 +440,7 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder {
}
- } else if (version == 4) {
+ } else if (version == 3 || version == 4) {
return decodeSc(channel, remoteAddress, buf, version, id, type, deviceSession);
diff --git a/src/org/traccar/protocol/CityeasyProtocolEncoder.java b/src/org/traccar/protocol/CityeasyProtocolEncoder.java
index c800131d6..387926e03 100644
--- a/src/org/traccar/protocol/CityeasyProtocolEncoder.java
+++ b/src/org/traccar/protocol/CityeasyProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
*/
package org.traccar.protocol;
+import java.util.TimeZone;
+
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.traccar.BaseProtocolEncoder;
@@ -56,13 +58,13 @@ public class CityeasyProtocolEncoder extends BaseProtocolEncoder {
content.writeShort(0);
return encodeContent(CityeasyProtocolDecoder.MSG_LOCATION_INTERVAL, content);
case Command.TYPE_SET_TIMEZONE:
- int timezone = command.getInteger(Command.KEY_TIMEZONE);
+ int timezone = TimeZone.getTimeZone(command.getString(Command.KEY_TIMEZONE)).getRawOffset() / 60000;
if (timezone < 0) {
content.writeByte(1);
} else {
content.writeByte(0);
}
- content.writeShort(Math.abs(timezone) / 60);
+ content.writeShort(Math.abs(timezone));
return encodeContent(CityeasyProtocolDecoder.MSG_TIMEZONE, content);
default:
Log.warning(new UnsupportedOperationException(command.getType()));
diff --git a/src/org/traccar/protocol/CradlepointProtocolDecoder.java b/src/org/traccar/protocol/CradlepointProtocolDecoder.java
index 1ba50f04b..e8f95a60c 100644
--- a/src/org/traccar/protocol/CradlepointProtocolDecoder.java
+++ b/src/org/traccar/protocol/CradlepointProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,11 +18,13 @@ package org.traccar.protocol;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.util.Date;
import java.util.regex.Pattern;
public class CradlepointProtocolDecoder extends BaseProtocolDecoder {
@@ -33,18 +35,18 @@ public class CradlepointProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN = new PatternBuilder()
.expression("([^,]+),") // id
- .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d{1,6}),") // time (hhmmss)
.number("(d+)(dd.d+),") // latitude
.expression("([NS]),")
.number("(d+)(dd.d+),") // longitude
.expression("([EW]),")
.number("(d+.d+)?,") // speed
.number("(d+.d+)?,") // course
- .expression("([^,]+),") // carrier
+ .expression("([^,]+)?,") // carrier
.expression("([^,]+)?,") // serdis
- .number("(-?d+),") // rsrp
- .number("(-?d+),") // dbm
- .number("(-?d+),") // rsrq
+ .number("(-?d+)?,") // rsrp
+ .number("(-?d+)?,") // rssi
+ .number("(-?d+)?,") // rsrq
.expression("([^,]+)?,") // ecio
.expression("([^,]+)?") // wan ip
.compile();
@@ -58,16 +60,21 @@ public class CradlepointProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setTime(parser.nextDateTime(Parser.DateTimeFormat.HMS));
+ int time = parser.nextInt();
+ DateBuilder dateBuilder = new DateBuilder(new Date());
+ dateBuilder.setHour(time / 100 / 100);
+ dateBuilder.setMinute(time / 100 % 100);
+ dateBuilder.setSecond(time % 100);
+ position.setTime(dateBuilder.getDate());
position.setValid(true);
position.setLatitude(parser.nextCoordinate());
@@ -75,9 +82,12 @@ public class CradlepointProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(parser.nextDouble(0));
position.setCourse(parser.nextDouble(0));
- parser.skip(4);
-
- position.set(Position.KEY_RSSI, parser.nextDouble());
+ position.set("carrid", parser.next());
+ position.set("serdis", parser.next());
+ position.set("rsrp", parser.next());
+ position.set("dbm", parser.next());
+ position.set("rsrq", parser.next());
+ position.set("ecio", parser.next());
return position;
}
diff --git a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/org/traccar/protocol/EasyTrackProtocolDecoder.java
index f44c91c4a..799254b65 100644
--- a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/EasyTrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
@@ -92,7 +93,7 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
position.setLongitude(parser.nextHexInt(0) / 600000.0);
}
- position.setSpeed(parser.nextHexInt(0) / 100.0);
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextHexInt(0) / 100.0));
position.setCourse(parser.nextHexInt(0) / 100.0);
position.set(Position.KEY_STATUS, parser.next());
diff --git a/src/org/traccar/protocol/EelinkProtocolDecoder.java b/src/org/traccar/protocol/EelinkProtocolDecoder.java
index 0f6551cc3..8d0f8016a 100644
--- a/src/org/traccar/protocol/EelinkProtocolDecoder.java
+++ b/src/org/traccar/protocol/EelinkProtocolDecoder.java
@@ -96,6 +96,22 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private void decodeStatus(Position position, int status) {
+ if (BitUtil.check(status, 1)) {
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 2));
+ }
+ if (BitUtil.check(status, 3)) {
+ position.set(Position.KEY_ARMED, BitUtil.check(status, 4));
+ }
+ if (BitUtil.check(status, 5)) {
+ position.set(Position.KEY_BLOCKED, !BitUtil.check(status, 6));
+ }
+ if (BitUtil.check(status, 7)) {
+ position.set(Position.KEY_CHARGE, BitUtil.check(status, 8));
+ }
+ position.set(Position.KEY_STATUS, status);
+ }
+
private Position decodeOld(DeviceSession deviceSession, ChannelBuffer buf, int type, int index) {
Position position = new Position();
@@ -115,27 +131,39 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
position.setValid((buf.readUnsignedByte() & 0x01) != 0);
- if (type == MSG_ALARM) {
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
- }
-
- if (buf.readableBytes() >= 2 * 5) {
+ if (type == MSG_GPS) {
- int status = buf.readUnsignedShort();
- if (BitUtil.check(status, 1)) {
- position.set(Position.KEY_IGNITION, BitUtil.check(status, 2));
+ if (buf.readableBytes() >= 2) {
+ decodeStatus(position, buf.readUnsignedShort());
}
- if (BitUtil.check(status, 7)) {
- position.set(Position.KEY_CHARGE, BitUtil.check(status, 8));
+
+ if (buf.readableBytes() >= 2 * 4) {
+
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+
+ position.set(Position.KEY_RSSI, buf.readUnsignedShort());
+
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+
}
- position.set(Position.KEY_STATUS, status);
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ } else if (type == MSG_ALARM) {
- position.set(Position.KEY_RSSI, buf.readUnsignedShort());
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+ } else if (type == MSG_STATE) {
+
+ int statusType = buf.readUnsignedByte();
+
+ position.set(Position.KEY_EVENT, statusType);
+
+ if (statusType == 0x01 || statusType == 0x02 || statusType == 0x03) {
+ buf.readUnsignedInt(); // device time
+ if (buf.readableBytes() >= 2) {
+ decodeStatus(position, buf.readUnsignedShort());
+ }
+ }
}
@@ -189,6 +217,45 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
buf.skipBytes(7); // bss2
}
+ if (buf.readableBytes() >= 2) {
+ int status = buf.readUnsignedShort();
+ position.setValid(BitUtil.check(status, 0));
+ if (BitUtil.check(status, 1)) {
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 2));
+ }
+ position.set(Position.KEY_STATUS, status);
+ }
+
+ if (buf.readableBytes() >= 2) {
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ }
+
+ if (buf.readableBytes() >= 4) {
+ position.set(Position.PREFIX_ADC + 0, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ }
+
+ if (buf.readableBytes() >= 4) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ }
+
+ if (buf.readableBytes() >= 4) {
+ buf.readUnsignedShort(); // gsm counter
+ buf.readUnsignedShort(); // gps counter
+ }
+
+ if (buf.readableBytes() >= 4) {
+ position.set(Position.KEY_STEPS, buf.readUnsignedShort());
+ buf.readUnsignedShort(); // walking time
+ }
+
+ if (buf.readableBytes() >= 12) {
+ position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedShort() / 256.0);
+ position.set("humidity", buf.readUnsignedShort() * 0.1);
+ position.set("illuminance", buf.readUnsignedInt() / 256.0);
+ position.set("co2", buf.readUnsignedInt());
+ }
+
return position;
}
@@ -212,6 +279,7 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
getDeviceSession(channel, remoteAddress, ChannelBuffers.hexDump(buf.readBytes(8)).substring(1));
} else {
+
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
return null;
@@ -221,7 +289,20 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
return decodeOld(deviceSession, buf, type, index);
} else if (type >= MSG_NORMAL && type <= MSG_OBD_CODE) {
return decodeNew(deviceSession, buf, index);
+ } else if (type == MSG_HEARTBEAT && buf.readableBytes() >= 2) {
+
+ Position position = new Position();
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setProtocol(getProtocolName());
+
+ getLastLocation(position, null);
+
+ decodeStatus(position, buf.readUnsignedShort());
+
+ return position;
+
}
+
}
return null;
diff --git a/src/org/traccar/protocol/EskyFrameDecoder.java b/src/org/traccar/protocol/EskyFrameDecoder.java
new file mode 100644
index 000000000..3175698fd
--- /dev/null
+++ b/src/org/traccar/protocol/EskyFrameDecoder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@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.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+
+public class EskyFrameDecoder extends FrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+
+ buf.readerIndex(buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 'E'));
+
+ int endIndex = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) 'E');
+ if (endIndex > 0) {
+ return buf.readBytes(endIndex - buf.readerIndex());
+ } else {
+ return buf.readBytes(buf.readableBytes()); // assume full frame
+ }
+ }
+
+}
diff --git a/src/org/traccar/protocol/EskyProtocol.java b/src/org/traccar/protocol/EskyProtocol.java
new file mode 100644
index 000000000..4c1d11f7d
--- /dev/null
+++ b/src/org/traccar/protocol/EskyProtocol.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@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.protocol;
+
+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.TrackerServer;
+
+import java.util.List;
+
+public class EskyProtocol extends BaseProtocol {
+
+ public EskyProtocol() {
+ super("esky");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new EskyFrameDecoder());
+ pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("objectDecoder", new EskyProtocolDecoder(EskyProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/EskyProtocolDecoder.java b/src/org/traccar/protocol/EskyProtocolDecoder.java
new file mode 100644
index 000000000..d524224af
--- /dev/null
+++ b/src/org/traccar/protocol/EskyProtocolDecoder.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@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.protocol;
+
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class EskyProtocolDecoder extends BaseProtocolDecoder {
+
+ public EskyProtocolDecoder(EskyProtocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("EO;") // header
+ .number("d+;") // index
+ .number("(d+);") // imei
+ .text("R;") // data type
+ .number("(d+)").text("+") // satellites
+ .number("(dd)(dd)(dd)") // date
+ .number("(dd)(dd)(dd)").text("+") // time
+ .number("(-?d+.d+)").text("+") // latitude
+ .number("(-?d+.d+)").text("+") // longitude
+ .number("(d+.d+)").text("+") // speed
+ .number("(d+)").text("+") // course
+ .text("0x").number("(d+)").text("+") // input
+ .number("(d+)").text("+") // message type
+ .number("(d+)").text("+") // odometer
+ .number("(d+)") // voltage
+ .any()
+ .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;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+
+ position.setValid(true);
+ position.setTime(parser.nextDateTime());
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromMps(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
+ position.set(Position.KEY_EVENT, parser.nextInt());
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+ position.set(Position.KEY_POWER, parser.nextInt());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/org/traccar/protocol/FifotrackProtocolDecoder.java
index f8f4fb078..304f6a2c3 100644
--- a/src/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -110,7 +110,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(adc[i], 16));
}
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
if (parser.hasNext()) {
String[] sensors = parser.next().split("\\|");
diff --git a/src/org/traccar/protocol/GenxProtocol.java b/src/org/traccar/protocol/GenxProtocol.java
new file mode 100644
index 000000000..2b5b1a43d
--- /dev/null
+++ b/src/org/traccar/protocol/GenxProtocol.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@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.protocol;
+
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder;
+import org.jboss.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class GenxProtocol extends BaseProtocol {
+
+ public GenxProtocol() {
+ super("genx");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024));
+ pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("objectDecoder", new GenxProtocolDecoder(GenxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/GenxProtocolDecoder.java b/src/org/traccar/protocol/GenxProtocolDecoder.java
new file mode 100644
index 000000000..3b716796c
--- /dev/null
+++ b/src/org/traccar/protocol/GenxProtocolDecoder.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@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.protocol;
+
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
+import org.traccar.DeviceSession;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.text.SimpleDateFormat;
+
+public class GenxProtocolDecoder extends BaseProtocolDecoder {
+
+ private int[] reportColumns;
+
+ public GenxProtocolDecoder(GenxProtocol protocol) {
+ super(protocol);
+ setReportColumns(Context.getConfig().getString(getProtocolName() + ".reportColumns", "1,2,3,4"));
+ }
+
+ public void setReportColumns(String format) {
+ String[] columns = format.split(",");
+ reportColumns = new int[columns.length];
+ for (int i = 0; i < columns.length; i++) {
+ reportColumns[i] = Integer.parseInt(columns[i]);
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String[] values = ((String) msg).split(",");
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setValid(true);
+
+ for (int i = 0; i < Math.min(values.length, reportColumns.length); i++) {
+ switch (reportColumns[i]) {
+ case 1:
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[i]);
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ }
+ break;
+ case 2:
+ position.setTime(new SimpleDateFormat("MM/dd/yy HH:mm:ss").parse(values[i]));
+ break;
+ case 3:
+ position.setLatitude(Double.parseDouble(values[i]));
+ break;
+ case 4:
+ position.setLongitude(Double.parseDouble(values[i]));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return position.getDeviceId() != 0 ? position : null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java b/src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java
new file mode 100644
index 000000000..071960e49
--- /dev/null
+++ b/src/org/traccar/protocol/Gl200BinaryProtocolDecoder.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@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.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.helper.BitBuffer;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Gl200BinaryProtocolDecoder extends BaseProtocolDecoder {
+
+ public Gl200BinaryProtocolDecoder(Gl200Protocol protocol) {
+ super(protocol);
+ }
+
+ private Date decodeTime(ChannelBuffer buf) {
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ return dateBuilder.getDate();
+ }
+
+ public static final int MSG_RSP_LCB = 3;
+ public static final int MSG_RSP_GEO = 8;
+ public static final int MSG_RSP_COMPRESSED = 100;
+
+ private List<Position> decodeLocation(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+
+ List<Position> positions = new LinkedList<>();
+
+ int type = buf.readUnsignedByte();
+
+ buf.readUnsignedInt(); // mask
+ buf.readUnsignedShort(); // length
+ buf.readUnsignedByte(); // device type
+ buf.readUnsignedShort(); // protocol version
+ buf.readUnsignedShort(); // firmware version
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.format("%015d", buf.readLong()));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int battery = buf.readUnsignedByte();
+ int power = buf.readUnsignedShort();
+
+ if (type == MSG_RSP_GEO) {
+ buf.readUnsignedByte(); // reserved
+ buf.readUnsignedByte(); // reserved
+ }
+
+ buf.readUnsignedByte(); // motion status
+ int satellites = buf.readUnsignedByte();
+
+ if (type != MSG_RSP_COMPRESSED) {
+ buf.readUnsignedByte(); // index
+ }
+
+ if (type == MSG_RSP_LCB) {
+ buf.readUnsignedByte(); // phone length
+ for (int b = buf.readUnsignedByte();; b = buf.readUnsignedByte()) {
+ if ((b & 0xf) == 0xf || (b & 0xf0) == 0xf0) {
+ break;
+ }
+ }
+ }
+
+ if (type == MSG_RSP_COMPRESSED) {
+
+ int count = buf.readUnsignedShort();
+
+ BitBuffer bits;
+ int speed = 0;
+ int heading = 0;
+ int latitude = 0;
+ int longitude = 0;
+ long time = 0;
+
+ for (int i = 0; i < count; i++) {
+
+ if (time > 0) {
+ time += 1;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ switch (BitUtil.from(buf.getUnsignedByte(buf.readerIndex()), 8 - 2)) {
+ case 1:
+ bits = new BitBuffer(buf.readBytes(3));
+ bits.readUnsigned(2); // point attribute
+ bits.readUnsigned(1); // fix type
+ speed = bits.readUnsigned(12);
+ heading = bits.readUnsigned(9);
+ longitude = buf.readInt();
+ latitude = buf.readInt();
+ if (time == 0) {
+ time = buf.readUnsignedInt();
+ }
+ break;
+ case 2:
+ bits = new BitBuffer(buf.readBytes(5));
+ bits.readUnsigned(2); // point attribute
+ bits.readUnsigned(1); // fix type
+ speed += bits.readSigned(7);
+ heading += bits.readSigned(7);
+ longitude += bits.readSigned(12);
+ latitude += bits.readSigned(11);
+ break;
+ default:
+ buf.readUnsignedByte(); // invalid or same
+ continue;
+ }
+
+ position.setValid(true);
+ position.setTime(new Date(time * 1000));
+ position.setSpeed(UnitsConverter.knotsFromKph(speed * 0.1));
+ position.setCourse(heading);
+ position.setLongitude(longitude * 0.000001);
+ position.setLatitude(latitude * 0.000001);
+
+ positions.add(position);
+
+ }
+
+ } else {
+
+ int count = buf.readUnsignedByte();
+
+ for (int i = 0; i < count; i++) {
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_BATTERY_LEVEL, battery);
+ position.set(Position.KEY_POWER, power);
+ position.set(Position.KEY_SATELLITES, satellites);
+
+ int hdop = buf.readUnsignedByte();
+ position.setValid(hdop > 0);
+ position.set(Position.KEY_HDOP, hdop);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedMedium() * 0.1));
+ position.setCourse(buf.readUnsignedShort());
+ position.setAltitude(buf.readShort());
+ position.setLongitude(buf.readInt() * 0.000001);
+ position.setLatitude(buf.readInt() * 0.000001);
+
+ position.setTime(decodeTime(buf));
+
+ position.setNetwork(new Network(CellTower.from(
+ buf.readUnsignedShort(), buf.readUnsignedShort(),
+ buf.readUnsignedShort(), buf.readUnsignedShort())));
+
+ buf.readUnsignedByte(); // reserved
+
+ positions.add(position);
+
+ }
+
+ }
+
+ return positions;
+ }
+
+ public static final int MSG_EVT_BPL = 6;
+ public static final int MSG_EVT_VGN = 45;
+ public static final int MSG_EVT_VGF = 46;
+ public static final int MSG_EVT_UPD = 15;
+ public static final int MSG_EVT_IDF = 17;
+ public static final int MSG_EVT_GSS = 21;
+ public static final int MSG_EVT_GES = 26;
+ public static final int MSG_EVT_GPJ = 31;
+ public static final int MSG_EVT_RMD = 35;
+ public static final int MSG_EVT_JDS = 33;
+ public static final int MSG_EVT_CRA = 23;
+ public static final int MSG_EVT_UPC = 34;
+
+ private Position decodeEvent(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ int type = buf.readUnsignedByte();
+
+ buf.readUnsignedInt(); // mask
+ buf.readUnsignedShort(); // length
+ buf.readUnsignedByte(); // device type
+ buf.readUnsignedShort(); // protocol version
+
+ position.set(Position.KEY_VERSION_FW, String.valueOf(buf.readUnsignedShort()));
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.format("%015d", buf.readLong()));
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ position.set(Position.KEY_POWER, buf.readUnsignedShort());
+
+ buf.readUnsignedByte(); // motion status
+
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+
+ switch (type) {
+ case MSG_EVT_BPL:
+ buf.readUnsignedShort(); // backup battery voltage
+ break;
+ case MSG_EVT_VGN:
+ case MSG_EVT_VGF:
+ buf.readUnsignedShort(); // reserved
+ buf.readUnsignedByte(); // report type
+ buf.readUnsignedInt(); // ignition duration
+ break;
+ case MSG_EVT_UPD:
+ buf.readUnsignedShort(); // code
+ buf.readUnsignedByte(); // retry
+ break;
+ case MSG_EVT_IDF:
+ buf.readUnsignedInt(); // idling duration
+ break;
+ case MSG_EVT_GSS:
+ buf.readUnsignedByte(); // gps signal status
+ buf.readUnsignedInt(); // reserved
+ break;
+ case MSG_EVT_GES:
+ buf.readUnsignedShort(); // trigger geo id
+ buf.readUnsignedByte(); // trigger geo enable
+ buf.readUnsignedByte(); // trigger mode
+ buf.readUnsignedInt(); // radius
+ buf.readUnsignedInt(); // check interval
+ break;
+ case MSG_EVT_GPJ:
+ buf.readUnsignedByte(); // cw jamming value
+ buf.readUnsignedByte(); // gps jamming state
+ break;
+ case MSG_EVT_RMD:
+ buf.readUnsignedByte(); // roaming state
+ break;
+ case MSG_EVT_JDS:
+ buf.readUnsignedByte(); // jamming state
+ break;
+ case MSG_EVT_CRA:
+ buf.readUnsignedByte(); // crash counter
+ break;
+ case MSG_EVT_UPC:
+ buf.readUnsignedByte(); // command id
+ buf.readUnsignedShort(); // result
+ break;
+ default:
+ break;
+ }
+
+ buf.readUnsignedByte(); // count
+
+ int hdop = buf.readUnsignedByte();
+ position.setValid(hdop > 0);
+ position.set(Position.KEY_HDOP, hdop);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedMedium() * 0.1));
+ position.setCourse(buf.readUnsignedShort());
+ position.setAltitude(buf.readShort());
+ position.setLongitude(buf.readInt() * 0.000001);
+ position.setLatitude(buf.readInt() * 0.000001);
+
+ position.setTime(decodeTime(buf));
+
+ position.setNetwork(new Network(CellTower.from(
+ buf.readUnsignedShort(), buf.readUnsignedShort(),
+ buf.readUnsignedShort(), buf.readUnsignedShort())));
+
+ buf.readUnsignedByte(); // reserved
+
+ return position;
+ }
+
+ public static final int MSG_INF_GPS = 2;
+ public static final int MSG_INF_CID = 4;
+ public static final int MSG_INF_CSQ = 5;
+ public static final int MSG_INF_VER = 6;
+ public static final int MSG_INF_BAT = 7;
+ public static final int MSG_INF_TMZ = 9;
+ public static final int MSG_INF_GIR = 10;
+
+ private Position decodeInformation(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ int type = buf.readUnsignedByte();
+
+ buf.readUnsignedInt(); // mask
+ buf.readUnsignedShort(); // length
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.format("%015d", buf.readLong()));
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ buf.readUnsignedByte(); // device type
+ buf.readUnsignedShort(); // protocol version
+
+ position.set(Position.KEY_VERSION_FW, String.valueOf(buf.readUnsignedShort()));
+
+ if (type == MSG_INF_VER) {
+ buf.readUnsignedShort(); // hardware version
+ buf.readUnsignedShort(); // mcu version
+ buf.readUnsignedShort(); // reserved
+ }
+
+ buf.readUnsignedByte(); // motion status
+ buf.readUnsignedByte(); // reserved
+
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+
+ buf.readUnsignedByte(); // mode
+ buf.skipBytes(7); // last fix time
+ buf.readUnsignedByte(); // reserved
+ buf.readUnsignedByte();
+ buf.readUnsignedShort(); // response report mask
+ buf.readUnsignedShort(); // ign interval
+ buf.readUnsignedShort(); // igf interval
+ buf.readUnsignedInt(); // reserved
+ buf.readUnsignedByte(); // reserved
+
+ if (type == MSG_INF_BAT) {
+ position.set(Position.KEY_CHARGE, buf.readUnsignedByte() != 0);
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ }
+
+ buf.skipBytes(10); // iccid
+
+ if (type == MSG_INF_CSQ) {
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ buf.readUnsignedByte();
+ }
+
+ buf.readUnsignedByte(); // time zone flags
+ buf.readUnsignedShort(); // time zone offset
+
+ if (type == MSG_INF_GIR) {
+ buf.readUnsignedByte(); // gir trigger
+ buf.readUnsignedByte(); // cell number
+ position.setNetwork(new Network(CellTower.from(
+ buf.readUnsignedShort(), buf.readUnsignedShort(),
+ buf.readUnsignedShort(), buf.readUnsignedShort())));
+ buf.readUnsignedByte(); // ta
+ buf.readUnsignedByte(); // rx level
+ }
+
+ getLastLocation(position, decodeTime(buf));
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ switch (buf.readBytes(4).toString(StandardCharsets.US_ASCII)) {
+ case "+RSP":
+ return decodeLocation(channel, remoteAddress, buf);
+ case "+INF":
+ return decodeInformation(channel, remoteAddress, buf);
+ case "+EVT":
+ return decodeEvent(channel, remoteAddress, buf);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gl200FrameDecoder.java b/src/org/traccar/protocol/Gl200FrameDecoder.java
new file mode 100644
index 000000000..960c3779a
--- /dev/null
+++ b/src/org/traccar/protocol/Gl200FrameDecoder.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@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.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Gl200FrameDecoder extends FrameDecoder {
+
+ private static final int MINIMUM_LENGTH = 11;
+
+ private static final Set<String> BINARY_HEADERS = new HashSet<>(
+ Arrays.asList("+RSP", "+BSP", "+EVT", "+BVT", "+INF", "+BNF", "+HBD", "+CRD", "+BRD"));
+
+ public static boolean isBinary(ChannelBuffer buf) {
+ String header = buf.toString(buf.readerIndex(), 4, StandardCharsets.US_ASCII);
+ if (header.equals("+ACK")) {
+ return buf.getByte(buf.readerIndex() + header.length()) != (byte) ':';
+ } else {
+ return BINARY_HEADERS.contains(header);
+ }
+ }
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
+
+ if (buf.readableBytes() < MINIMUM_LENGTH) {
+ return null;
+ }
+
+ if (isBinary(buf)) {
+
+ int length;
+ switch (buf.toString(buf.readerIndex(), 4, StandardCharsets.US_ASCII)) {
+ case "+ACK":
+ length = buf.getUnsignedByte(buf.readerIndex() + 6);
+ break;
+ case "+INF":
+ case "+BNF":
+ length = buf.getUnsignedShort(buf.readerIndex() + 7);
+ break;
+ case "+HBD":
+ length = buf.getUnsignedByte(buf.readerIndex() + 5);
+ break;
+ case "+CRD":
+ case "+BRD":
+ length = buf.getUnsignedShort(buf.readerIndex() + 6);
+ break;
+ default:
+ length = buf.getUnsignedShort(buf.readerIndex() + 9);
+ break;
+ }
+
+ if (buf.readableBytes() >= length) {
+ return buf.readBytes(length);
+ }
+
+ } else {
+
+ int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '$');
+ if (endIndex < 0) {
+ endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0);
+ }
+ if (endIndex > 0) {
+ ChannelBuffer frame = buf.readBytes(endIndex - buf.readerIndex());
+ buf.readByte(); // delimiter
+ return frame;
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Gl200Protocol.java b/src/org/traccar/protocol/Gl200Protocol.java
index b3743042c..799d7fe36 100644
--- a/src/org/traccar/protocol/Gl200Protocol.java
+++ b/src/org/traccar/protocol/Gl200Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,10 +18,8 @@ package org.traccar.protocol;
import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
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;
@@ -44,9 +42,8 @@ public class Gl200Protocol extends BaseProtocol {
serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
- pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(4096, "$", "\0"));
+ pipeline.addLast("frameDecoder", new Gl200FrameDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder());
pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this));
}
@@ -55,7 +52,6 @@ public class Gl200Protocol extends BaseProtocol {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("stringEncoder", new StringEncoder());
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("objectEncoder", new Gl200ProtocolEncoder());
pipeline.addLast("objectDecoder", new Gl200ProtocolDecoder(Gl200Protocol.this));
}
diff --git a/src/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/org/traccar/protocol/Gl200ProtocolDecoder.java
index a3062c942..0de7bb926 100644
--- a/src/org/traccar/protocol/Gl200ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gl200ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,864 +15,34 @@
*/
package org.traccar.protocol;
+import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
-import org.traccar.DeviceSession;
-import org.traccar.helper.BitUtil;
-import org.traccar.helper.Parser;
-import org.traccar.helper.PatternBuilder;
-import org.traccar.helper.UnitsConverter;
-import org.traccar.model.CellTower;
-import org.traccar.model.Network;
-import org.traccar.model.Position;
-import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
- private boolean ignoreFixTime;
+ private final Gl200TextProtocolDecoder textProtocolDecoder;
+ private final Gl200BinaryProtocolDecoder binaryProtocolDecoder;
public Gl200ProtocolDecoder(Gl200Protocol protocol) {
super(protocol);
-
- ignoreFixTime = Context.getConfig().getBoolean(getProtocolName() + ".ignoreFixTime");
- }
-
- private static final Pattern PATTERN_ACK = new PatternBuilder()
- .text("+ACK:GT")
- .expression("...,") // type
- .number("([0-9A-Z]{2}xxxx),") // protocol version
- .number("(d{15}|x{14}),") // imei
- .any().text(",")
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(xxxx)") // counter
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_INF = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTINF,")
- .number("[0-9A-Z]{2}xxxx,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("(?:[0-9A-Z]{17},)?") // vin
- .expression("(?:[^,]+)?,") // device name
- .number("(xx),") // state
- .expression("(?:[0-9F]{20})?,") // iccid
- .number("d{1,2},")
- .number("d{1,2},")
- .expression("[01],") // external power
- .number("([d.]+)?,") // odometer or external power
- .number("d*,") // backup battery or lightness
- .number("(d+.d+),") // battery
- .expression("([01]),") // charging
- .number("(?:d),") // led
- .number("(?:d)?,") // gps on need
- .number("(?:d)?,") // gps antenna type
- .number("(?:d),").optional() // gps antenna state
- .number("d{14},") // last fix time
- .groupBegin()
- .number("(d+),") // battery percentage
- .expression("[01]?,") // flash type
- .number("(-?[d.]+)?,,,") // temperature
- .or()
- .expression("(?:[01])?,").optional() // pin15 mode
- .number("(d+)?,") // adc1
- .number("(d+)?,").optional() // adc2
- .number("(xx)?,") // digital input
- .number("(xx)?,") // digital output
- .number("[-+]dddd,") // timezone
- .expression("[01],") // daylight saving
- .groupEnd()
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(xxxx)") // counter
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_VER = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTVER,")
- .number("[0-9A-Z]{2}xxxx,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,") // device name
- .expression("([^,]*),") // device type
- .number("(xxxx),") // firmware version
- .number("(xxxx),") // hardware version
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(xxxx)") // counter
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_LOCATION = new PatternBuilder()
- .number("(d{1,2})?,") // hdop
- .number("(d{1,3}.d)?,") // speed
- .number("(d{1,3})?,") // course
- .number("(-?d{1,5}.d)?,") // altitude
- .number("(-?d{1,3}.d{6})?,") // longitude
- .number("(-?d{1,2}.d{6})?,") // latitude
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(d+)?,") // mcc
- .number("(d+)?,") // mnc
- .groupBegin()
- .number("(d+),") // lac
- .number("(d+),") // cid
- .or()
- .number("(x+)?,") // lac
- .number("(x+)?,") // cid
- .groupEnd()
- .number("(?:d+|(d+.d))?,") // odometer
- .compile();
-
- private static final Pattern PATTERN_OBD = new PatternBuilder()
- .text("+RESP:GTOBD,")
- .number("[0-9A-Z]{2}xxxx,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("(?:[0-9A-Z]{17})?,") // vin
- .expression("[^,]{0,20},") // device name
- .expression("[01],") // report type
- .number("x{1,8},") // report mask
- .expression("(?:[0-9A-Z]{17})?,") // vin
- .number("[01],") // obd connect
- .number("(?:d{1,5})?,") // obd voltage
- .number("(?:x{8})?,") // support pids
- .number("(d{1,5})?,") // engine rpm
- .number("(d{1,3})?,") // speed
- .number("(-?d{1,3})?,") // coolant temp
- .number("(d+.?d*|Inf|NaN)?,") // fuel consumption
- .number("(d{1,5})?,") // dtcs cleared distance
- .number("(?:d{1,5})?,")
- .expression("([01])?,") // obd connect
- .number("(d{1,3})?,") // number of dtcs
- .number("(x*),") // dtcs
- .number("(d{1,3})?,") // throttle
- .number("(?:d{1,3})?,") // engine load
- .number("(d{1,3})?,") // fuel level
- .expression("(?:[0-9A],)?") // obd protocol
- .number("(d+),") // odometer
- .expression(PATTERN_LOCATION.pattern())
- .number("(d{1,7}.d)?,") // odometer
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_FRI = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTFRI,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("(?:([0-9A-Z]{17}),)?") // vin
- .expression("[^,]*,") // device name
- .number("(d+)?,") // power
- .number("d{1,2},") // report type
- .number("d{1,2},") // count
- .expression("((?:")
- .expression(PATTERN_LOCATION.pattern())
- .expression(")+)")
- .groupBegin()
- .number("(d{1,7}.d)?,").optional() // odometer
- .number("(d{1,3})?,") // battery
- .or()
- .number("(d{1,7}.d)?,") // odometer
- .number("(d{5}:dd:dd)?,") // hour meter
- .number("(x+)?,") // adc 1
- .number("(x+)?,") // adc 2
- .number("(d{1,3})?,") // battery
- .number("(?:(xx)(xx)(xx))?,") // device status
- .number("(d+)?,") // rpm
- .number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption
- .number("(d+)?,") // fuel level
- .groupEnd()
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_ERI = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTERI,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,") // device name
- .number("x{8},") // mask
- .number("(d+)?,") // power
- .number("d{1,2},") // report type
- .number("d{1,2},") // count
- .expression("((?:")
- .expression(PATTERN_LOCATION.pattern())
- .expression(")+)")
- .number("(d{1,7}.d)?,") // odometer
- .number("(d{5}:dd:dd)?,") // hour meter
- .number("(x+)?,") // adc 1
- .number("(x+)?,") // adc 2
- .number("(d{1,3})?,") // battery
- .number("(?:(xx)(xx)(xx))?,") // device status
- .expression("(.*)") // additional data
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_IGN = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTIG[NF],")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,") // device name
- .number("d+,") // ignition off duration
- .expression(PATTERN_LOCATION.pattern())
- .number("(d{5}:dd:dd)?,") // hour meter
- .number("(d{1,7}.d)?,") // odometer
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_IDA = new PatternBuilder()
- .text("+RESP:GTIDA,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,,") // device name
- .number("([^,]+),") // rfid
- .expression("[01],") // report type
- .number("1,") // count
- .expression(PATTERN_LOCATION.pattern())
- .number("(d+.d),") // odometer
- .text(",,,,")
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_WIF = new PatternBuilder()
- .text("+RESP:GTWIF,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,") // device name
- .number("(d+),") // count
- .number("((?:x{12},-?d+,,,,)+),,,,") // wifi
- .number("(d{1,3}),") // battery
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_GSM = new PatternBuilder()
- .text("+RESP:GTGSM,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("(?:STR|CTN|NMR|RTL),") // fix type
- .expression("(.*)") // cells
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GT...,")
- .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
- .number("(d{15}|x{14}),") // imei
- .expression("[^,]*,") // device name
- .number("d*,")
- .number("(d{1,2}),") // report type
- .number("d{1,2},") // count
- .expression(PATTERN_LOCATION.pattern())
- .groupBegin()
- .number("(d{1,7}.d)?,").optional() // odometer
- .number("(d{1,3})?,") // battery
- .or()
- .number("(d{1,7}.d)?,") // odometer
- .groupEnd()
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)") // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private static final Pattern PATTERN_BASIC = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF)").text(":")
- .expression("GT...,")
- .number("(?:[0-9A-Z]{2}xxxx)?,").optional() // protocol version
- .number("(d{15}|x{14}),") // imei
- .any()
- .number("(d{1,2})?,") // hdop
- .number("(d{1,3}.d)?,") // speed
- .number("(d{1,3})?,") // course
- .number("(-?d{1,5}.d)?,") // altitude
- .number("(-?d{1,3}.d{6})?,") // longitude
- .number("(-?d{1,2}.d{6})?,") // latitude
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(d+),") // mcc
- .number("(d+),") // mnc
- .number("(x+),") // lac
- .number("(x+),").optional(4) // cell
- .any()
- .number("(dddd)(dd)(dd)") // date (yyyymmdd)
- .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
- .text(",")
- .number("(xxxx)") // count number
- .text("$").optional()
- .compile();
-
- private Object decodeAck(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
- Parser parser = new Parser(PATTERN_ACK, sentence);
- if (parser.matches()) {
- String protocolVersion = parser.next();
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
- if (type.equals("HBD")) {
- if (channel != null) {
- parser.skip(6);
- channel.write("+SACK:GTHBD," + protocolVersion + "," + parser.next() + "$", remoteAddress);
- }
- } else {
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- getLastLocation(position, parser.nextDateTime());
- position.setValid(false);
- position.set(Position.KEY_RESULT, "Command " + type + " accepted");
- return position;
- }
- }
- return null;
- }
-
- private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) {
- if (parser.matches()) {
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession != null) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
- return position;
- }
- }
- return null;
- }
-
- private void decodeDeviceTime(Position position, Parser parser) {
- if (parser.hasNext(6)) {
- if (ignoreFixTime) {
- position.setTime(parser.nextDateTime());
- } else {
- position.setDeviceTime(parser.nextDateTime());
- }
- }
- }
-
- private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_INF, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- position.set(Position.KEY_STATUS, parser.next());
-
- parser.next(); // odometer or external power
-
- position.set(Position.KEY_BATTERY, parser.nextDouble(0));
- position.set(Position.KEY_CHARGE, parser.nextInt(0) == 1);
-
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
-
- position.set(Position.PREFIX_TEMP + 1, parser.next());
-
- position.set(Position.PREFIX_ADC + 1, parser.next());
- position.set(Position.PREFIX_ADC + 2, parser.next());
-
- position.set(Position.KEY_INPUT, parser.next());
- position.set(Position.KEY_OUTPUT, parser.next());
-
- getLastLocation(position, parser.nextDateTime());
-
- position.set(Position.KEY_INDEX, parser.nextHexInt(0));
-
- return position;
- }
-
- private Object decodeVer(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_VER, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- position.set("deviceType", parser.next());
- position.set(Position.KEY_VERSION_FW, parser.nextHexInt(0));
- position.set(Position.KEY_VERSION_HW, parser.nextHexInt(0));
-
- getLastLocation(position, parser.nextDateTime());
-
- return position;
- }
-
- private void decodeLocation(Position position, Parser parser) {
- int hdop = parser.nextInt(0);
- position.setValid(hdop > 0);
- position.set(Position.KEY_HDOP, hdop);
-
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
- position.setAltitude(parser.nextDouble(0));
-
- if (parser.hasNext(8)) {
- position.setValid(true);
- position.setLongitude(parser.nextDouble(0));
- position.setLatitude(parser.nextDouble(0));
- position.setTime(parser.nextDateTime());
- } else {
- getLastLocation(position, null);
- }
-
- if (parser.hasNext(6)) {
- int mcc = parser.nextInt(0);
- int mnc = parser.nextInt(0);
- if (parser.hasNext(2)) {
- position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(0), parser.nextInt(0))));
- }
- if (parser.hasNext(2)) {
- position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(0), parser.nextHexInt(0))));
- }
- }
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- }
-
- private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_OBD, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- position.set(Position.KEY_RPM, parser.nextInt());
- position.set(Position.KEY_OBD_SPEED, parser.nextInt());
- position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
- position.set(Position.KEY_FUEL_CONSUMPTION, parser.next());
- position.set("dtcsClearedDistance", parser.nextInt());
- position.set("odbConnect", parser.nextInt(0) == 1);
- position.set("dtcsNumber", parser.nextInt());
- position.set("dtcsCodes", parser.next());
- position.set(Position.KEY_THROTTLE, parser.nextInt());
- position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
- position.set(Position.KEY_OBD_ODOMETER, parser.nextInt(0) * 1000);
-
- decodeLocation(position, parser);
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
-
- decodeDeviceTime(position, parser);
-
- return position;
- }
-
- private void decodeStatus(Position position, Parser parser) {
- if (parser.hasNext(3)) {
- int ignition = parser.nextHexInt(0);
- if (BitUtil.check(ignition, 4)) {
- position.set(Position.KEY_IGNITION, false);
- } else if (BitUtil.check(ignition, 5)) {
- position.set(Position.KEY_IGNITION, true);
- }
- position.set(Position.KEY_INPUT, parser.nextHexInt(0));
- position.set(Position.KEY_OUTPUT, parser.nextHexInt(0));
- }
- }
-
- private Object decodeFri(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_FRI, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
-
- LinkedList<Position> positions = new LinkedList<>();
-
- String vin = parser.next();
- int power = parser.nextInt(0);
-
- Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
- while (itemParser.find()) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.set(Position.KEY_VIN, vin);
-
- decodeLocation(position, itemParser);
-
- positions.add(position);
- }
-
- Position position = positions.getLast();
-
- decodeLocation(position, parser);
-
- // power value only on some devices
- if (power > 10) {
- position.set(Position.KEY_POWER, power);
- }
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_HOURS, parser.next());
- position.set(Position.PREFIX_ADC + 1, parser.next());
- position.set(Position.PREFIX_ADC + 2, parser.next());
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
-
- decodeStatus(position, parser);
-
- position.set(Position.KEY_RPM, parser.nextInt());
- position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
-
- decodeDeviceTime(position, parser);
-
- return positions;
- }
-
- private Object decodeEri(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_ERI, sentence);
- if (!parser.matches()) {
- return null;
- }
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
- }
-
- LinkedList<Position> positions = new LinkedList<>();
-
- int power = parser.nextInt(0);
-
- Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
- while (itemParser.find()) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- decodeLocation(position, itemParser);
-
- positions.add(position);
- }
-
- Position position = positions.getLast();
-
- decodeLocation(position, parser);
-
- position.set(Position.KEY_POWER, power);
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_HOURS, parser.next());
- position.set(Position.PREFIX_ADC + 1, parser.next());
- position.set(Position.PREFIX_ADC + 2, parser.next());
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
-
- decodeStatus(position, parser);
-
- int index = 0;
- String[] data = parser.next().split(",");
- if (data.length > 1) {
- int deviceType = Integer.parseInt(data[index++]);
- if (deviceType == 2) {
- int deviceCount = Integer.parseInt(data[index++]);
- for (int i = 1; i <= deviceCount; i++) {
- index++; // id
- index++; // type
- position.set(Position.PREFIX_TEMP + i, Short.parseShort(data[index++], 16) * 0.0625);
- }
- }
- }
-
- decodeDeviceTime(position, parser);
-
- return positions;
- }
-
- private Object decodeIgn(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_IGN, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- decodeLocation(position, parser);
-
- position.set(Position.KEY_HOURS, parser.next());
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
-
- decodeDeviceTime(position, parser);
-
- return position;
- }
-
- private Object decodeIda(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_IDA, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- position.set(Position.KEY_RFID, parser.next());
-
- decodeLocation(position, parser);
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
-
- decodeDeviceTime(position, parser);
-
- return position;
- }
-
- private Object decodeWif(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_WIF, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- getLastLocation(position, null);
-
- Network network = new Network();
-
- parser.nextInt(0); // count
- Matcher matcher = Pattern.compile("([0-9a-fA-F]{12}),(-?\\d+),,,,").matcher(parser.next());
- while (matcher.find()) {
- String mac = matcher.group(1).replaceAll("(..)", "$1:");
- network.addWifiAccessPoint(WifiAccessPoint.from(
- mac.substring(0, mac.length() - 1), Integer.parseInt(matcher.group(2))));
- }
-
- position.setNetwork(network);
-
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
-
- return position;
- }
-
- private Object decodeGsm(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN_GSM, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- getLastLocation(position, null);
-
- Network network = new Network();
-
- String[] data = parser.next().split(",");
- for (int i = 0; i < 6; i++) {
- if (!data[i * 6].isEmpty()) {
- network.addCellTower(CellTower.from(
- Integer.parseInt(data[i * 6]), Integer.parseInt(data[i * 6 + 1]),
- Integer.parseInt(data[i * 6 + 2], 16), Integer.parseInt(data[i * 6 + 3], 16),
- Integer.parseInt(data[i * 6 + 4])));
- }
- }
-
- position.setNetwork(network);
-
- return position;
- }
-
- private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
- Parser parser = new Parser(PATTERN, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- int reportType = parser.nextInt(0);
- if (type.equals("NMR")) {
- position.set(Position.KEY_MOTION, reportType == 1);
- } else if (type.equals("SOS")) {
- position.set(Position.KEY_ALARM, Position.ALARM_SOS);
- }
-
- decodeLocation(position, parser);
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
-
- position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
-
- decodeDeviceTime(position, parser);
-
- if (Context.getConfig().getBoolean(getProtocolName() + ".ack") && channel != null) {
- channel.write("+SACK:" + parser.next() + "$", remoteAddress);
- }
-
- return position;
- }
-
- private Object decodeBasic(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
- Parser parser = new Parser(PATTERN_BASIC, sentence);
- Position position = initPosition(parser, channel, remoteAddress);
- if (position == null) {
- return null;
- }
-
- int hdop = parser.nextInt(0);
- position.setValid(hdop > 0);
- position.set(Position.KEY_HDOP, hdop);
-
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
- position.setAltitude(parser.nextDouble(0));
-
- if (parser.hasNext(2)) {
- position.setLongitude(parser.nextDouble(0));
- position.setLatitude(parser.nextDouble(0));
- } else {
- getLastLocation(position, null);
- }
-
- if (parser.hasNext(6)) {
- position.setTime(parser.nextDateTime());
- }
-
- if (parser.hasNext(4)) {
- position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0))));
- }
-
- decodeDeviceTime(position, parser);
-
- switch (type) {
- case "PNA":
- position.set(Position.KEY_ALARM, Position.ALARM_POWER_ON);
- break;
- case "PFA":
- position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF);
- break;
- case "EPN":
- position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED);
- break;
- case "EPF":
- position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
- break;
- case "BPL":
- position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
- break;
- case "STT":
- position.set(Position.KEY_ALARM, Position.ALARM_MOVEMENT);
- break;
- case "SWG":
- position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE);
- break;
- case "TMP":
- case "TEM":
- position.set(Position.KEY_ALARM, Position.ALARM_TEMPERATURE);
- break;
- case "JDR":
- case "JDS":
- position.set(Position.KEY_ALARM, Position.ALARM_JAMMING);
- break;
- default:
- break;
- }
-
- return position;
+ textProtocolDecoder = new Gl200TextProtocolDecoder(protocol);
+ binaryProtocolDecoder = new Gl200BinaryProtocolDecoder(protocol);
}
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- String sentence = (String) msg;
+ ChannelBuffer buf = (ChannelBuffer) msg;
- int typeIndex = sentence.indexOf(":GT");
- if (typeIndex < 0) {
- return null;
- }
-
- Object result;
- String type = sentence.substring(typeIndex + 3, typeIndex + 6);
- if (sentence.startsWith("+ACK")) {
- result = decodeAck(channel, remoteAddress, sentence, type);
+ if (Gl200FrameDecoder.isBinary(buf)) {
+ return binaryProtocolDecoder.decode(channel, remoteAddress, msg);
} else {
- switch (type) {
- case "INF":
- result = decodeInf(channel, remoteAddress, sentence);
- break;
- case "OBD":
- result = decodeObd(channel, remoteAddress, sentence);
- break;
- case "FRI":
- result = decodeFri(channel, remoteAddress, sentence);
- break;
- case "ERI":
- result = decodeEri(channel, remoteAddress, sentence);
- break;
- case "IGN":
- case "IGF":
- result = decodeIgn(channel, remoteAddress, sentence);
- break;
- case "IDA":
- result = decodeIda(channel, remoteAddress, sentence);
- break;
- case "WIF":
- result = decodeWif(channel, remoteAddress, sentence);
- break;
- case "GSM":
- result = decodeGsm(channel, remoteAddress, sentence);
- break;
- case "VER":
- result = decodeVer(channel, remoteAddress, sentence);
- break;
- default:
- result = decodeOther(channel, remoteAddress, sentence, type);
- break;
- }
-
- if (result == null) {
- result = decodeBasic(channel, remoteAddress, sentence, type);
- }
-
- if (result != null) {
- if (result instanceof Position) {
- ((Position) result).set(Position.KEY_TYPE, type);
- } else {
- for (Position p : (List<Position>) result) {
- p.set(Position.KEY_TYPE, type);
- }
- }
- }
+ return textProtocolDecoder.decode(channel, remoteAddress, msg);
}
-
- return result;
}
}
diff --git a/src/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java
new file mode 100644
index 000000000..2f03cbb8f
--- /dev/null
+++ b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -0,0 +1,917 @@
+/*
+ * Copyright 2012 - 2017 Anton Tananaev (anton@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.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
+import org.traccar.DeviceSession;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
+
+ private boolean ignoreFixTime;
+
+ public Gl200TextProtocolDecoder(Gl200Protocol protocol) {
+ super(protocol);
+
+ ignoreFixTime = Context.getConfig().getBoolean(getProtocolName() + ".ignoreFixTime");
+ }
+
+ private static final Pattern PATTERN_ACK = new PatternBuilder()
+ .text("+ACK:GT")
+ .expression("...,") // type
+ .number("([0-9A-Z]{2}xxxx),") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .any().text(",")
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(xxxx)") // counter
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_INF = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTINF,")
+ .number("[0-9A-Z]{2}xxxx,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:[0-9A-Z]{17},)?") // vin
+ .expression("(?:[^,]+)?,") // device name
+ .number("(xx),") // state
+ .expression("(?:[0-9Ff]{20})?,") // iccid
+ .number("(d{1,2}),") // rssi
+ .number("d{1,2},")
+ .expression("[01],") // external power
+ .number("([d.]+)?,") // odometer or external power
+ .number("d*,") // backup battery or lightness
+ .number("(d+.d+),") // battery
+ .expression("([01]),") // charging
+ .number("(?:d),") // led
+ .number("(?:d)?,") // gps on need
+ .number("(?:d)?,") // gps antenna type
+ .number("(?:d)?,").optional() // gps antenna state
+ .number("d{14},") // last fix time
+ .groupBegin()
+ .number("(d+),") // battery percentage
+ .number("[d.]*,") // flash type / power
+ .number("(-?[d.]+)?,,,") // temperature
+ .or()
+ .expression("(?:[01])?,").optional() // pin15 mode
+ .number("(d+)?,") // adc1
+ .number("(d+)?,").optional() // adc2
+ .number("(xx)?,") // digital input
+ .number("(xx)?,") // digital output
+ .number("[-+]dddd,") // timezone
+ .expression("[01],") // daylight saving
+ .groupEnd()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(xxxx)") // counter
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_VER = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTVER,")
+ .number("[0-9A-Z]{2}xxxx,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .expression("([^,]*),") // device type
+ .number("(xxxx),") // firmware version
+ .number("(xxxx),") // hardware version
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(xxxx)") // counter
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_LOCATION = new PatternBuilder()
+ .number("(d{1,2})?,") // hdop
+ .number("(d{1,3}.d)?,") // speed
+ .number("(d{1,3})?,") // course
+ .number("(-?d{1,5}.d)?,") // altitude
+ .number("(-?d{1,3}.d{6})?,") // longitude
+ .number("(-?d{1,2}.d{6})?,") // latitude
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(d+)?,") // mcc
+ .number("(d+)?,") // mnc
+ .groupBegin()
+ .number("(d+),") // lac
+ .number("(d+),") // cid
+ .or()
+ .number("(x+)?,") // lac
+ .number("(x+)?,") // cid
+ .groupEnd()
+ .number("(?:d+|(d+.d))?,") // odometer
+ .compile();
+
+ private static final Pattern PATTERN_OBD = new PatternBuilder()
+ .text("+RESP:GTOBD,")
+ .number("[0-9A-Z]{2}xxxx,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:[0-9A-Z]{17})?,") // vin
+ .expression("[^,]{0,20},") // device name
+ .expression("[01],") // report type
+ .number("x{1,8},") // report mask
+ .expression("(?:[0-9A-Z]{17})?,") // vin
+ .number("[01],") // obd connect
+ .number("(?:d{1,5})?,") // obd voltage
+ .number("(?:x{8})?,") // support pids
+ .number("(d{1,5})?,") // engine rpm
+ .number("(d{1,3})?,") // speed
+ .number("(-?d{1,3})?,") // coolant temp
+ .number("(d+.?d*|Inf|NaN)?,") // fuel consumption
+ .number("(d{1,5})?,") // dtcs cleared distance
+ .number("(?:d{1,5})?,")
+ .expression("([01])?,") // obd connect
+ .number("(d{1,3})?,") // number of dtcs
+ .number("(x*),") // dtcs
+ .number("(d{1,3})?,") // throttle
+ .number("(?:d{1,3})?,") // engine load
+ .number("(d{1,3})?,") // fuel level
+ .expression("(?:[0-9A],)?") // obd protocol
+ .number("(d+),") // odometer
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_FRI = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTFRI,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:([0-9A-Z]{17}),)?") // vin
+ .expression("[^,]*,") // device name
+ .number("(d+)?,") // power
+ .number("d{1,2},") // report type
+ .number("d{1,2},") // count
+ .expression("((?:")
+ .expression(PATTERN_LOCATION.pattern())
+ .expression(")+)")
+ .groupBegin()
+ .number("(d{1,7}.d)?,").optional() // odometer
+ .number("(d{1,3})?,") // battery
+ .or()
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(d{5}:dd:dd)?,") // hour meter
+ .number("(x+)?,") // adc 1
+ .number("(x+)?,") // adc 2
+ .number("(d{1,3})?,") // battery
+ .number("(?:(xx)(xx)(xx))?,") // device status
+ .number("(d+)?,") // rpm
+ .number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption
+ .number("(d+)?,") // fuel level
+ .groupEnd()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_ERI = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTERI,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("x{8},") // mask
+ .number("(d+)?,") // power
+ .number("d{1,2},") // report type
+ .number("d{1,2},") // count
+ .expression("((?:")
+ .expression(PATTERN_LOCATION.pattern())
+ .expression(")+)")
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(d{5}:dd:dd)?,") // hour meter
+ .number("(x+)?,") // adc 1
+ .number("(x+)?,") // adc 2
+ .number("(d{1,3})?,") // battery
+ .number("(?:(xx)(xx)(xx))?,") // device status
+ .expression("(.*)") // additional data
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_IGN = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GTIG[NF],")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("d+,") // ignition off duration
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(d{5}:dd:dd)?,") // hour meter
+ .number("(d{1,7}.d)?,") // odometer
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_IDA = new PatternBuilder()
+ .text("+RESP:GTIDA,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,,") // device name
+ .number("([^,]+),") // rfid
+ .expression("[01],") // report type
+ .number("1,") // count
+ .expression(PATTERN_LOCATION.pattern())
+ .number("(d+.d),") // odometer
+ .text(",,,,")
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_WIF = new PatternBuilder()
+ .text("+RESP:GTWIF,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("(d+),") // count
+ .number("((?:x{12},-?d+,,,,)+),,,,") // wifi
+ .number("(d{1,3}),") // battery
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_GSM = new PatternBuilder()
+ .text("+RESP:GTGSM,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("(?:STR|CTN|NMR|RTL),") // fix type
+ .expression("(.*)") // cells
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF):GT...,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .expression("[^,]*,") // device name
+ .number("d*,")
+ .number("(d{1,2}),") // report type
+ .number("d{1,2},") // count
+ .expression(PATTERN_LOCATION.pattern())
+ .groupBegin()
+ .number("(d{1,7}.d)?,").optional() // odometer
+ .number("(d{1,3})?,") // battery
+ .or()
+ .number("(d{1,7}.d)?,") // odometer
+ .groupEnd()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)") // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private static final Pattern PATTERN_BASIC = new PatternBuilder()
+ .text("+").expression("(?:RESP|BUFF)").text(":")
+ .expression("GT...,")
+ .number("(?:[0-9A-Z]{2}xxxx)?,").optional() // protocol version
+ .number("(d{15}|x{14}),") // imei
+ .any()
+ .number("(d{1,2})?,") // hdop
+ .number("(d{1,3}.d)?,") // speed
+ .number("(d{1,3})?,") // course
+ .number("(-?d{1,5}.d)?,") // altitude
+ .number("(-?d{1,3}.d{6})?,") // longitude
+ .number("(-?d{1,2}.d{6})?,") // latitude
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(d+),") // mcc
+ .number("(d+),") // mnc
+ .number("(x+),") // lac
+ .number("(x+),").optional(4) // cell
+ .any()
+ .number("(dddd)(dd)(dd)") // date (yyyymmdd)
+ .number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
+ .text(",")
+ .number("(xxxx)") // count number
+ .text("$").optional()
+ .compile();
+
+ private Object decodeAck(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
+ Parser parser = new Parser(PATTERN_ACK, sentence);
+ if (parser.matches()) {
+ String protocolVersion = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+ if (type.equals("HBD")) {
+ if (channel != null) {
+ parser.skip(6);
+ channel.write("+SACK:GTHBD," + protocolVersion + "," + parser.next() + "$", remoteAddress);
+ }
+ } else {
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, parser.nextDateTime());
+ position.setValid(false);
+ position.set(Position.KEY_RESULT, "Command " + type + " accepted");
+ return position;
+ }
+ }
+ return null;
+ }
+
+ private Position initPosition(Parser parser, Channel channel, SocketAddress remoteAddress) {
+ if (parser.matches()) {
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession != null) {
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ return position;
+ }
+ }
+ return null;
+ }
+
+ private void decodeDeviceTime(Position position, Parser parser) {
+ if (parser.hasNext(6)) {
+ if (ignoreFixTime) {
+ position.setTime(parser.nextDateTime());
+ } else {
+ position.setDeviceTime(parser.nextDateTime());
+ }
+ }
+ }
+
+ private Object decodeInf(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_INF, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ switch (parser.nextHexInt()) {
+ case 0x16:
+ case 0x1A:
+ case 0x12:
+ position.set(Position.KEY_IGNITION, false);
+ position.set(Position.KEY_MOTION, true);
+ break;
+ case 0x11:
+ position.set(Position.KEY_IGNITION, false);
+ position.set(Position.KEY_MOTION, false);
+ break;
+ case 0x21:
+ position.set(Position.KEY_IGNITION, true);
+ position.set(Position.KEY_MOTION, false);
+ break;
+ case 0x22:
+ position.set(Position.KEY_IGNITION, true);
+ position.set(Position.KEY_MOTION, true);
+ break;
+ case 0x41:
+ position.set(Position.KEY_MOTION, false);
+ break;
+ case 0x42:
+ position.set(Position.KEY_MOTION, true);
+ break;
+ default:
+ break;
+ }
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+
+ parser.next(); // odometer or external power
+
+ position.set(Position.KEY_BATTERY, parser.nextDouble(0));
+ position.set(Position.KEY_CHARGE, parser.nextInt(0) == 1);
+
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ position.set(Position.PREFIX_TEMP + 1, parser.next());
+
+ position.set(Position.PREFIX_ADC + 1, parser.next());
+ position.set(Position.PREFIX_ADC + 2, parser.next());
+
+ position.set(Position.KEY_INPUT, parser.next());
+ position.set(Position.KEY_OUTPUT, parser.next());
+
+ getLastLocation(position, parser.nextDateTime());
+
+ position.set(Position.KEY_INDEX, parser.nextHexInt(0));
+
+ return position;
+ }
+
+ private Object decodeVer(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_VER, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ position.set("deviceType", parser.next());
+ position.set(Position.KEY_VERSION_FW, parser.nextHexInt(0));
+ position.set(Position.KEY_VERSION_HW, parser.nextHexInt(0));
+
+ getLastLocation(position, parser.nextDateTime());
+
+ return position;
+ }
+
+ private void decodeLocation(Position position, Parser parser) {
+ int hdop = parser.nextInt(0);
+ position.setValid(hdop > 0);
+ position.set(Position.KEY_HDOP, hdop);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setCourse(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble(0));
+
+ if (parser.hasNext(8)) {
+ position.setValid(true);
+ position.setLongitude(parser.nextDouble(0));
+ position.setLatitude(parser.nextDouble(0));
+ position.setTime(parser.nextDateTime());
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (parser.hasNext(6)) {
+ int mcc = parser.nextInt(0);
+ int mnc = parser.nextInt(0);
+ if (parser.hasNext(2)) {
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextInt(0), parser.nextInt(0))));
+ }
+ if (parser.hasNext(2)) {
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, parser.nextHexInt(0), parser.nextHexInt(0))));
+ }
+ }
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ }
+
+ private Object decodeObd(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_OBD, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set(Position.KEY_OBD_SPEED, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+ position.set(Position.KEY_FUEL_CONSUMPTION, parser.next());
+ position.set("dtcsClearedDistance", parser.nextInt());
+ position.set("odbConnect", parser.nextInt(0) == 1);
+ position.set("dtcsNumber", parser.nextInt());
+ position.set("dtcsCodes", parser.next());
+ position.set(Position.KEY_THROTTLE, parser.nextInt());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+ position.set(Position.KEY_OBD_ODOMETER, parser.nextInt(0) * 1000);
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private void decodeStatus(Position position, Parser parser) {
+ if (parser.hasNext(3)) {
+ int ignition = parser.nextHexInt(0);
+ if (BitUtil.check(ignition, 4)) {
+ position.set(Position.KEY_IGNITION, false);
+ } else if (BitUtil.check(ignition, 5)) {
+ position.set(Position.KEY_IGNITION, true);
+ }
+ position.set(Position.KEY_INPUT, parser.nextHexInt(0));
+ position.set(Position.KEY_OUTPUT, parser.nextHexInt(0));
+ }
+ }
+
+ private Object decodeFri(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_FRI, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ LinkedList<Position> positions = new LinkedList<>();
+
+ String vin = parser.next();
+ int power = parser.nextInt(0);
+
+ Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
+ while (itemParser.find()) {
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_VIN, vin);
+
+ decodeLocation(position, itemParser);
+
+ positions.add(position);
+ }
+
+ Position position = positions.getLast();
+
+ decodeLocation(position, parser);
+
+ // power value only on some devices
+ if (power > 10) {
+ position.set(Position.KEY_POWER, power);
+ }
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ position.set(Position.KEY_HOURS, parser.next());
+ position.set(Position.PREFIX_ADC + 1, parser.next());
+ position.set(Position.PREFIX_ADC + 2, parser.next());
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ decodeStatus(position, parser);
+
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+
+ decodeDeviceTime(position, parser);
+ if (ignoreFixTime) {
+ positions.clear();
+ positions.add(position);
+ }
+
+ return positions;
+ }
+
+ private Object decodeEri(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_ERI, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ LinkedList<Position> positions = new LinkedList<>();
+
+ int power = parser.nextInt(0);
+
+ Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
+ while (itemParser.find()) {
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ decodeLocation(position, itemParser);
+
+ positions.add(position);
+ }
+
+ Position position = positions.getLast();
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_POWER, power);
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ position.set(Position.KEY_HOURS, parser.next());
+ position.set(Position.PREFIX_ADC + 1, parser.next());
+ position.set(Position.PREFIX_ADC + 2, parser.next());
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+
+ decodeStatus(position, parser);
+
+ int index = 0;
+ String[] data = parser.next().split(",");
+ if (data.length > 1) {
+ int deviceType = Integer.parseInt(data[index++]);
+ if (deviceType == 2) {
+ int deviceCount = Integer.parseInt(data[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index++; // id
+ index++; // type
+ position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index++], 16) * 0.0625);
+ }
+ }
+ }
+
+ decodeDeviceTime(position, parser);
+ if (ignoreFixTime) {
+ positions.clear();
+ positions.add(position);
+ }
+
+ return positions;
+ }
+
+ private Object decodeIgn(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_IGN, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_HOURS, parser.next());
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private Object decodeIda(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_IDA, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+
+ decodeDeviceTime(position, parser);
+
+ return position;
+ }
+
+ private Object decodeWif(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_WIF, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ getLastLocation(position, null);
+
+ Network network = new Network();
+
+ parser.nextInt(0); // count
+ Matcher matcher = Pattern.compile("([0-9a-fA-F]{12}),(-?\\d+),,,,").matcher(parser.next());
+ while (matcher.find()) {
+ String mac = matcher.group(1).replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), Integer.parseInt(matcher.group(2))));
+ }
+
+ position.setNetwork(network);
+
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
+
+ return position;
+ }
+
+ private Object decodeGsm(Channel channel, SocketAddress remoteAddress, String sentence) {
+ Parser parser = new Parser(PATTERN_GSM, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ getLastLocation(position, null);
+
+ Network network = new Network();
+
+ String[] data = parser.next().split(",");
+ for (int i = 0; i < 6; i++) {
+ if (!data[i * 6].isEmpty()) {
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(data[i * 6]), Integer.parseInt(data[i * 6 + 1]),
+ Integer.parseInt(data[i * 6 + 2], 16), Integer.parseInt(data[i * 6 + 3], 16),
+ Integer.parseInt(data[i * 6 + 4])));
+ }
+ }
+
+ position.setNetwork(network);
+
+ return position;
+ }
+
+ private Object decodeOther(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
+ Parser parser = new Parser(PATTERN, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ int reportType = parser.nextInt(0);
+ if (type.equals("NMR")) {
+ position.set(Position.KEY_MOTION, reportType == 1);
+ } else if (type.equals("SOS")) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+
+ decodeLocation(position, parser);
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
+
+ position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
+
+ decodeDeviceTime(position, parser);
+
+ if (Context.getConfig().getBoolean(getProtocolName() + ".ack") && channel != null) {
+ channel.write("+SACK:" + parser.next() + "$", remoteAddress);
+ }
+
+ return position;
+ }
+
+ private Object decodeBasic(Channel channel, SocketAddress remoteAddress, String sentence, String type) {
+ Parser parser = new Parser(PATTERN_BASIC, sentence);
+ Position position = initPosition(parser, channel, remoteAddress);
+ if (position == null) {
+ return null;
+ }
+
+ int hdop = parser.nextInt(0);
+ position.setValid(hdop > 0);
+ position.set(Position.KEY_HDOP, hdop);
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setCourse(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble(0));
+
+ if (parser.hasNext(2)) {
+ position.setLongitude(parser.nextDouble(0));
+ position.setLatitude(parser.nextDouble(0));
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (parser.hasNext(6)) {
+ position.setTime(parser.nextDateTime());
+ }
+
+ if (parser.hasNext(4)) {
+ position.setNetwork(new Network(CellTower.from(
+ parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0))));
+ }
+
+ decodeDeviceTime(position, parser);
+
+ switch (type) {
+ case "PNA":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_ON);
+ break;
+ case "PFA":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_OFF);
+ break;
+ case "EPN":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_RESTORED);
+ break;
+ case "EPF":
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ break;
+ case "BPL":
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_BATTERY);
+ break;
+ case "STT":
+ position.set(Position.KEY_ALARM, Position.ALARM_MOVEMENT);
+ break;
+ case "SWG":
+ position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE);
+ break;
+ case "TMP":
+ case "TEM":
+ position.set(Position.KEY_ALARM, Position.ALARM_TEMPERATURE);
+ break;
+ case "JDR":
+ case "JDS":
+ position.set(Position.KEY_ALARM, Position.ALARM_JAMMING);
+ break;
+ default:
+ break;
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = ((ChannelBuffer) msg).toString(StandardCharsets.US_ASCII);
+
+ int typeIndex = sentence.indexOf(":GT");
+ if (typeIndex < 0) {
+ return null;
+ }
+
+ Object result;
+ String type = sentence.substring(typeIndex + 3, typeIndex + 6);
+ if (sentence.startsWith("+ACK")) {
+ result = decodeAck(channel, remoteAddress, sentence, type);
+ } else {
+ switch (type) {
+ case "INF":
+ result = decodeInf(channel, remoteAddress, sentence);
+ break;
+ case "OBD":
+ result = decodeObd(channel, remoteAddress, sentence);
+ break;
+ case "FRI":
+ result = decodeFri(channel, remoteAddress, sentence);
+ break;
+ case "ERI":
+ result = decodeEri(channel, remoteAddress, sentence);
+ break;
+ case "IGN":
+ case "IGF":
+ result = decodeIgn(channel, remoteAddress, sentence);
+ break;
+ case "IDA":
+ result = decodeIda(channel, remoteAddress, sentence);
+ break;
+ case "WIF":
+ result = decodeWif(channel, remoteAddress, sentence);
+ break;
+ case "GSM":
+ result = decodeGsm(channel, remoteAddress, sentence);
+ break;
+ case "VER":
+ result = decodeVer(channel, remoteAddress, sentence);
+ break;
+ default:
+ result = decodeOther(channel, remoteAddress, sentence, type);
+ break;
+ }
+
+ if (result == null) {
+ result = decodeBasic(channel, remoteAddress, sentence, type);
+ }
+
+ if (result != null) {
+ if (result instanceof Position) {
+ ((Position) result).set(Position.KEY_TYPE, type);
+ } else {
+ for (Position p : (List<Position>) result) {
+ p.set(Position.KEY_TYPE, type);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/org/traccar/protocol/GnxProtocolDecoder.java b/src/org/traccar/protocol/GnxProtocolDecoder.java
index 070d394e8..2274ec164 100644
--- a/src/org/traccar/protocol/GnxProtocolDecoder.java
+++ b/src/org/traccar/protocol/GnxProtocolDecoder.java
@@ -102,7 +102,7 @@ public class GnxProtocolDecoder extends BaseProtocolDecoder {
position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
if (type.equals("MIF")) {
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
}
return position;
diff --git a/src/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/org/traccar/protocol/Gps103ProtocolDecoder.java
index f5ba3cff7..099047aa0 100644
--- a/src/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -248,6 +248,8 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_TEMP + 1, alarm.substring(2));
} else if (alarm.startsWith("oil ")) {
position.set("oil", alarm.substring(4));
+ } else if (!position.getAttributes().containsKey(Position.KEY_ALARM) && !alarm.equals("tracker")) {
+ position.set(Position.KEY_EVENT, alarm);
}
DateBuilder dateBuilder = new DateBuilder()
@@ -258,7 +260,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
String rfid = parser.next();
if (alarm.equals("rfid")) {
- position.set(Position.KEY_RFID, rfid);
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid);
}
String utcHours = parser.next();
diff --git a/src/org/traccar/protocol/Gt06FrameDecoder.java b/src/org/traccar/protocol/Gt06FrameDecoder.java
index f35af6572..c8b5e56ae 100644
--- a/src/org/traccar/protocol/Gt06FrameDecoder.java
+++ b/src/org/traccar/protocol/Gt06FrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,6 @@ public class Gt06FrameDecoder extends FrameDecoder {
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
- // Check minimum length
if (buf.readableBytes() < 5) {
return null;
}
@@ -35,21 +34,22 @@ public class Gt06FrameDecoder extends FrameDecoder {
if (buf.getByte(buf.readerIndex()) == 0x78) {
length += 1 + buf.getUnsignedByte(buf.readerIndex() + 2);
-
- int type = buf.getUnsignedByte(buf.readerIndex() + 3);
- if (type == Gt06ProtocolDecoder.MSG_STATUS && length == 13) {
- length += 2; // workaround for #1727
- }
-
} else {
length += 2 + buf.getUnsignedShort(buf.readerIndex() + 2);
}
- // Check length and return buffer
- if (buf.readableBytes() >= length) {
+ if (buf.readableBytes() >= length && buf.getUnsignedShort(buf.readerIndex() + length - 2) == 0x0d0a) {
return buf.readBytes(length);
}
+ int endIndex = buf.readerIndex() - 1;
+ do {
+ endIndex = buf.indexOf(endIndex + 1, buf.writerIndex(), (byte) 0x0d);
+ if (endIndex > 0 && buf.writerIndex() > endIndex + 1 && buf.getByte(endIndex + 1) == 0x0a) {
+ return buf.readBytes(endIndex + 2 - buf.readerIndex());
+ }
+ } while (endIndex > 0);
+
return null;
}
diff --git a/src/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/org/traccar/protocol/Gt06ProtocolDecoder.java
index 24bedcabf..fbd1adfc6 100644
--- a/src/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -31,6 +31,7 @@ import org.traccar.model.CellTower;
import org.traccar.model.Device;
import org.traccar.model.Network;
import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
@@ -68,6 +69,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_GPS_LBS_STATUS_1 = 0x16;
public static final int MSG_GPS_LBS_STATUS_2 = 0x26;
public static final int MSG_GPS_LBS_STATUS_3 = 0x27;
+ public static final int MSG_LBS_MULTIPLE = 0x28;
+ public static final int MSG_LBS_WIFI = 0x2C;
public static final int MSG_LBS_PHONE = 0x17;
public static final int MSG_LBS_EXTEND = 0x18;
public static final int MSG_LBS_STATUS = 0x19;
@@ -152,9 +155,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return false;
}
- int length = buf.readUnsignedByte();
- position.set(Position.KEY_SATELLITES, BitUtil.to(length, 4));
- length = BitUtil.from(length, 4);
+ position.set(Position.KEY_SATELLITES, BitUtil.to(buf.readUnsignedByte(), 4));
double latitude = buf.readUnsignedInt() / 60.0 / 30000.0;
double longitude = buf.readUnsignedInt() / 60.0 / 30000.0;
@@ -178,10 +179,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_IGNITION, BitUtil.check(flags, 15));
}
- if (length > 0) {
- buf.skipBytes(length - 12); // skip reserved
- }
-
return true;
}
@@ -199,7 +196,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedShort(), buf.readUnsignedMedium())));
if (length > 0) {
- buf.skipBytes(length - 8);
+ buf.skipBytes(length - (hasLength ? 9 : 8));
}
return true;
@@ -336,7 +333,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
buf.readUnsignedShort(); // type
- // Timezone offset
if (dataLength > 10) {
int extensionBits = buf.readUnsignedShort();
int hours = (extensionBits >> 4) / 100;
@@ -391,77 +387,104 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else {
- Position position = new Position();
- position.setDeviceId(deviceSession.getDeviceId());
- position.setProtocol(getProtocolName());
-
- if (type == MSG_LBS_EXTEND) {
+ return decodeBasicOther(channel, buf, deviceSession, type, dataLength);
- DateBuilder dateBuilder = new DateBuilder(timeZone)
- .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
- .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ }
- getLastLocation(position, dateBuilder.getDate());
+ return null;
+ }
- int mcc = buf.readUnsignedShort();
- int mnc = buf.readUnsignedByte();
+ private Object decodeBasicOther(Channel channel, ChannelBuffer buf,
+ DeviceSession deviceSession, int type, int dataLength) throws Exception {
- Network network = new Network();
- for (int i = 0; i < 7; i++) {
- network.addCellTower(CellTower.from(
- mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedMedium(), -buf.readUnsignedByte()));
- }
- position.setNetwork(network);
+ Position position = new Position();
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setProtocol(getProtocolName());
- } else if (type == MSG_STRING) {
+ if (type == MSG_LBS_MULTIPLE || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI) {
- getLastLocation(position, null);
+ DateBuilder dateBuilder = new DateBuilder(timeZone)
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
- int commandLength = buf.readUnsignedByte();
+ getLastLocation(position, dateBuilder.getDate());
- if (commandLength > 0) {
- buf.readUnsignedByte(); // server flag (reserved)
- position.set(Position.KEY_RESULT,
- buf.readBytes(commandLength - 1).toString(StandardCharsets.US_ASCII));
+ int mcc = buf.readUnsignedShort();
+ int mnc = buf.readUnsignedByte();
+ Network network = new Network();
+ for (int i = 0; i < 7; i++) {
+ int lac = buf.readUnsignedShort();
+ int cid = buf.readUnsignedMedium();
+ int rssi = -buf.readUnsignedByte();
+ if (lac > 0) {
+ network.addCellTower(CellTower.from(mcc, mnc, lac, cid, rssi));
}
+ }
- } else if (isSupported(type)) {
+ buf.readUnsignedByte(); // time leads
- if (hasGps(type)) {
- decodeGps(position, buf, false);
- } else {
- getLastLocation(position, null);
+ if (type != MSG_LBS_MULTIPLE) {
+ int wifiCount = buf.readUnsignedByte();
+ for (int i = 0; i < wifiCount; i++) {
+ String mac = ChannelBuffers.hexDump(buf.readBytes(6)).replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), buf.readUnsignedByte()));
}
+ }
- if (hasLbs(type)) {
- decodeLbs(position, buf, hasStatus(type));
- }
+ position.setNetwork(network);
- if (hasStatus(type)) {
- decodeStatus(position, buf);
- }
+ } else if (type == MSG_STRING) {
- if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 4 + 6) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- }
+ getLastLocation(position, null);
+ int commandLength = buf.readUnsignedByte();
+
+ if (commandLength > 0) {
+ buf.readUnsignedByte(); // server flag (reserved)
+ position.set(Position.KEY_RESULT,
+ buf.readBytes(commandLength - 1).toString(StandardCharsets.US_ASCII));
+ }
+
+ } else if (isSupported(type)) {
+
+ if (hasGps(type)) {
+ decodeGps(position, buf, false);
} else {
+ getLastLocation(position, null);
+ }
- buf.skipBytes(dataLength);
- if (type != MSG_COMMAND_0 && type != MSG_COMMAND_1 && type != MSG_COMMAND_2) {
- sendResponse(channel, false, type);
- }
- return null;
+ if (hasLbs(type)) {
+ decodeLbs(position, buf, hasStatus(type));
+ }
+ if (hasStatus(type)) {
+ decodeStatus(position, buf);
}
- sendResponse(channel, false, type);
+ if (type == MSG_GPS_LBS_1 && buf.readableBytes() >= 4 + 6) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ }
- return position;
+ if (type == MSG_GPS_LBS_2 && buf.readableBytes() >= 3 + 6) {
+ position.set(Position.KEY_IGNITION, buf.readUnsignedByte() > 0);
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte()); // reason
+ position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0);
+ }
+
+ } else {
+
+ buf.skipBytes(dataLength);
+ if (type != MSG_COMMAND_0 && type != MSG_COMMAND_1 && type != MSG_COMMAND_2) {
+ sendResponse(channel, false, type);
+ }
+ return null;
}
- return null;
+ sendResponse(channel, false, type);
+
+ return position;
}
private Object decodeExtended(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) throws Exception {
@@ -509,7 +532,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else if (subType == 0x05) {
int flags = buf.readUnsignedByte();
- position.set("door", BitUtil.check(flags, 0));
+ position.set(Position.KEY_DOOR, BitUtil.check(flags, 0));
position.set(Position.PREFIX_IO + 1, BitUtil.check(flags, 2));
return position;
@@ -533,7 +556,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
if (photo.writableBytes() > 0) {
sendPhotoRequest(channel, pictureId);
} else {
- Device device = Context.getDeviceManager().getDeviceById(deviceSession.getDeviceId());
+ Device device = Context.getDeviceManager().getById(deviceSession.getDeviceId());
Context.getMediaManager().writeFile(device.getUniqueId(), photo, "jpg");
photos.remove(pictureId);
}
@@ -544,7 +567,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, position.getDeviceTime());
}
- decodeLbs(position, buf, true);
+ if (decodeLbs(position, buf, true)) {
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ }
buf.skipBytes(buf.readUnsignedByte()); // additional cell towers
buf.skipBytes(buf.readUnsignedByte()); // wifi access point
diff --git a/src/org/traccar/protocol/H02Protocol.java b/src/org/traccar/protocol/H02Protocol.java
index df64402f8..66965e9db 100644
--- a/src/org/traccar/protocol/H02Protocol.java
+++ b/src/org/traccar/protocol/H02Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package org.traccar.protocol;
+import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.handler.codec.string.StringEncoder;
@@ -50,5 +51,13 @@ public class H02Protocol extends BaseProtocol {
pipeline.addLast("objectDecoder", new H02ProtocolDecoder(H02Protocol.this));
}
});
+ serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("objectEncoder", new H02ProtocolEncoder());
+ pipeline.addLast("objectDecoder", new H02ProtocolDecoder(H02Protocol.this));
+ }
+ });
}
}
diff --git a/src/org/traccar/protocol/H02ProtocolDecoder.java b/src/org/traccar/protocol/H02ProtocolDecoder.java
index aa3d47650..4414870d2 100644
--- a/src/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/org/traccar/protocol/H02ProtocolDecoder.java
@@ -150,10 +150,22 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.text("*")
.expression("..,") // manufacturer
.number("(d+),") // imei
- .expression("[^,]+,")
- .any()
+ .groupBegin()
+ .text("VP1,")
+ .or()
+ .groupBegin()
+ .text("V4,")
+ .expression("(.*),") // response
+ .or()
+ .expression("V[^,]*,")
+ .groupEnd()
.number("(?:(dd)(dd)(dd))?,") // time (hhmmss)
- .expression("([AV])?,") // validity
+ .groupEnd()
+ .groupBegin()
+ .expression("([ABV])?,") // validity
+ .or()
+ .number("(d+),") // coding scheme
+ .groupEnd()
.groupBegin()
.number("-(d+)-(d+.d+),") // latitude
.or()
@@ -169,25 +181,28 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.?d*),") // speed
.number("(d+.?d*)?,") // course
.number("(?:(dd)(dd)(dd))?") // date (ddmmyy)
- .any()
- .number(",(x{8})") // status
+ .groupBegin()
+ .expression(",[^,]*,")
+ .expression("[^,]*,")
+ .expression("[^,]*") // sim info
+ .groupEnd("?")
+ .groupBegin()
+ .number(",(x{8})")
.groupBegin()
.number(",(d+),") // odometer
.number("(-?d+),") // temperature
.number("(d+.d+),") // fuel
.number("(-?d+),") // altitude
.number("(x+),") // lac
- .number("(x+)#") // cid
+ .number("(x+)") // cid
.or()
- .number(",(d+),")
- .number("(d+),")
- .number("(d+),")
- .number("(d+)#")
+ .text(",")
+ .expression("(.*)") // data
.or()
- .expression(",.*")
+ .groupEnd()
.or()
- .text("#")
.groupEnd()
+ .text("#")
.compile();
private static final Pattern PATTERN_NBR = new PatternBuilder()
@@ -222,6 +237,24 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_V3 = new PatternBuilder()
+ .text("*")
+ .expression("..,") // manufacturer
+ .number("(d+),") // imei
+ .text("V3,")
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(ddd)") // mcc
+ .number("(d+),") // mnc
+ .number("(d+),") // count
+ .expression("(.*),") // cell info
+ .number("(x{4}),") // battery
+ .number("d+,") // reboot info
+ .text("X,")
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(x{8})") // status
+ .text("#").optional()
+ .compile();
+
private Position decodeText(String sentence, Channel channel, SocketAddress remoteAddress) {
Parser parser = new Parser(PATTERN, sentence);
@@ -238,6 +271,10 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ if (parser.hasNext()) {
+ position.set(Position.KEY_RESULT, parser.next());
+ }
+
DateBuilder dateBuilder = new DateBuilder();
if (parser.hasNext(3)) {
dateBuilder.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
@@ -246,6 +283,10 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext()) {
position.setValid(parser.next().equals("A"));
}
+ if (parser.hasNext()) {
+ parser.nextInt(); // coding scheme
+ position.setValid(true);
+ }
if (parser.hasNext(2)) {
position.setLatitude(-parser.nextCoordinate());
@@ -271,7 +312,9 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.setTime(new Date());
}
- processStatus(position, parser.nextLong(16, 0));
+ if (parser.hasNext()) {
+ processStatus(position, parser.nextLong(16, 0));
+ }
if (parser.hasNext(6)) {
position.set(Position.KEY_ODOMETER, parser.nextInt(0));
@@ -284,8 +327,9 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
}
if (parser.hasNext(4)) {
- for (int i = 1; i <= 4; i++) {
- position.set(Position.PREFIX_IO + i, parser.nextInt(0));
+ String[] values = parser.next().split(",");
+ for (int i = 0; i < values.length; i++) {
+ position.set(Position.PREFIX_IO + (i + 1), values[i].trim());
}
}
@@ -354,7 +398,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RSSI, parser.nextInt());
position.set(Position.KEY_SATELLITES, parser.nextInt());
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
- position.set("steps", parser.nextInt());
+ position.set(Position.KEY_STEPS, parser.nextInt());
position.set("turnovers", parser.nextInt());
dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
@@ -366,6 +410,48 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeV3(String sentence, Channel channel, SocketAddress remoteAddress) {
+
+ Parser parser = new Parser(PATTERN_V3, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+
+ int mcc = parser.nextInt();
+ int mnc = parser.nextInt();
+
+ int count = parser.nextInt();
+ Network network = new Network();
+ String[] values = parser.next().split(",");
+ for (int i = 0; i < count; i++) {
+ network.addCellTower(CellTower.from(
+ mcc, mnc, Integer.parseInt(values[i * 4]), Integer.parseInt(values[i * 4 + 1])));
+ }
+ position.setNetwork(network);
+
+ position.set(Position.KEY_BATTERY, parser.nextHexInt());
+
+ dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+
+ getLastLocation(position, dateBuilder.getDate());
+
+ processStatus(position, parser.nextLong(16, 0));
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -385,6 +471,8 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
return decodeLbs(sentence, channel, remoteAddress);
case "LINK":
return decodeLink(sentence, channel, remoteAddress);
+ case "V3":
+ return decodeV3(sentence, channel, remoteAddress);
default:
return decodeText(sentence, channel, remoteAddress);
}
diff --git a/src/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/org/traccar/protocol/Jt600ProtocolDecoder.java
index 2bd02d3f1..f76fd8069 100644
--- a/src/org/traccar/protocol/Jt600ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Jt600ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -31,6 +32,8 @@ import org.traccar.model.Position;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
import java.util.regex.Pattern;
public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
@@ -45,10 +48,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
return degrees + minutes / 60;
}
- private Position decodeBinary(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {
+ private List<Position> decodeBinary(ChannelBuffer buf, Channel channel, SocketAddress remoteAddress) {
- Position position = new Position();
- position.setProtocol(getProtocolName());
+ List<Position> positions = new LinkedList<>();
buf.readByte(); // header
@@ -59,97 +61,106 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
if (deviceSession == null) {
return null;
}
- position.setDeviceId(deviceSession.getDeviceId());
if (longFormat) {
buf.readUnsignedByte(); // protocol
}
- int version = buf.readUnsignedByte() >> 4;
+ int version = BitUtil.from(buf.readUnsignedByte(), 4);
buf.readUnsignedShort(); // length
- DateBuilder dateBuilder = new DateBuilder()
- .setDay(BcdUtil.readInteger(buf, 2))
- .setMonth(BcdUtil.readInteger(buf, 2))
- .setYear(BcdUtil.readInteger(buf, 2))
- .setHour(BcdUtil.readInteger(buf, 2))
- .setMinute(BcdUtil.readInteger(buf, 2))
- .setSecond(BcdUtil.readInteger(buf, 2));
- position.setTime(dateBuilder.getDate());
-
- double latitude = convertCoordinate(BcdUtil.readInteger(buf, 8));
- double longitude = convertCoordinate(BcdUtil.readInteger(buf, 9));
-
- byte flags = buf.readByte();
- position.setValid((flags & 0x1) == 0x1);
- if ((flags & 0x2) == 0) {
- latitude = -latitude;
- }
- position.setLatitude(latitude);
- if ((flags & 0x4) == 0) {
- longitude = -longitude;
- }
- position.setLongitude(longitude);
+ while (buf.readableBytes() > 1) {
- position.setSpeed(BcdUtil.readInteger(buf, 2));
- position.setCourse(buf.readUnsignedByte() * 2.0);
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
- if (longFormat) {
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDay(BcdUtil.readInteger(buf, 2))
+ .setMonth(BcdUtil.readInteger(buf, 2))
+ .setYear(BcdUtil.readInteger(buf, 2))
+ .setHour(BcdUtil.readInteger(buf, 2))
+ .setMinute(BcdUtil.readInteger(buf, 2))
+ .setSecond(BcdUtil.readInteger(buf, 2));
+ position.setTime(dateBuilder.getDate());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000);
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ double latitude = convertCoordinate(BcdUtil.readInteger(buf, 8));
+ double longitude = convertCoordinate(BcdUtil.readInteger(buf, 9));
- buf.readUnsignedInt(); // vehicle id combined
+ byte flags = buf.readByte();
+ position.setValid((flags & 0x1) == 0x1);
+ if ((flags & 0x2) == 0) {
+ latitude = -latitude;
+ }
+ position.setLatitude(latitude);
+ if ((flags & 0x4) == 0) {
+ longitude = -longitude;
+ }
+ position.setLongitude(longitude);
- position.set(Position.KEY_STATUS, buf.readUnsignedShort());
+ position.setSpeed(BcdUtil.readInteger(buf, 2));
+ position.setCourse(buf.readUnsignedByte() * 2.0);
- int battery = buf.readUnsignedByte();
- if (battery == 0xff) {
- position.set(Position.KEY_CHARGE, true);
- } else {
- position.set(Position.KEY_BATTERY_LEVEL, battery);
- }
+ if (longFormat) {
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000);
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- CellTower cellTower = CellTower.fromCidLac(buf.readUnsignedShort(), buf.readUnsignedShort());
- cellTower.setSignalStrength((int) buf.readUnsignedByte());
- position.setNetwork(new Network(cellTower));
+ buf.readUnsignedInt(); // vehicle id combined
- position.set(Position.KEY_INDEX, buf.readUnsignedByte());
+ position.set(Position.KEY_STATUS, buf.readUnsignedShort());
- } else if (version == 1) {
+ int battery = buf.readUnsignedByte();
+ if (battery == 0xff) {
+ position.set(Position.KEY_CHARGE, true);
+ } else {
+ position.set(Position.KEY_BATTERY_LEVEL, battery);
+ }
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- position.set(Position.KEY_POWER, buf.readUnsignedByte());
+ CellTower cellTower = CellTower.fromCidLac(buf.readUnsignedShort(), buf.readUnsignedShort());
+ cellTower.setSignalStrength((int) buf.readUnsignedByte());
+ position.setNetwork(new Network(cellTower));
- buf.readByte(); // other flags and sensors
+ } else if (version == 1) {
- position.setAltitude(buf.readUnsignedShort());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.set(Position.KEY_POWER, buf.readUnsignedByte());
- int cid = buf.readUnsignedShort();
- int lac = buf.readUnsignedShort();
- int rssi = buf.readUnsignedByte();
+ buf.readByte(); // other flags and sensors
- if (cid != 0 && lac != 0) {
- CellTower cellTower = CellTower.fromCidLac(cid, lac);
- cellTower.setSignalStrength(rssi);
- position.setNetwork(new Network(cellTower));
- } else {
- position.set(Position.KEY_RSSI, rssi);
- }
+ position.setAltitude(buf.readUnsignedShort());
+
+ int cid = buf.readUnsignedShort();
+ int lac = buf.readUnsignedShort();
+ int rssi = buf.readUnsignedByte();
+
+ if (cid != 0 && lac != 0) {
+ CellTower cellTower = CellTower.fromCidLac(cid, lac);
+ cellTower.setSignalStrength(rssi);
+ position.setNetwork(new Network(cellTower));
+ } else {
+ position.set(Position.KEY_RSSI, rssi);
+ }
- } else if (version == 2) {
+ } else if (version == 2) {
- int fuel = buf.readUnsignedByte() << 8;
+ int fuel = buf.readUnsignedByte() << 8;
- position.set(Position.KEY_STATUS, buf.readUnsignedInt());
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000);
+ position.set(Position.KEY_STATUS, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 1000);
- fuel += buf.readUnsignedByte();
- position.set(Position.KEY_FUEL_LEVEL, fuel);
+ fuel += buf.readUnsignedByte();
+ position.set(Position.KEY_FUEL_LEVEL, fuel);
+
+ }
+
+ positions.add(position);
}
- return position;
+ buf.readUnsignedByte(); // index
+
+ return positions;
}
private static final Pattern PATTERN_W01 = new PatternBuilder()
diff --git a/src/org/traccar/protocol/Jt600ProtocolEncoder.java b/src/org/traccar/protocol/Jt600ProtocolEncoder.java
index 0bf389460..377f104a3 100644
--- a/src/org/traccar/protocol/Jt600ProtocolEncoder.java
+++ b/src/org/traccar/protocol/Jt600ProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
*/
package org.traccar.protocol;
+import java.util.TimeZone;
+
import org.traccar.StringProtocolEncoder;
import org.traccar.helper.Log;
import org.traccar.model.Command;
@@ -29,7 +31,7 @@ public class Jt600ProtocolEncoder extends StringProtocolEncoder {
case Command.TYPE_ENGINE_RESUME:
return "(S07,1)";
case Command.TYPE_SET_TIMEZONE:
- int offset = command.getInteger(Command.KEY_TIMEZONE) / 60;
+ int offset = TimeZone.getTimeZone(command.getString(Command.KEY_TIMEZONE)).getRawOffset() / 60000;
return "(S09,1," + offset + ")";
case Command.TYPE_REBOOT_DEVICE:
return "(S17)";
diff --git a/src/org/traccar/protocol/MegastekProtocolDecoder.java b/src/org/traccar/protocol/MegastekProtocolDecoder.java
index 15a384cc0..3ef52acd1 100644
--- a/src/org/traccar/protocol/MegastekProtocolDecoder.java
+++ b/src/org/traccar/protocol/MegastekProtocolDecoder.java
@@ -267,7 +267,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
.or().text(" ")
.groupEnd("?").text(",")
.number("(d+)?,") // rfid
- .number("d*,")
+ .expression("[^,]*,")
.number("(d+)?,") // battery
.expression("([^,]*);") // alert
.any()
@@ -280,13 +280,13 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
if (parser.next().equals("S")) {
@@ -327,7 +327,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
}
}
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
String battery = parser.next();
if (battery != null) {
diff --git a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
index 44e01d5e0..e41a42843 100644
--- a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
+++ b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
@@ -94,8 +94,8 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.d+),") // single fuel consumption
.number("(d+.d+),") // total fuel consumption
.number("(d+),") // error code count
- .number("(d+),") // harsh acceleration count
- .number("(d+)") // harsh break count
+ .number("(d+),") // hard acceleration count
+ .number("(d+)") // hard brake count
.compile();
private static final Pattern PATTERN_OBDA = new PatternBuilder()
@@ -106,8 +106,8 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // average speed
.number("(d+),") // history highest speed
.number("(d+),") // history highest rpm
- .number("(d+),") // total harsh acceleration
- .number("(d+)") // total harsh break n0
+ .number("(d+),") // total hard acceleration
+ .number("(d+)") // total hard brake
.compile();
public static final int MSG_HEARTBEAT = 0x0001;
@@ -194,10 +194,16 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_MOVEMENT;
case 0x13:
return Position.ALARM_GEOFENCE_ENTER;
+ case 0x14:
+ return Position.ALARM_ACCIDENT;
case 0x50:
return Position.ALARM_POWER_OFF;
case 0x53:
return Position.ALARM_GPS_ANTENNA_CUT;
+ case 0x72:
+ return Position.ALARM_BRAKING;
+ case 0x73:
+ return Position.ALARM_ACCELERATION;
default:
return null;
}
@@ -253,7 +259,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
}
if (parser.hasNext()) {
- position.set(Position.KEY_RFID, parser.nextHexInt(0));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(parser.nextHexInt(0)));
}
return position;
@@ -295,8 +301,8 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
position.set("singleFuelConsumption", parser.nextDouble());
position.set("totalFuelConsumption", parser.nextDouble());
position.set(Position.KEY_DTCS, parser.nextInt());
- position.set("harshAcelerationNo", parser.nextInt());
- position.set("harshBreakerNo", parser.nextInt());
+ position.set("hardAccelerationCount", parser.nextInt());
+ position.set("hardBrakingCount", parser.nextInt());
return position;
}
@@ -353,7 +359,13 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
position.setProtocol(getProtocolName());
if (command == MSG_ALARM) {
- position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ short alarmCode = buf.readUnsignedByte();
+ position.set(Position.KEY_ALARM, decodeAlarm(alarmCode));
+ if (alarmCode >= 0x02 && alarmCode <= 0x05) {
+ position.set(Position.PREFIX_IN + alarmCode, 1);
+ } else if (alarmCode >= 0x32 && alarmCode <= 0x35) {
+ position.set(Position.PREFIX_IN + (alarmCode - 0x30), 0);
+ }
} else if (command == MSG_POSITION_LOGGED) {
buf.skipBytes(6);
}
@@ -370,7 +382,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
if (rfid != 0) {
String card = String.format("%010d", rfid);
position.set("card" + (i + 1), card);
- position.set(Position.KEY_RFID, card);
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, card);
}
}
}
diff --git a/src/org/traccar/protocol/MeiligaoProtocolEncoder.java b/src/org/traccar/protocol/MeiligaoProtocolEncoder.java
index 268bae392..2e0a1e84c 100644
--- a/src/org/traccar/protocol/MeiligaoProtocolEncoder.java
+++ b/src/org/traccar/protocol/MeiligaoProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import org.traccar.model.Command;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
+import java.util.TimeZone;
public class MeiligaoProtocolEncoder extends BaseProtocolEncoder {
@@ -78,7 +79,7 @@ public class MeiligaoProtocolEncoder extends BaseProtocolEncoder {
content.writeShort(command.getInteger(Command.KEY_RADIUS));
return encodeContent(command.getDeviceId(), MSG_MOVEMENT_ALARM, content);
case Command.TYPE_SET_TIMEZONE:
- int offset = command.getInteger(Command.KEY_TIMEZONE) / 60;
+ int offset = TimeZone.getTimeZone(command.getString(Command.KEY_TIMEZONE)).getRawOffset() / 60000;
content.writeBytes(String.valueOf(offset).getBytes(StandardCharsets.US_ASCII));
return encodeContent(command.getDeviceId(), MSG_TIME_ZONE, content);
case Command.TYPE_REBOOT_DEVICE:
diff --git a/src/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/org/traccar/protocol/MeitrackProtocolDecoder.java
index 38ecde519..efc9c24db 100644
--- a/src/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -20,6 +20,7 @@ import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
import org.traccar.DeviceSession;
+import org.traccar.helper.Checksum;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -69,16 +70,15 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
.number("(x+)?|") // adc2
.number("(x+)?|") // adc3
.number("(x+)|") // battery
- .number("(x+),") // power
+ .number("(x+)?,") // power
.groupBegin()
.expression("([^,]+)?,") // event specific
.expression("[^,]*,") // reserved
- .number("d*,") // protocol
+ .number("(d+)?,") // protocol
.number("(x{4})?") // fuel
.number("(?:,(x{6}(?:|x{6})*))?") // temperature
- .or()
+ .groupEnd("?")
.any()
- .groupEnd()
.text("*")
.number("xx")
.text("\r\n").optional()
@@ -160,7 +160,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
}
}
- String deviceModel = Context.getIdentityManager().getDeviceById(deviceSession.getDeviceId()).getModel();
+ String deviceModel = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getModel();
if (deviceModel == null) {
deviceModel = "";
}
@@ -201,7 +201,7 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
if (eventData != null && !eventData.isEmpty()) {
switch (event) {
case 37:
- position.set(Position.KEY_RFID, eventData);
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, eventData);
break;
default:
position.set("eventData", eventData);
@@ -209,6 +209,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
}
}
+ int protocol = parser.nextInt(0);
+
if (parser.hasNext()) {
String fuel = parser.next();
position.set(Position.KEY_FUEL_LEVEL,
@@ -218,9 +220,14 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext()) {
for (String temp : parser.next().split("\\|")) {
int index = Integer.valueOf(temp.substring(0, 2), 16);
- double value = Byte.valueOf(temp.substring(2, 4), 16);
- value += (value < 0 ? -0.01 : 0.01) * Integer.valueOf(temp.substring(4), 16);
- position.set(Position.PREFIX_TEMP + index, value);
+ if (protocol >= 3) {
+ double value = Short.valueOf(temp.substring(2), 16);
+ position.set(Position.PREFIX_TEMP + index, value * 0.01);
+ } else {
+ double value = Byte.valueOf(temp.substring(2, 4), 16);
+ value += (value < 0 ? -0.01 : 0.01) * Integer.valueOf(temp.substring(4), 16);
+ position.set(Position.PREFIX_TEMP + index, value);
+ }
}
}
@@ -307,7 +314,6 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
ChannelBuffer buf = (ChannelBuffer) msg;
- // Find type
int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ',');
index = buf.indexOf(index + 1, buf.writerIndex(), (byte) ',');
@@ -316,8 +322,12 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
case "D03":
if (channel != null) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- String imei = Context.getIdentityManager().getDeviceById(deviceSession.getDeviceId()).getUniqueId();
- channel.write("@@O46," + imei + ",D00,camera_picture.jpg,0*00\r\n");
+ String imei = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId();
+ String content = "D00,camera_picture.jpg,0";
+ int length = 1 + imei.length() + 1 + content.length() + 5;
+ String response = String.format("@@O%02d,%s,%s*", length, imei, content);
+ response += Checksum.sum(response) + "\r\n";
+ channel.write(response);
}
return null;
case "CCC":
diff --git a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java
index 8bfb4fb36..05994b697 100644
--- a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java
+++ b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java
@@ -133,7 +133,11 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder {
String sentence = (String) msg;
if (sentence.startsWith("!1,")) {
- getDeviceSession(channel, remoteAddress, sentence.substring(3, sentence.length()));
+ int index = sentence.indexOf(',', 3);
+ if (index < 0) {
+ index = sentence.length();
+ }
+ getDeviceSession(channel, remoteAddress, sentence.substring(3, index));
return null;
}
diff --git a/src/org/traccar/protocol/MiniFinderProtocolEncoder.java b/src/org/traccar/protocol/MiniFinderProtocolEncoder.java
index e5c43e29a..486f406a5 100644
--- a/src/org/traccar/protocol/MiniFinderProtocolEncoder.java
+++ b/src/org/traccar/protocol/MiniFinderProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
*/
package org.traccar.protocol;
+import java.util.TimeZone;
+
import org.traccar.StringProtocolEncoder;
import org.traccar.helper.Log;
import org.traccar.model.Command;
@@ -27,7 +29,7 @@ public class MiniFinderProtocolEncoder extends StringProtocolEncoder implements
if (key.equals(Command.KEY_ENABLE)) {
return (Boolean) value ? "1" : "0";
} else if (key.equals(Command.KEY_TIMEZONE)) {
- return String.format("%+03d", ((Number) value).longValue() / 3600);
+ return String.format("%+03d", TimeZone.getTimeZone((String) value).getRawOffset() / 3600000);
} else if (key.equals(Command.KEY_INDEX)) {
switch (((Number) value).intValue()) {
case 0:
diff --git a/src/org/traccar/protocol/MxtProtocolDecoder.java b/src/org/traccar/protocol/MxtProtocolDecoder.java
index 49987ce57..6d82e4a4b 100644
--- a/src/org/traccar/protocol/MxtProtocolDecoder.java
+++ b/src/org/traccar/protocol/MxtProtocolDecoder.java
@@ -159,7 +159,7 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(infoGroups, 7)) {
- position.set(Position.KEY_RFID, buf.readUnsignedInt());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readUnsignedInt()));
}
buf.readerIndex(buf.writerIndex() - 3);
diff --git a/src/org/traccar/protocol/OsmAndProtocolDecoder.java b/src/org/traccar/protocol/OsmAndProtocolDecoder.java
index 15f6f40b8..15a71c88b 100644
--- a/src/org/traccar/protocol/OsmAndProtocolDecoder.java
+++ b/src/org/traccar/protocol/OsmAndProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -110,7 +110,7 @@ public class OsmAndProtocolDecoder extends BaseProtocolDecoder {
position.setLongitude(Double.parseDouble(location[1]));
break;
case "speed":
- position.setSpeed(Double.parseDouble(value));
+ position.setSpeed(convertSpeed(Double.parseDouble(value), "kn"));
break;
case "bearing":
case "heading":
@@ -128,11 +128,24 @@ public class OsmAndProtocolDecoder extends BaseProtocolDecoder {
case "batt":
position.set(Position.KEY_BATTERY_LEVEL, Double.parseDouble(value));
break;
+ case "driverUniqueId":
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, value);
+ break;
default:
try {
position.set(entry.getKey(), Double.parseDouble(value));
} catch (NumberFormatException e) {
- position.set(entry.getKey(), value);
+ switch (value) {
+ case "true":
+ position.set(entry.getKey(), true);
+ break;
+ case "false":
+ position.set(entry.getKey(), false);
+ break;
+ default:
+ position.set(entry.getKey(), value);
+ break;
+ }
}
break;
}
diff --git a/src/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/org/traccar/protocol/Pt502ProtocolDecoder.java
index b1851f8ca..fef5d9b39 100644
--- a/src/org/traccar/protocol/Pt502ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Pt502ProtocolDecoder.java
@@ -67,7 +67,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
case "HDA":
return Position.ALARM_ACCELERATION;
case "HDB":
- return Position.ALARM_BREAKING;
+ return Position.ALARM_BRAKING;
case "FDA":
return Position.ALARM_FATIGUE_DRIVING;
case "SKA":
@@ -129,7 +129,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
}
position.set(Position.KEY_ODOMETER, parser.nextInt(0));
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
if (parser.hasNext()) {
int value = parser.nextHexInt(0);
diff --git a/src/org/traccar/protocol/Pt502ProtocolEncoder.java b/src/org/traccar/protocol/Pt502ProtocolEncoder.java
index 5f7665e50..4a876f6da 100644
--- a/src/org/traccar/protocol/Pt502ProtocolEncoder.java
+++ b/src/org/traccar/protocol/Pt502ProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,27 @@
*/
package org.traccar.protocol;
+import java.util.TimeZone;
+
import org.traccar.StringProtocolEncoder;
import org.traccar.helper.Log;
import org.traccar.model.Command;
-public class Pt502ProtocolEncoder extends StringProtocolEncoder {
+public class Pt502ProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter {
+
+ @Override
+ public String formatValue(String key, Object value) {
+ if (key.equals(Command.KEY_TIMEZONE)) {
+ return String.valueOf(TimeZone.getTimeZone((String) value).getRawOffset() / 3600000);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected String formatCommand(Command command, String format, String... keys) {
+ return formatCommand(command, format, this, keys);
+ }
@Override
protected Object encodeCommand(Command command) {
diff --git a/src/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/org/traccar/protocol/RuptelaProtocolDecoder.java
index ac95ed27a..8752d30c0 100644
--- a/src/org/traccar/protocol/RuptelaProtocolDecoder.java
+++ b/src/org/traccar/protocol/RuptelaProtocolDecoder.java
@@ -74,6 +74,43 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private long readValue(ChannelBuffer buf, int length, boolean signed) {
+ switch (length) {
+ case 1:
+ return signed ? buf.readByte() : buf.readUnsignedByte();
+ case 2:
+ return signed ? buf.readShort() : buf.readUnsignedShort();
+ case 4:
+ return signed ? buf.readInt() : buf.readUnsignedInt();
+ default:
+ return buf.readLong();
+ }
+ }
+
+ private void decodeParameter(Position position, int id, ChannelBuffer buf, int length) {
+ switch (id) {
+ case 2:
+ case 3:
+ case 4:
+ position.set("di" + (id - 1), readValue(buf, length, false));
+ break;
+ case 5:
+ position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1);
+ break;
+ case 74:
+ position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1);
+ break;
+ case 78:
+ case 79:
+ case 80:
+ position.set(Position.PREFIX_TEMP + (id - 78), readValue(buf, length, true) * 0.1);
+ break;
+ default:
+ position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
+ break;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -133,28 +170,28 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
int cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte();
- position.set(Position.PREFIX_IO + id, buf.readUnsignedByte());
+ decodeParameter(position, id, buf, 1);
}
// Read 2 byte data
cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte();
- position.set(Position.PREFIX_IO + id, buf.readUnsignedShort());
+ decodeParameter(position, id, buf, 2);
}
// Read 4 byte data
cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte();
- position.set(Position.PREFIX_IO + id, buf.readUnsignedInt());
+ decodeParameter(position, id, buf, 4);
}
// Read 8 byte data
cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
int id = type == MSG_EXTENDED_RECORDS ? buf.readUnsignedShort() : buf.readUnsignedByte();
- position.set(Position.PREFIX_IO + id, buf.readLong());
+ decodeParameter(position, id, buf, 8);
}
positions.add(position);
diff --git a/src/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/org/traccar/protocol/StarLinkProtocolDecoder.java
index e90dde455..79f013fac 100644
--- a/src/org/traccar/protocol/StarLinkProtocolDecoder.java
+++ b/src/org/traccar/protocol/StarLinkProtocolDecoder.java
@@ -69,6 +69,29 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
return value.charAt(0) == '+' ? result : -result;
}
+ private String decodeAlarm(int event) {
+ switch (event) {
+ case 6:
+ return Position.ALARM_OVERSPEED;
+ case 7:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 8:
+ return Position.ALARM_GEOFENCE_EXIT;
+ case 9:
+ return Position.ALARM_POWER_CUT;
+ case 11:
+ return Position.ALARM_LOW_BATTERY;
+ case 26:
+ return Position.ALARM_TOW;
+ case 36:
+ return Position.ALARM_SOS;
+ case 42:
+ return Position.ALARM_JAMMING;
+ default:
+ return null;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -109,6 +132,7 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
break;
case "#EID#":
event = Integer.parseInt(data[i]);
+ position.set(Position.KEY_ALARM, decodeAlarm(event));
position.set(Position.KEY_EVENT, event);
break;
case "#PDT#":
@@ -196,7 +220,7 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
if (rfid.matches("0+")) {
rfid = data[data.length - 2];
}
- position.set(Position.KEY_RFID, rfid);
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, rfid);
}
return position;
diff --git a/src/org/traccar/protocol/Stl060ProtocolDecoder.java b/src/org/traccar/protocol/Stl060ProtocolDecoder.java
index c81e83aab..26817a5c8 100644
--- a/src/org/traccar/protocol/Stl060ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Stl060ProtocolDecoder.java
@@ -104,7 +104,7 @@ public class Stl060ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_CHARGE, parser.nextInt(0) == 1);
position.set(Position.KEY_IGNITION, parser.nextInt(0) == 1);
position.set(Position.KEY_INPUT, parser.nextInt(0));
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
position.set(Position.KEY_ODOMETER, parser.nextInt(0));
position.set(Position.PREFIX_TEMP + 1, parser.nextInt(0));
position.set(Position.KEY_FUEL_LEVEL, parser.nextInt(0));
diff --git a/src/org/traccar/protocol/SuntechProtocolDecoder.java b/src/org/traccar/protocol/SuntechProtocolDecoder.java
index 42e81f60c..6dfc6f77f 100644
--- a/src/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -19,6 +19,7 @@ import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
import org.traccar.DeviceSession;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -32,18 +33,34 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
private int protocolType;
private boolean hbm;
+ private boolean includeAdc;
+ private boolean includeTemp;
public SuntechProtocolDecoder(SuntechProtocol protocol) {
super(protocol);
protocolType = Context.getConfig().getInteger(getProtocolName() + ".protocolType");
hbm = Context.getConfig().getBoolean(getProtocolName() + ".hbm");
+ includeAdc = Context.getConfig().getBoolean(getProtocolName() + ".includeAdc");
+ includeTemp = Context.getConfig().getBoolean(getProtocolName() + ".includeTemp");
}
public void setProtocolType(int protocolType) {
this.protocolType = protocolType;
}
+ public void setHbm(boolean hbm) {
+ this.hbm = hbm;
+ }
+
+ public void setIncludeAdc(boolean includeAdc) {
+ this.includeAdc = includeAdc;
+ }
+
+ public void setIncludeTemp(boolean includeTemp) {
+ this.includeTemp = includeTemp;
+ }
+
private Position decode9(
Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException {
int index = 1;
@@ -54,19 +71,19 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ if (deviceSession == null) {
+ return null;
+ }
+
Position position = new Position();
position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
if (type.equals("Emergency") || type.equals("Alert")) {
position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
}
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
- if (deviceSession == null) {
- return null;
- }
- position.setDeviceId(deviceSession.getDeviceId());
-
if (!type.equals("Alert") || protocolType == 0) {
position.set(Position.KEY_VERSION_FW, values[index++]);
}
@@ -87,13 +104,60 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.setValid(values[index++].equals("1"));
if (protocolType == 1) {
- position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index]));
+ position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index++]));
}
return position;
}
- private Position decode23(
+ private String decodeEmergency(int value) {
+ switch (value) {
+ case 1:
+ return Position.ALARM_SOS;
+ case 2:
+ return Position.ALARM_PARKING;
+ case 3:
+ return Position.ALARM_POWER_CUT;
+ case 5:
+ case 6:
+ return Position.ALARM_DOOR;
+ case 7:
+ return Position.ALARM_MOVEMENT;
+ case 8:
+ return Position.ALARM_SHOCK;
+ default:
+ return null;
+ }
+ }
+
+ private String decodeAlert(int value) {
+ switch (value) {
+ case 1:
+ return Position.ALARM_OVERSPEED;
+ case 5:
+ return Position.ALARM_GEOFENCE_EXIT;
+ case 6:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 14:
+ return Position.ALARM_LOW_BATTERY;
+ case 15:
+ return Position.ALARM_SHOCK;
+ case 16:
+ return Position.ALARM_ACCIDENT;
+ case 46:
+ return Position.ALARM_ACCELERATION;
+ case 47:
+ return Position.ALARM_BRAKING;
+ case 48:
+ return Position.ALARM_ACCIDENT;
+ case 50:
+ return Position.ALARM_JAMMING;
+ default:
+ return null;
+ }
+ }
+
+ private Position decode235(
Channel channel, SocketAddress remoteAddress, String protocol, String[] values) throws ParseException {
int index = 0;
@@ -103,20 +167,17 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position();
- position.setProtocol(getProtocolName());
-
- if (type.equals("EMG") || type.equals("ALT")) {
- position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
- }
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_TYPE, type);
- if (protocol.equals("ST300")) {
+ if (protocol.equals("ST300") || protocol.equals("ST500")) {
index += 1; // model
}
@@ -126,7 +187,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
position.setTime(dateFormat.parse(values[index++] + values[index++]));
- index += 1; // cell
+ if (!protocol.equals("ST500")) {
+ index += 1; // cell
+ }
position.setLatitude(Double.parseDouble(values[index++]));
position.setLongitude(Double.parseDouble(values[index++]));
@@ -140,12 +203,32 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, Integer.parseInt(values[index++]));
position.set(Position.KEY_POWER, Double.parseDouble(values[index++]));
- position.set(Position.PREFIX_IO + 1, values[index++]);
-
- index += 1; // mode
+ String io = values[index++];
+ if (io.length() == 6) {
+ position.set(Position.KEY_IGNITION, io.charAt(0) == '1');
+ position.set(Position.PREFIX_IN + 1, io.charAt(1) == '1');
+ position.set(Position.PREFIX_IN + 2, io.charAt(2) == '1');
+ position.set(Position.PREFIX_IN + 3, io.charAt(3) == '1');
+ position.set(Position.PREFIX_OUT + 1, io.charAt(4) == '1');
+ position.set(Position.PREFIX_OUT + 2, io.charAt(5) == '1');
+ }
- if (type.equals("STT")) {
- position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
+ switch (type) {
+ case "STT":
+ index += 1; // mode
+ position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
+ break;
+ case "EMG":
+ position.set(Position.KEY_ALARM, decodeEmergency(Integer.parseInt(values[index++])));
+ break;
+ case "EVT":
+ position.set(Position.KEY_EVENT, Integer.parseInt(values[index++]));
+ break;
+ case "ALT":
+ position.set(Position.KEY_ALARM, decodeAlert(Integer.parseInt(values[index++])));
+ break;
+ default:
+ break;
}
if (hbm) {
@@ -155,7 +238,35 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
if (index < values.length) {
- position.set(Position.KEY_BATTERY, Double.parseDouble(values[index]));
+ position.set(Position.KEY_BATTERY, Double.parseDouble(values[index++]));
+ }
+
+ if (index < values.length && values[index++].equals("0")) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ if (includeAdc) {
+ position.set(Position.PREFIX_ADC + 1, Double.parseDouble(values[index++]));
+ position.set(Position.PREFIX_ADC + 2, Double.parseDouble(values[index++]));
+ position.set(Position.PREFIX_ADC + 3, Double.parseDouble(values[index++]));
+ }
+
+ if (values.length - index >= 2) {
+ String driverUniqueId = values[index++];
+ if (values[index++].equals("1") && !driverUniqueId.isEmpty()) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, driverUniqueId);
+ }
+ }
+
+ if (includeTemp) {
+ for (int i = 1; i <= 3; i++) {
+ String temperature = values[index++];
+ String value = temperature.substring(temperature.indexOf(':') + 1);
+ if (!value.isEmpty()) {
+ position.set(Position.PREFIX_TEMP + i, Double.parseDouble(value));
+ }
+ }
+
}
}
@@ -163,18 +274,105 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeUniversal(
+ Channel channel, SocketAddress remoteAddress, String[] values) throws ParseException {
+ int index = 0;
+
+ String type = values[index++];
+
+ if (!type.equals("STT")) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_TYPE, type);
+
+ int mask = Integer.parseInt(values[index++], 16);
+
+ if (BitUtil.check(mask, 1)) {
+ index += 1; // model
+ }
+
+ if (BitUtil.check(mask, 2)) {
+ position.set(Position.KEY_VERSION_FW, values[index++]);
+ }
+
+ if (BitUtil.check(mask, 3) && values[index++].equals("0")) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ if (BitUtil.check(mask, 4) && BitUtil.check(mask, 5)) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(values[index++] + values[index++]));
+ }
+
+ if (BitUtil.check(mask, 6)) {
+ index += 1; // cell
+ }
+
+ if (BitUtil.check(mask, 7)) {
+ index += 1; // mcc
+ }
+
+ if (BitUtil.check(mask, 8)) {
+ index += 1; // mnc
+ }
+
+ if (BitUtil.check(mask, 9)) {
+ index += 1; // lac
+ }
+
+ if (BitUtil.check(mask, 10)) {
+ position.set(Position.KEY_RSSI, Integer.parseInt(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 11)) {
+ position.setLatitude(Double.parseDouble(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 12)) {
+ position.setLongitude(Double.parseDouble(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 13)) {
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ }
+
+ if (BitUtil.check(mask, 14)) {
+ position.setCourse(Double.parseDouble(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 15)) {
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+ }
+
+ if (BitUtil.check(mask, 16)) {
+ position.setValid(values[index++].equals("1"));
+ }
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
String[] values = ((String) msg).split(";");
- String protocol = values[0].substring(0, 5);
-
- if (protocol.equals("ST910")) {
+ if (values[0].length() < 5) {
+ return decodeUniversal(channel, remoteAddress, values);
+ } else if (values[0].equals("ST910")) {
return decode9(channel, remoteAddress, values);
} else {
- return decode23(channel, remoteAddress, protocol, values);
+ return decode235(channel, remoteAddress, values[0].substring(0, 5), values);
}
}
diff --git a/src/org/traccar/protocol/T55ProtocolDecoder.java b/src/org/traccar/protocol/T55ProtocolDecoder.java
index dee7210b1..6b4ee6ebd 100644
--- a/src/org/traccar/protocol/T55ProtocolDecoder.java
+++ b/src/org/traccar/protocol/T55ProtocolDecoder.java
@@ -18,6 +18,7 @@ package org.traccar.protocol;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.socket.DatagramChannel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -98,8 +99,11 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
private Position decodeGprmc(
DeviceSession deviceSession, String sentence, SocketAddress remoteAddress, Channel channel) {
- if (channel != null && !(channel instanceof DatagramChannel)) {
- channel.write("OK1\r\n");
+ if (deviceSession != null && channel != null && !(channel instanceof DatagramChannel)) {
+ if (Context.getIdentityManager().lookupAttributeBoolean(
+ deviceSession.getDeviceId(), getProtocolName() + ".ack", false, true)) {
+ channel.write("OK1\r\n");
+ }
}
Parser parser = new Parser(PATTERN_GPRMC, sentence);
diff --git a/src/org/traccar/protocol/TaipProtocolDecoder.java b/src/org/traccar/protocol/TaipProtocolDecoder.java
index 7702a89fb..e7117a5c9 100644
--- a/src/org/traccar/protocol/TaipProtocolDecoder.java
+++ b/src/org/traccar/protocol/TaipProtocolDecoder.java
@@ -18,6 +18,7 @@ package org.traccar.protocol;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.DateUtil;
@@ -67,7 +68,6 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
.number("(x{8})") // odometer
.number("[01]") // gps power
.groupEnd("?")
- .number("(d)") // fix mode
.any()
.compile();
@@ -104,15 +104,38 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position();
position.setProtocol(getProtocolName());
+ Integer event = null;
+
if (parser.hasNext(3)) {
- position.set(Position.KEY_EVENT, parser.nextInt(0));
+ event = parser.nextInt();
position.setTime(getTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0)));
} else if (parser.hasNext()) {
position.setTime(getTime(parser.nextInt(0)));
}
if (parser.hasNext()) {
- position.set(Position.KEY_EVENT, parser.nextInt(0));
+ event = parser.nextInt();
+ }
+
+ if (event != null) {
+ switch (event) {
+ case 22:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 23:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 24:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCIDENT);
+ break;
+ case 26:
+ case 28:
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ default:
+ position.set(Position.KEY_EVENT, event);
+ break;
+ }
}
if (parser.hasNext(6)) {
@@ -138,7 +161,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0));
}
- position.setValid(parser.nextInt(0) != 0);
+ position.setValid(true);
String[] attributes = null;
beginIndex = sentence.indexOf(';');
@@ -150,6 +173,12 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
attributes = sentence.substring(beginIndex, endIndex).split(";");
}
+ return decodeAttributes(channel, remoteAddress, position, attributes);
+ }
+
+ private Position decodeAttributes(
+ Channel channel, SocketAddress remoteAddress, Position position, String[] attributes) {
+
String uniqueId = null;
DeviceSession deviceSession = null;
String messageIndex = null;
@@ -161,7 +190,6 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
String key = attribute.substring(0, index).toLowerCase();
String value = attribute.substring(index + 1);
switch (key) {
-
case "id":
uniqueId = value;
deviceSession = getDeviceSession(channel, remoteAddress, value);
@@ -169,23 +197,30 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
}
break;
-
+ case "io":
+ position.set(Position.KEY_IGNITION, BitUtil.check(value.charAt(0) - '0', 0));
+ position.set(Position.KEY_CHARGE, BitUtil.check(value.charAt(0) - '0', 1));
+ position.set(Position.KEY_OUTPUT, value.charAt(1) - '0');
+ position.set(Position.KEY_INPUT, value.charAt(2) - '0');
+ break;
+ case "ix":
+ position.set(Position.PREFIX_IO + 1, value);
+ break;
+ case "ad":
+ position.set(Position.PREFIX_ADC + 1, Integer.parseInt(value));
+ break;
case "sv":
position.set(Position.KEY_SATELLITES, Integer.parseInt(value));
break;
-
case "bl":
- position.set(Position.KEY_BATTERY, Integer.parseInt(value));
+ position.set(Position.KEY_BATTERY, Integer.parseInt(value) * 0.001);
break;
-
case "vo":
position.set(Position.KEY_ODOMETER, Long.parseLong(value));
break;
-
default:
position.set(key, value);
break;
-
}
} else if (attribute.startsWith("#")) {
messageIndex = attribute;
diff --git a/src/org/traccar/protocol/TelicProtocolDecoder.java b/src/org/traccar/protocol/TelicProtocolDecoder.java
index 62b756ab5..2c0d714c6 100644
--- a/src/org/traccar/protocol/TelicProtocolDecoder.java
+++ b/src/org/traccar/protocol/TelicProtocolDecoder.java
@@ -34,8 +34,8 @@ public class TelicProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN = new PatternBuilder()
.number("dddd")
- .number("(d{6})") // device id
- .number("(d+),") // type
+ .number("(d{6}|d{15})") // device id
+ .number("(d{1,2}),") // type
.number("d{12},") // event time
.number("d+,")
.number("(dd)(dd)(dd)") // date (ddmmyy)
diff --git a/src/org/traccar/protocol/TeltonikaProtocol.java b/src/org/traccar/protocol/TeltonikaProtocol.java
index 524e6d5b5..d0177da97 100644
--- a/src/org/traccar/protocol/TeltonikaProtocol.java
+++ b/src/org/traccar/protocol/TeltonikaProtocol.java
@@ -39,14 +39,14 @@ public class TeltonikaProtocol extends BaseProtocol {
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new TeltonikaFrameDecoder());
pipeline.addLast("objectEncoder", new TeltonikaProtocolEncoder());
- pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this));
+ pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this, false));
}
});
serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("objectEncoder", new TeltonikaProtocolEncoder());
- pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this));
+ pipeline.addLast("objectDecoder", new TeltonikaProtocolDecoder(TeltonikaProtocol.this, true));
}
});
}
diff --git a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 3f5b68f67..80f0045d5 100644
--- a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -18,8 +18,8 @@ package org.traccar.protocol;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
-import org.jboss.netty.channel.socket.DatagramChannel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.helper.BitUtil;
import org.traccar.helper.UnitsConverter;
@@ -35,8 +35,17 @@ import java.util.List;
public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
- public TeltonikaProtocolDecoder(TeltonikaProtocol protocol) {
+ private boolean connectionless;
+ private boolean extended;
+
+ public void setExtended(boolean extended) {
+ this.extended = extended;
+ }
+
+ public TeltonikaProtocolDecoder(TeltonikaProtocol protocol, boolean connectionless) {
super(protocol);
+ this.connectionless = connectionless;
+ this.extended = Context.getConfig().getBoolean(getProtocolName() + ".extended");
}
private DeviceSession parseIdentification(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
@@ -67,7 +76,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_TYPE, buf.readUnsignedByte());
- position.set(Position.KEY_COMMAND, buf.readBytes(buf.readInt()).toString(StandardCharsets.US_ASCII));
+ position.set(Position.KEY_RESULT, buf.readBytes(buf.readInt()).toString(StandardCharsets.US_ASCII));
}
@@ -84,7 +93,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeParameter(Position position, int id, ChannelBuffer buf, int length) {
+ private void decodeOtherParameter(Position position, int id, ChannelBuffer buf, int length) {
switch (id) {
case 1:
case 2:
@@ -95,15 +104,24 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
case 9:
position.set(Position.PREFIX_ADC + 1, readValue(buf, length, false));
break;
+ case 17:
+ position.set("axisX", readValue(buf, length, true));
+ break;
+ case 18:
+ position.set("axisY", readValue(buf, length, true));
+ break;
+ case 19:
+ position.set("axisZ", readValue(buf, length, true));
+ break;
+ case 21:
+ position.set(Position.KEY_RSSI, readValue(buf, length, false));
+ break;
case 66:
position.set(Position.KEY_POWER, readValue(buf, length, false) * 0.001);
break;
case 67:
position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001);
break;
- case 70:
- position.set(Position.KEY_DEVICE_TEMP, readValue(buf, length, true) * 0.1);
- break;
case 72:
position.set(Position.PREFIX_TEMP + 1, readValue(buf, length, true) * 0.1);
break;
@@ -114,17 +132,102 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1);
break;
case 78:
- position.set(Position.KEY_RFID, readValue(buf, length, false));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.format("%016X", readValue(buf, length, false)));
+ break;
+ case 80:
+ position.set("workMode", readValue(buf, length, false));
+ break;
+ case 179:
+ position.set(Position.PREFIX_OUT + 1, readValue(buf, length, false) == 1);
+ break;
+ case 180:
+ position.set(Position.PREFIX_OUT + 2, readValue(buf, length, false) == 1);
+ break;
+ case 181:
+ position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1);
break;
case 182:
position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1);
break;
+ case 239:
+ position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1);
+ break;
+ case 240:
+ position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1);
+ break;
+ case 241:
+ position.set(Position.KEY_OPERATOR, readValue(buf, length, false));
+ break;
default:
position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
break;
}
}
+ private void decodeGh3000Parameter(Position position, int id, ChannelBuffer buf, int length) {
+ switch (id) {
+ case 1:
+ position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length, false));
+ break;
+ case 2:
+ position.set("usbConnected", readValue(buf, length, false) == 1);
+ break;
+ case 5:
+ position.set("uptime", readValue(buf, length, false));
+ break;
+ case 20:
+ position.set(Position.KEY_HDOP, readValue(buf, length, false) * 0.1);
+ break;
+ case 21:
+ position.set(Position.KEY_VDOP, readValue(buf, length, false) * 0.1);
+ break;
+ case 22:
+ position.set(Position.KEY_PDOP, readValue(buf, length, false) * 0.1);
+ break;
+ case 67:
+ position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001);
+ break;
+ case 221:
+ position.set("button", readValue(buf, length, false));
+ break;
+ case 222:
+ if (readValue(buf, length, false) == 1) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+ break;
+ case 240:
+ position.set(Position.KEY_MOTION, readValue(buf, length, false) == 1);
+ break;
+ case 244:
+ position.set(Position.KEY_ROAMING, readValue(buf, length, false) == 1);
+ break;
+ default:
+ position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
+ break;
+ }
+ }
+
+ private void decodeParameter(Position position, int id, ChannelBuffer buf, int length, int codec) {
+ if (codec == CODEC_GH3000) {
+ decodeGh3000Parameter(position, id, buf, length);
+ } else {
+ decodeOtherParameter(position, id, buf, length);
+ }
+ }
+
+ private void decodeNetwork(Position position) {
+ long cid = position.getLong(Position.PREFIX_IO + 205);
+ int lac = position.getInteger(Position.PREFIX_IO + 206);
+ if (cid != 0 && lac != 0) {
+ CellTower cellTower = CellTower.fromLacCid(lac, cid);
+ long operator = position.getInteger(Position.KEY_OPERATOR);
+ if (operator != 0) {
+ cellTower.setOperator(operator);
+ }
+ position.setNetwork(new Network(cellTower));
+ }
+ }
+
private void decodeLocation(Position position, ChannelBuffer buf, int codec) {
int globalMask = 0x0f;
@@ -171,14 +274,19 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
cellTower.setSignalStrength((int) buf.readUnsignedByte());
}
- position.setNetwork(new Network(cellTower));
+ if (BitUtil.check(locationMask, 7)) {
+ cellTower.setOperator(buf.readUnsignedInt());
+ }
- } else if (BitUtil.check(locationMask, 6)) {
- position.set(Position.KEY_RSSI, buf.readUnsignedByte());
- }
+ position.setNetwork(new Network(cellTower));
- if (BitUtil.check(locationMask, 7)) {
- position.set(Position.KEY_OPERATOR, buf.readUnsignedInt());
+ } else {
+ if (BitUtil.check(locationMask, 6)) {
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ }
+ if (BitUtil.check(locationMask, 7)) {
+ position.set(Position.KEY_OPERATOR, buf.readUnsignedInt());
+ }
}
} else {
@@ -215,7 +323,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 1)) {
int cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, buf.readUnsignedByte(), buf, 1);
+ decodeParameter(position, buf.readUnsignedByte(), buf, 1, codec);
}
}
@@ -223,7 +331,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 2)) {
int cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, buf.readUnsignedByte(), buf, 2);
+ decodeParameter(position, buf.readUnsignedByte(), buf, 2, codec);
}
}
@@ -231,7 +339,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(globalMask, 3)) {
int cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, buf.readUnsignedByte(), buf, 4);
+ decodeParameter(position, buf.readUnsignedByte(), buf, 4, codec);
}
}
@@ -239,17 +347,27 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
if (codec == CODEC_FM4X00) {
int cnt = buf.readUnsignedByte();
for (int j = 0; j < cnt; j++) {
- decodeParameter(position, buf.readUnsignedByte(), buf, 8);
+ decodeOtherParameter(position, buf.readUnsignedByte(), buf, 8);
}
}
+ // Read 16 byte data
+ if (extended) {
+ int cnt = buf.readUnsignedByte();
+ for (int j = 0; j < cnt; j++) {
+ position.set(Position.PREFIX_IO + buf.readUnsignedByte(), ChannelBuffers.hexDump(buf.readBytes(16)));
+ }
+ }
+
+ decodeNetwork(position);
+
}
private List<Position> parseData(
Channel channel, SocketAddress remoteAddress, ChannelBuffer buf, int locationPacketId, String... imei) {
List<Position> positions = new LinkedList<>();
- if (!(channel instanceof DatagramChannel)) {
+ if (!connectionless) {
buf.readUnsignedInt(); // data length
}
@@ -278,7 +396,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
if (channel != null) {
- if (channel instanceof DatagramChannel) {
+ if (connectionless) {
ChannelBuffer response = ChannelBuffers.dynamicBuffer();
response.writeShort(5);
response.writeShort(0);
@@ -301,7 +419,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
ChannelBuffer buf = (ChannelBuffer) msg;
- if (channel instanceof DatagramChannel) {
+ if (connectionless) {
return decodeUdp(channel, remoteAddress, buf);
} else {
return decodeTcp(channel, remoteAddress, buf);
diff --git a/src/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/org/traccar/protocol/Tk103ProtocolDecoder.java
index 4c7da12e0..be3def453 100644
--- a/src/org/traccar/protocol/Tk103ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Tk103ProtocolDecoder.java
@@ -23,7 +23,6 @@ import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
-import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
@@ -33,8 +32,11 @@ import java.util.regex.Pattern;
public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
+ private boolean decodeLow;
+
public Tk103ProtocolDecoder(Tk103Protocol protocol) {
super(protocol);
+ decodeLow = Context.getConfig().getBoolean(getProtocolName() + ".decodeLow");
}
private static final Pattern PATTERN = new PatternBuilder()
@@ -50,7 +52,14 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.d)(?:d*,)?") // speed
.number("(dd)(dd)(dd),?") // time (hhmmss)
.number("(d+.?d{1,2}),?") // course
- .number("(?:([01]{8})|(x{8}))?,?") // state
+ .groupBegin()
+ .number("([01])") // charge
+ .number("([01])") // ignition
+ .number("(x)") // io
+ .number("(x)") // io
+ .number("(x)") // io
+ .number("(xxx),?") // fuel
+ .groupEnd("?")
.number("(?:L(x+))?") // odometer
.any()
.number("([+-]ddd.d)?") // temperature
@@ -200,17 +209,11 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
if (channel != null) {
String id = sentence.substring(0, 12);
String type = sentence.substring(12, 16);
- if (type.equals("BP00") || type.equals("BP05")) {
- String content = sentence.substring(16);
- if (content.length() >= 15) {
- getDeviceSession(channel, remoteAddress, content.substring(0, 15));
- }
- if (type.equals("BP00")) {
- channel.write("(" + id + "AP01HSO)");
- return null;
- } else if (type.equals("BP05")) {
- channel.write("(" + id + "AP05)");
- }
+ if (type.equals("BP00")) {
+ channel.write("(" + id + "AP01HSO)");
+ return null;
+ } else if (type.equals("BP05")) {
+ channel.write("(" + id + "AP05)");
}
}
@@ -249,32 +252,44 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
position.setLatitude(parser.nextCoordinate());
position.setLongitude(parser.nextCoordinate());
- switch (Context.getConfig().getString(getProtocolName() + ".speed", "kmh")) {
- case "kn":
- position.setSpeed(parser.nextDouble(0));
- break;
- case "mph":
- position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble(0)));
- break;
- default:
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- break;
- }
+ position.setSpeed(convertSpeed(parser.nextDouble(0), "kmh"));
dateBuilder.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
position.setTime(dateBuilder.getDate());
position.setCourse(parser.nextDouble(0));
- String status = parser.next();
- if (status != null) {
- position.set(Position.KEY_STATUS, status); // binary status
+ if (parser.hasNext(6)) {
+ position.set(Position.KEY_CHARGE, parser.nextInt() == 0);
+ position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
+
+ int mask1 = parser.nextHexInt();
+ position.set(Position.PREFIX_IN + 2, BitUtil.check(mask1, 0) ? 1 : 0);
+ position.set("panic", BitUtil.check(mask1, 1) ? 1 : 0);
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(mask1, 2) ? 1 : 0);
+ if (decodeLow || BitUtil.check(mask1, 3)) {
+ position.set(Position.KEY_BLOCKED, BitUtil.check(mask1, 3) ? 1 : 0);
+ }
+
+ int mask2 = parser.nextHexInt();
+ for (int i = 0; i < 3; i++) {
+ if (decodeLow || BitUtil.check(mask2, i)) {
+ position.set("hs" + (3 - i), BitUtil.check(mask2, i) ? 1 : 0);
+ }
+ }
+ if (decodeLow || BitUtil.check(mask2, 3)) {
+ position.set(Position.KEY_DOOR, BitUtil.check(mask2, 3) ? 1 : 0);
+ }
+
+ int mask3 = parser.nextHexInt();
+ for (int i = 1; i <= 3; i++) {
+ if (decodeLow || BitUtil.check(mask3, i)) {
+ position.set("ls" + (3 - i + 1), BitUtil.check(mask3, i) ? 1 : 0);
+ }
+ }
- int value = Integer.parseInt(new StringBuilder(status).reverse().toString(), 2);
- position.set(Position.KEY_CHARGE, !BitUtil.check(value, 0));
- position.set(Position.KEY_IGNITION, BitUtil.check(value, 1));
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextHexInt());
}
- position.set(Position.KEY_STATUS, parser.next()); // hex status
if (parser.hasNext()) {
position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0));
diff --git a/src/org/traccar/protocol/TlvProtocol.java b/src/org/traccar/protocol/TlvProtocol.java
new file mode 100644
index 000000000..da8d88b8a
--- /dev/null
+++ b/src/org/traccar/protocol/TlvProtocol.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@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.protocol;
+
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class TlvProtocol extends BaseProtocol {
+
+ public TlvProtocol() {
+ super("tlv");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder('\0'));
+ pipeline.addLast("objectDecoder", new TlvProtocolDecoder(TlvProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/TlvProtocolDecoder.java b/src/org/traccar/protocol/TlvProtocolDecoder.java
new file mode 100644
index 000000000..41d65be09
--- /dev/null
+++ b/src/org/traccar/protocol/TlvProtocolDecoder.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@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.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class TlvProtocolDecoder extends BaseProtocolDecoder {
+
+ public TlvProtocolDecoder(TlvProtocol protocol) {
+ super(protocol);
+ }
+
+ private void sendResponse(Channel channel, String type, String... arguments) {
+ if (channel != null) {
+ ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ response.writeBytes(ChannelBuffers.copiedBuffer(type, StandardCharsets.US_ASCII));
+ for (String argument : arguments) {
+ response.writeByte(argument.length());
+ response.writeBytes(ChannelBuffers.copiedBuffer(argument, StandardCharsets.US_ASCII));
+ }
+ response.writeByte(0);
+ channel.write(response);
+ }
+ }
+
+ private String readArgument(ChannelBuffer buf) {
+ return buf.readBytes(buf.readUnsignedByte()).toString(StandardCharsets.US_ASCII);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ String type = buf.readBytes(2).toString(StandardCharsets.US_ASCII);
+
+ if (channel != null) {
+ switch (type) {
+ case "0A":
+ case "0C":
+ sendResponse(channel, type);
+ break;
+ case "0B":
+ sendResponse(channel, type, "1482202689", "10", "20", "15");
+ break;
+ case "0E":
+ case "0F":
+ sendResponse(channel, type, "30", "Unknown");
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (type.equals("0E")) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, readArgument(buf));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setTime(new Date(Long.parseLong(readArgument(buf)) * 1000));
+
+ readArgument(buf); // location identifier
+
+ position.setLongitude(Double.parseDouble(readArgument(buf)));
+ position.setLatitude(Double.parseDouble(readArgument(buf)));
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(readArgument(buf))));
+ position.setCourse(Double.parseDouble(readArgument(buf)));
+
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(readArgument(buf)));
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/TmgProtocolDecoder.java b/src/org/traccar/protocol/TmgProtocolDecoder.java
index c10523117..b8458dd52 100644
--- a/src/org/traccar/protocol/TmgProtocolDecoder.java
+++ b/src/org/traccar/protocol/TmgProtocolDecoder.java
@@ -144,7 +144,7 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + 1, parser.nextDouble(0));
position.set(Position.PREFIX_ADC + 2, parser.nextDouble(0));
position.set(Position.KEY_VERSION_FW, parser.next());
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
return position;
}
diff --git a/src/org/traccar/protocol/TotemProtocolDecoder.java b/src/org/traccar/protocol/TotemProtocolDecoder.java
index a4ae4cb8f..3c2dee8ec 100644
--- a/src/org/traccar/protocol/TotemProtocolDecoder.java
+++ b/src/org/traccar/protocol/TotemProtocolDecoder.java
@@ -171,6 +171,8 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_LOW_BATTERY;
case 0x11:
return Position.ALARM_OVERSPEED;
+ case 0x30:
+ return Position.ALARM_PARKING;
case 0x42:
return Position.ALARM_GEOFENCE_EXIT;
case 0x43:
diff --git a/src/org/traccar/protocol/TramigoProtocolDecoder.java b/src/org/traccar/protocol/TramigoProtocolDecoder.java
index b39e3eab1..b1e28e17d 100644
--- a/src/org/traccar/protocol/TramigoProtocolDecoder.java
+++ b/src/org/traccar/protocol/TramigoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -130,12 +130,13 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(matcher.group(2))));
}
- pattern = Pattern.compile("(\\d{1,2}:\\d{2} \\w{3} \\d{1,2})");
+ pattern = Pattern.compile("(\\d{1,2}:\\d{2}(:\\d{2})? \\w{3} \\d{1,2})");
matcher = pattern.matcher(sentence);
if (!matcher.find()) {
return null;
}
- DateFormat dateFormat = new SimpleDateFormat("HH:mm MMM d yyyy", Locale.ENGLISH);
+ DateFormat dateFormat = new SimpleDateFormat(
+ matcher.group(2) != null ? "HH:mm:ss MMM d yyyy" : "HH:mm MMM d yyyy", Locale.ENGLISH);
position.setTime(DateUtil.correctYear(
dateFormat.parse(matcher.group(1) + " " + Calendar.getInstance().get(Calendar.YEAR))));
diff --git a/src/org/traccar/protocol/TrvProtocolDecoder.java b/src/org/traccar/protocol/TrvProtocolDecoder.java
index 88ac76134..918748f7b 100644
--- a/src/org/traccar/protocol/TrvProtocolDecoder.java
+++ b/src/org/traccar/protocol/TrvProtocolDecoder.java
@@ -53,8 +53,8 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.number("(ddd)") // satellites
.number("(ddd)") // battery
.number("(d)") // acc
- .number("dd") // arm status
- .number("dd,") // working mode
+ .number("(dd)") // arm status
+ .number("(dd),") // working mode
.number("(d+),") // mcc
.number("(d+),") // mnc
.number("(d+),") // lac
@@ -71,9 +71,41 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
.number("(d)") // acc
.number("(dd)") // arm status
.number("(dd)") // working mode
+ .groupBegin()
+ .number("(ddd)") // interval
+ .number("d") // vibration alarm
+ .number("ddd") // vibration sensitivity
+ .number("d") // automatic arm
+ .number("dddd") // automatic arm time
+ .number("(d)") // blocked
+ .number("(d)") // power status
+ .number("(d)") // movement status
+ .groupEnd("?")
.any()
.compile();
+ private Boolean decodeOptionalValue(Parser parser, int activeValue) {
+ int value = parser.nextInt();
+ if (value != 0) {
+ return value == activeValue;
+ }
+ return null;
+ }
+
+ private void decodeCommon(Position position, Parser parser) {
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_BATTERY, parser.nextInt());
+ position.set(Position.KEY_IGNITION, decodeOptionalValue(parser, 1));
+ position.set(Position.KEY_ARMED, decodeOptionalValue(parser, 1));
+
+ int mode = parser.nextInt();
+ if (mode != 0) {
+ position.set("mode", mode);
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -84,11 +116,14 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
String type = sentence.substring(id.length(), id.length() + 4);
if (channel != null) {
+ String responseHeader = id + (char) (type.charAt(0) + 1) + type.substring(1);
if (type.equals("AP00") && id.equals("IW")) {
String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
- channel.write(id + (char) (type.charAt(0) + 1) + type.substring(1) + "," + time + ",0#");
+ channel.write(responseHeader + "," + time + ",0#");
+ } else if (type.equals("AP14")) {
+ channel.write(responseHeader + ",0.000,0.000#");
} else {
- channel.write(id + (char) (type.charAt(0) + 1) + type.substring(1) + "#");
+ channel.write(responseHeader + "#");
}
}
@@ -115,13 +150,13 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
- position.set(Position.KEY_RSSI, parser.nextInt(0));
- position.set(Position.KEY_SATELLITES, parser.nextInt(0));
- position.set(Position.KEY_BATTERY, parser.nextInt(0));
- position.set(Position.KEY_IGNITION, parser.nextInt(0) != 0);
+ decodeCommon(position, parser);
- position.set("arm", parser.nextInt(0));
- position.set("mode", parser.nextInt(0));
+ if (parser.hasNext(3)) {
+ position.set(Position.KEY_BLOCKED, decodeOptionalValue(parser, 2));
+ position.set(Position.KEY_CHARGE, decodeOptionalValue(parser, 1));
+ position.set(Position.KEY_MOTION, decodeOptionalValue(parser, 1));
+ }
return position;
@@ -137,31 +172,25 @@ public class TrvProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
- .setDate(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setValid(parser.next().equals("A"));
position.setLatitude(parser.nextCoordinate());
position.setLongitude(parser.nextCoordinate());
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
- dateBuilder.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ dateBuilder.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setTime(dateBuilder.getDate());
- position.setCourse(parser.nextDouble(0));
+ position.setCourse(parser.nextDouble());
- int rssi = parser.nextInt(0);
- position.set(Position.KEY_SATELLITES, parser.nextInt(0));
- position.set(Position.KEY_BATTERY, parser.nextInt(0));
-
- int acc = parser.nextInt(0);
- if (acc != 0) {
- position.set(Position.KEY_IGNITION, acc == 1);
- }
+ decodeCommon(position, parser);
position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), rssi)));
+ parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt())));
return position;
+
}
return null;
diff --git a/src/org/traccar/protocol/TytanProtocolDecoder.java b/src/org/traccar/protocol/TytanProtocolDecoder.java
index 030fbce78..0ae669784 100644
--- a/src/org/traccar/protocol/TytanProtocolDecoder.java
+++ b/src/org/traccar/protocol/TytanProtocolDecoder.java
@@ -111,7 +111,7 @@ public class TytanProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OBD_ODOMETER, buf.readUnsignedInt() * 5);
break;
case 150:
- position.set("door", buf.readUnsignedByte());
+ position.set(Position.KEY_DOOR, buf.readUnsignedByte());
break;
default:
buf.skipBytes(length);
diff --git a/src/org/traccar/protocol/TzoneProtocolDecoder.java b/src/org/traccar/protocol/TzoneProtocolDecoder.java
index 69aa916df..079ad3126 100644
--- a/src/org/traccar/protocol/TzoneProtocolDecoder.java
+++ b/src/org/traccar/protocol/TzoneProtocolDecoder.java
@@ -43,7 +43,7 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
case 0x11:
return Position.ALARM_OVERSPEED;
case 0x14:
- return Position.ALARM_BREAKING;
+ return Position.ALARM_BRAKING;
case 0x15:
return Position.ALARM_ACCELERATION;
case 0x30:
@@ -203,7 +203,16 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
if (blockLength >= 13) {
position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
position.set("terminalInfo", buf.readUnsignedByte());
- position.set(Position.PREFIX_IO + 1, buf.readUnsignedShort());
+
+ int status = buf.readUnsignedByte();
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(status, 0));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(status, 1));
+ status = buf.readUnsignedByte();
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(status, 4));
+ if (BitUtil.check(status, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
position.set("gsmStatus", buf.readUnsignedByte());
position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
diff --git a/src/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/org/traccar/protocol/UlbotechProtocolDecoder.java
index 1b22eeb75..31a3d2cfe 100644
--- a/src/org/traccar/protocol/UlbotechProtocolDecoder.java
+++ b/src/org/traccar/protocol/UlbotechProtocolDecoder.java
@@ -308,7 +308,8 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
break;
case DATA_RFID:
- position.set(Position.KEY_RFID, buf.readBytes(length - 1).toString(StandardCharsets.US_ASCII));
+ position.set(Position.KEY_DRIVER_UNIQUE_ID,
+ buf.readBytes(length - 1).toString(StandardCharsets.US_ASCII));
position.set("authorized", buf.readUnsignedByte() != 0);
break;
diff --git a/src/org/traccar/protocol/VisiontekProtocolDecoder.java b/src/org/traccar/protocol/VisiontekProtocolDecoder.java
index 636a3d640..f32c9fbfe 100644
--- a/src/org/traccar/protocol/VisiontekProtocolDecoder.java
+++ b/src/org/traccar/protocol/VisiontekProtocolDecoder.java
@@ -130,7 +130,7 @@ public class VisiontekProtocolDecoder extends BaseProtocolDecoder {
position.setValid(parser.next().equals("A"));
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
return position;
}
diff --git a/src/org/traccar/protocol/Vt200ProtocolDecoder.java b/src/org/traccar/protocol/Vt200ProtocolDecoder.java
index b9af11b43..2ae24efbb 100644
--- a/src/org/traccar/protocol/Vt200ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Vt200ProtocolDecoder.java
@@ -27,6 +27,8 @@ import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.util.Arrays;
+import java.util.Date;
public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
@@ -40,6 +42,13 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
return degrees + minutes * 0.0001 / 60;
}
+ protected Date decodeDate(ChannelBuffer buf) {
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDateReverse(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2))
+ .setTime(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2));
+ return dateBuilder.getDate();
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -57,7 +66,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
int type = buf.readUnsignedShort();
buf.readUnsignedShort(); // length
- if (type == 0x2084) {
+ if (type == 0x2086 || type == 0x2084 || type == 0x2082) {
Position position = new Position();
position.setProtocol(getProtocolName());
@@ -66,12 +75,7 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // data type
buf.readUnsignedShort(); // trip id
- DateBuilder dateBuilder = new DateBuilder();
- dateBuilder.setDateReverse(
- BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2));
- dateBuilder.setTime(
- BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2));
- position.setTime(dateBuilder.getDate());
+ position.setTime(decodeDate(buf));
position.setLatitude(decodeCoordinate(BcdUtil.readInteger(buf, 8)));
position.setLongitude(decodeCoordinate(BcdUtil.readInteger(buf, 9)));
@@ -97,6 +101,48 @@ public class Vt200ProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type == 0x3088) {
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ buf.readUnsignedShort(); // trip id
+ buf.skipBytes(8); // imei
+ buf.skipBytes(8); // imsi
+
+ position.set("tripStart", decodeDate(buf).getTime());
+ position.set("tripEnd", decodeDate(buf).getTime());
+ position.set("drivingTime", buf.readUnsignedShort());
+
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt());
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt());
+
+ position.set("maxSpeed", UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ position.set("maxRpm", buf.readUnsignedShort());
+ position.set("maxTemp", buf.readUnsignedByte() - 40);
+ position.set("hardAccelerationCount", buf.readUnsignedByte());
+ position.set("hardBrakingCount", buf.readUnsignedByte());
+
+ for (String speedType : Arrays.asList("over", "high", "normal", "low")) {
+ position.set(speedType + "SpeedTime", buf.readUnsignedShort());
+ position.set(speedType + "SpeedDistance", buf.readUnsignedInt());
+ position.set(speedType + "SpeedFuel", buf.readUnsignedInt());
+ }
+
+ position.set("idleTime", buf.readUnsignedShort());
+ position.set("idleFuel", buf.readUnsignedInt());
+
+ position.set("hardCorneringCount", buf.readUnsignedByte());
+ position.set("overspeedCount", buf.readUnsignedByte());
+ position.set("overheatCount", buf.readUnsignedShort());
+ position.set("laneChangeCount", buf.readUnsignedByte());
+ position.set("emergencyRefueling", buf.readUnsignedByte());
+
+ return position;
+
}
return null;
diff --git a/src/org/traccar/protocol/VtfmsProtocolDecoder.java b/src/org/traccar/protocol/VtfmsProtocolDecoder.java
index 852b9a749..5fb687e6d 100644
--- a/src/org/traccar/protocol/VtfmsProtocolDecoder.java
+++ b/src/org/traccar/protocol/VtfmsProtocolDecoder.java
@@ -84,7 +84,7 @@ public class VtfmsProtocolDecoder extends BaseProtocolDecoder {
case 15:
return Position.ALARM_POWER_RESTORED;
case 32:
- return Position.ALARM_BREAKING;
+ return Position.ALARM_BRAKING;
case 33:
return Position.ALARM_ACCELERATION;
default:
diff --git a/src/org/traccar/protocol/WatchProtocol.java b/src/org/traccar/protocol/WatchProtocol.java
index 42a640b85..2be2dc9ae 100644
--- a/src/org/traccar/protocol/WatchProtocol.java
+++ b/src/org/traccar/protocol/WatchProtocol.java
@@ -29,6 +29,7 @@ public class WatchProtocol extends BaseProtocol {
public WatchProtocol() {
super("watch");
setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
Command.TYPE_POSITION_SINGLE,
Command.TYPE_POSITION_PERIODIC,
Command.TYPE_SOS_NUMBER,
diff --git a/src/org/traccar/protocol/WatchProtocolDecoder.java b/src/org/traccar/protocol/WatchProtocolDecoder.java
index 57f5d7e78..86dc9456d 100644
--- a/src/org/traccar/protocol/WatchProtocolDecoder.java
+++ b/src/org/traccar/protocol/WatchProtocolDecoder.java
@@ -76,8 +76,6 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_GEOFENCE_ENTER;
} else if (BitUtil.check(status, 3)) {
return Position.ALARM_OVERSPEED;
- } else if (BitUtil.check(status, 4)) {
- return Position.ALARM_MOVEMENT;
} else if (BitUtil.check(status, 16)) {
return Position.ALARM_SOS;
} else if (BitUtil.check(status, 17)) {
@@ -207,9 +205,13 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RSSI, parser.nextInt(0));
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0));
- position.set("steps", parser.nextInt(0));
+ position.set(Position.KEY_STEPS, parser.nextInt(0));
- position.set(Position.KEY_ALARM, decodeAlarm(parser.nextHexInt(0)));
+ int status = parser.nextHexInt(0);
+ position.set(Position.KEY_ALARM, decodeAlarm(status));
+ if (BitUtil.check(status, 4)) {
+ position.set(Position.KEY_MOTION, true);
+ }
decodeTail(position, parser.next());
diff --git a/src/org/traccar/protocol/WatchProtocolEncoder.java b/src/org/traccar/protocol/WatchProtocolEncoder.java
index c5d8fad86..d2d3b52d1 100644
--- a/src/org/traccar/protocol/WatchProtocolEncoder.java
+++ b/src/org/traccar/protocol/WatchProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,13 +26,14 @@ import java.text.DecimalFormatSymbols;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.TimeZone;
public class WatchProtocolEncoder extends StringProtocolEncoder implements StringProtocolEncoder.ValueFormatter {
@Override
public String formatValue(String key, Object value) {
if (key.equals(Command.KEY_TIMEZONE)) {
- double offset = ((Number) value).longValue() / 3600.0;
+ double offset = TimeZone.getTimeZone((String) value).getRawOffset() / 3600000.0;
DecimalFormat fmt = new DecimalFormat("+#.##;-#.##", DecimalFormatSymbols.getInstance(Locale.US));
return fmt.format(offset);
}
@@ -41,8 +42,9 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin
}
+ @Override
protected String formatCommand(Command command, String format, String... keys) {
- String content = super.formatCommand(command, format, this, keys);
+ String content = formatCommand(command, format, this, keys);
return String.format("[CS*%s*%04x*%s]",
getUniqueId(command.getDeviceId()), content.length(), content);
}
@@ -97,6 +99,8 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin
protected Object encodeCommand(Command command) {
switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatCommand(command, command.getString(Command.KEY_DATA));
case Command.TYPE_POSITION_SINGLE:
return formatCommand(command, "RG");
case Command.TYPE_SOS_NUMBER:
diff --git a/src/org/traccar/protocol/WialonProtocolDecoder.java b/src/org/traccar/protocol/WialonProtocolDecoder.java
index 82098413b..4eb3b9b8e 100644
--- a/src/org/traccar/protocol/WialonProtocolDecoder.java
+++ b/src/org/traccar/protocol/WialonProtocolDecoder.java
@@ -109,7 +109,7 @@ public class WialonProtocolDecoder extends BaseProtocolDecoder {
}
}
- position.set(Position.KEY_RFID, parser.next());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
if (parser.hasNext()) {
String[] values = parser.next().split(",");
diff --git a/src/org/traccar/protocol/XexunProtocolDecoder.java b/src/org/traccar/protocol/XexunProtocolDecoder.java
index 20804bbb4..bb4b4f48c 100644
--- a/src/org/traccar/protocol/XexunProtocolDecoder.java
+++ b/src/org/traccar/protocol/XexunProtocolDecoder.java
@@ -17,13 +17,11 @@ package org.traccar.protocol;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
-import org.traccar.helper.UnitsConverter;
import java.net.SocketAddress;
import java.util.regex.Pattern;
@@ -70,9 +68,11 @@ public class XexunProtocolDecoder extends BaseProtocolDecoder {
if (value != null) {
switch (value.toLowerCase()) {
case "acc on":
+ case "accstart":
position.set(Position.KEY_IGNITION, true);
break;
case "acc off":
+ case "accstop":
position.set(Position.KEY_IGNITION, false);
break;
case "help me!":
@@ -121,14 +121,7 @@ public class XexunProtocolDecoder extends BaseProtocolDecoder {
position.setLatitude(parser.nextCoordinate());
position.setLongitude(parser.nextCoordinate());
- switch (Context.getConfig().getString(getProtocolName() + ".speed", "kn")) {
- case "kmh":
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- break;
- default:
- position.setSpeed(parser.nextDouble(0));
- break;
- }
+ position.setSpeed(convertSpeed(parser.nextDouble(0), "kn"));
position.setCourse(parser.nextDouble(0));
diff --git a/src/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/org/traccar/protocol/Xt2400ProtocolDecoder.java
index a42c6175f..15e8558be 100644
--- a/src/org/traccar/protocol/Xt2400ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Xt2400ProtocolDecoder.java
@@ -25,6 +25,7 @@ import org.traccar.model.Position;
import javax.xml.bind.DatatypeConverter;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -77,8 +78,6 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
for (int i : l4) {
TAG_LENGTH_MAP.put(i, 4);
}
- TAG_LENGTH_MAP.put(0x65, 17);
- TAG_LENGTH_MAP.put(0x73, 16);
TAG_LENGTH_MAP.put(0x95, 24);
}
@@ -163,12 +162,24 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
case 0x13:
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
break;
+ case 0x14:
+ position.set(Position.KEY_RSSI, buf.readShort());
+ break;
case 0x16:
position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.1);
break;
case 0x17:
position.set(Position.KEY_POWER, buf.readUnsignedByte() * 0.1);
break;
+ case 0x57:
+ position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
+ break;
+ case 0x65:
+ position.set(Position.KEY_VIN, buf.readBytes(17).toString(StandardCharsets.US_ASCII));
+ break;
+ case 0x73:
+ position.set(Position.KEY_VERSION_FW, buf.readBytes(16).toString(StandardCharsets.US_ASCII).trim());
+ break;
default:
buf.skipBytes(getTagLength(tag));
break;
diff --git a/src/org/traccar/reports/Events.java b/src/org/traccar/reports/Events.java
index 0706f1382..a13aeeeb4 100644
--- a/src/org/traccar/reports/Events.java
+++ b/src/org/traccar/reports/Events.java
@@ -42,6 +42,7 @@ public final class Events {
public static Collection<Event> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
Collection<String> types, Date from, Date to) throws SQLException {
+ ReportUtils.checkPeriodLimit(from, to);
ArrayList<Event> result = new ArrayList<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
Context.getPermissionsManager().checkDevice(userId, deviceId);
@@ -50,7 +51,7 @@ public final class Events {
for (Event event : events) {
if (all || types.contains(event.getType())) {
long geofenceId = event.getGeofenceId();
- if (geofenceId == 0 || Context.getGeofenceManager().checkGeofence(userId, geofenceId)) {
+ if (geofenceId == 0 || Context.getGeofenceManager().checkItemPermission(userId, geofenceId)) {
result.add(event);
}
}
@@ -62,6 +63,7 @@ public final class Events {
public static void getExcel(OutputStream outputStream,
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
Collection<String> types, Date from, Date to) throws SQLException, IOException {
+ ReportUtils.checkPeriodLimit(from, to);
ArrayList<DeviceReport> devicesEvents = new ArrayList<>();
ArrayList<String> sheetNames = new ArrayList<>();
HashMap<Long, String> geofenceNames = new HashMap<>();
@@ -74,8 +76,8 @@ public final class Events {
if (all || types.contains(event.getType())) {
long geofenceId = event.getGeofenceId();
if (geofenceId != 0) {
- if (Context.getGeofenceManager().checkGeofence(userId, geofenceId)) {
- Geofence geofence = Context.getGeofenceManager().getGeofence(geofenceId);
+ if (Context.getGeofenceManager().checkItemPermission(userId, geofenceId)) {
+ Geofence geofence = (Geofence) Context.getGeofenceManager().getById(geofenceId);
if (geofence != null) {
geofenceNames.put(geofenceId, geofence.getName());
}
@@ -88,11 +90,11 @@ public final class Events {
}
}
DeviceReport deviceEvents = new DeviceReport();
- Device device = Context.getIdentityManager().getDeviceById(deviceId);
+ Device device = Context.getIdentityManager().getById(deviceId);
deviceEvents.setDeviceName(device.getName());
sheetNames.add(WorkbookUtil.createSafeSheetName(deviceEvents.getDeviceName()));
if (device.getGroupId() != 0) {
- Group group = Context.getDeviceManager().getGroupById(device.getGroupId());
+ Group group = Context.getGroupsManager().getById(device.getGroupId());
if (group != null) {
deviceEvents.setGroupName(group.getName());
}
diff --git a/src/org/traccar/reports/ReportUtils.java b/src/org/traccar/reports/ReportUtils.java
index 71c567c29..f6f386e99 100644
--- a/src/org/traccar/reports/ReportUtils.java
+++ b/src/org/traccar/reports/ReportUtils.java
@@ -26,6 +26,10 @@ import org.jxls.transform.Transformer;
import org.jxls.transform.poi.PoiTransformer;
import org.jxls.util.TransformerFactory;
import org.traccar.Context;
+import org.traccar.events.MotionEventHandler;
+import org.traccar.model.DeviceState;
+import org.traccar.model.Driver;
+import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.reports.model.BaseReport;
import org.traccar.reports.model.StopReport;
@@ -39,8 +43,10 @@ import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Date;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.TimeZone;
public final class ReportUtils {
@@ -48,16 +54,27 @@ public final class ReportUtils {
private ReportUtils() {
}
+ public static void checkPeriodLimit(Date from, Date to) {
+ long limit = Context.getConfig().getLong("report.periodLimit") * 1000;
+ if (limit > 0 && to.getTime() - from.getTime() > limit) {
+ throw new IllegalArgumentException("Time period exceeds the limit");
+ }
+ }
+
public static String getDistanceUnit(long userId) {
- return (String) Context.getPermissionsManager().lookupPreference(userId, "distanceUnit", "km");
+ return (String) Context.getPermissionsManager().lookupAttribute(userId, "distanceUnit", "km");
}
public static String getSpeedUnit(long userId) {
- return (String) Context.getPermissionsManager().lookupPreference(userId, "speedUnit", "kn");
+ return (String) Context.getPermissionsManager().lookupAttribute(userId, "speedUnit", "kn");
+ }
+
+ public static String getVolumeUnit(long userId) {
+ return (String) Context.getPermissionsManager().lookupAttribute(userId, "volumeUnit", "ltr");
}
public static TimeZone getTimezone(long userId) {
- String timezone = (String) Context.getPermissionsManager().lookupPreference(userId, "timezone", null);
+ String timezone = (String) Context.getPermissionsManager().lookupAttribute(userId, "timezone", null);
return timezone != null ? TimeZone.getTimeZone(timezone) : TimeZone.getDefault();
}
@@ -101,10 +118,30 @@ public final class ReportUtils {
return 0;
}
+ public static String findDriver(Position firstPosition, Position lastPosition) {
+ if (firstPosition.getAttributes().containsKey(Position.KEY_DRIVER_UNIQUE_ID)) {
+ return firstPosition.getString(Position.KEY_DRIVER_UNIQUE_ID);
+ } else if (lastPosition.getAttributes().containsKey(Position.KEY_DRIVER_UNIQUE_ID)) {
+ return lastPosition.getString(Position.KEY_DRIVER_UNIQUE_ID);
+ }
+ return null;
+ }
+
+ public static String findDriverName(String driverUniqueId) {
+ if (driverUniqueId != null && Context.getDriversManager() != null) {
+ Driver driver = Context.getDriversManager().getDriverByUniqueId(driverUniqueId);
+ if (driver != null) {
+ return driver.getName();
+ }
+ }
+ return null;
+ }
+
public static org.jxls.common.Context initializeContext(long userId) {
org.jxls.common.Context jxlsContext = PoiTransformer.createInitialContext();
jxlsContext.putVar("distanceUnit", getDistanceUnit(userId));
jxlsContext.putVar("speedUnit", getSpeedUnit(userId));
+ jxlsContext.putVar("volumeUnit", getVolumeUnit(userId));
jxlsContext.putVar("webUrl", Context.getVelocityEngine().getProperty("web.url"));
jxlsContext.putVar("dateTool", new DateTool());
jxlsContext.putVar("numberTool", new NumberTool());
@@ -127,15 +164,6 @@ public final class ReportUtils {
transformer.write();
}
- 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.minimalParkingDuration", 300) * 1000,
- Context.getConfig().getBoolean("report.trip.greedyParking"),
- Context.getConfig().getLong("report.trip.minimalNoDataDuration", 3600) * 1000);
- }
-
private static TripReport calculateTrip(
ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
Position startTrip = positions.get(startIndex);
@@ -156,7 +184,7 @@ public final class ReportUtils {
long tripDuration = endTrip.getFixTime().getTime() - startTrip.getFixTime().getTime();
long deviceId = startTrip.getDeviceId();
trip.setDeviceId(deviceId);
- trip.setDeviceName(Context.getIdentityManager().getDeviceById(deviceId).getName());
+ trip.setDeviceName(Context.getIdentityManager().getById(deviceId).getName());
trip.setStartPositionId(startTrip.getId());
trip.setStartLat(startTrip.getLatitude());
@@ -176,6 +204,9 @@ public final class ReportUtils {
trip.setMaxSpeed(speedMax);
trip.setSpentFuel(calculateFuel(startTrip, endTrip));
+ trip.setDriverUniqueId(findDriver(startTrip, endTrip));
+ trip.setDriverName(findDriverName(trip.getDriverUniqueId()));
+
return trip;
}
@@ -187,7 +218,7 @@ public final class ReportUtils {
long deviceId = startStop.getDeviceId();
stop.setDeviceId(deviceId);
- stop.setDeviceName(Context.getIdentityManager().getDeviceById(deviceId).getName());
+ stop.setDeviceName(Context.getIdentityManager().getById(deviceId).getName());
stop.setPositionId(startStop.getId());
stop.setLatitude(startStop.getLatitude());
@@ -213,115 +244,79 @@ public final class ReportUtils {
}
- private static boolean isMoving(ArrayList<Position> positions, int index,
- TripsConfig tripsConfig, double speedThreshold) {
- if (tripsConfig.getMinimalNoDataDuration() > 0 && index < positions.size() - 1
- && positions.get(index + 1).getFixTime().getTime() - positions.get(index).getFixTime().getTime()
- >= tripsConfig.getMinimalNoDataDuration()) {
- return false;
+ private static <T extends BaseReport> T calculateTripOrStop(ArrayList<Position> positions, int startIndex,
+ int endIndex, boolean ignoreOdometer, Class<T> reportClass) {
+ if (reportClass.equals(TripReport.class)) {
+ return (T) calculateTrip(positions, startIndex, endIndex, ignoreOdometer);
+ } else {
+ return (T) calculateStop(positions, startIndex, endIndex);
+ }
+ }
+
+ private static boolean isMoving(ArrayList<Position> positions, int index, TripsConfig tripsConfig) {
+ if (tripsConfig.getMinimalNoDataDuration() > 0) {
+ boolean beforeGap = index < positions.size() - 1
+ && positions.get(index + 1).getFixTime().getTime() - positions.get(index).getFixTime().getTime()
+ >= tripsConfig.getMinimalNoDataDuration();
+ boolean afterGap = index > 0
+ && positions.get(index).getFixTime().getTime() - positions.get(index - 1).getFixTime().getTime()
+ >= tripsConfig.getMinimalNoDataDuration();
+ if (beforeGap || afterGap) {
+ return false;
+ }
}
if (positions.get(index).getAttributes().containsKey(Position.KEY_MOTION)
&& positions.get(index).getAttributes().get(Position.KEY_MOTION) instanceof Boolean) {
return positions.get(index).getBoolean(Position.KEY_MOTION);
} else {
- return positions.get(index).getSpeed() > speedThreshold;
+ return positions.get(index).getSpeed() > tripsConfig.getSpeedThreshold();
}
}
- public static Collection<BaseReport> detectTripsAndStops(TripsConfig tripsConfig, boolean ignoreOdometer,
- double speedThreshold, Collection<Position> positionCollection, boolean trips) {
-
- Collection<BaseReport> result = new ArrayList<>();
+ public static <T extends BaseReport> Collection<T> detectTripsAndStops(Collection<Position> positionCollection,
+ TripsConfig tripsConfig, boolean ignoreOdometer, Class<T> reportClass) {
+ Collection<T> result = new ArrayList<>();
ArrayList<Position> positions = new ArrayList<>(positionCollection);
if (positions != null && !positions.isEmpty()) {
- int previousStartParkingIndex = 0;
- int startParkingIndex = -1;
- int previousEndParkingIndex = 0;
- int endParkingIndex = 0;
-
- boolean isMoving = false;
- boolean isLast = false;
- boolean skipped = false;
- boolean tripFiltered = false;
-
+ boolean trips = reportClass.equals(TripReport.class);
+ MotionEventHandler motionHandler = new MotionEventHandler(tripsConfig);
+ DeviceState deviceState = new DeviceState();
+ deviceState.setMotionState(isMoving(positions, 0, tripsConfig));
+ int startEventIndex = trips == deviceState.getMotionState() ? 0 : -1;
+ int startNoEventIndex = -1;
for (int i = 0; i < positions.size(); i++) {
- isMoving = isMoving(positions, i, tripsConfig, speedThreshold);
- isLast = i == positions.size() - 1;
-
- if ((isMoving || isLast) && startParkingIndex != -1) {
- if (!skipped || previousEndParkingIndex == 0) {
- previousEndParkingIndex = endParkingIndex;
- }
- endParkingIndex = i;
+ Map<Event, Position> event = motionHandler.updateMotionState(deviceState, positions.get(i),
+ isMoving(positions, i, tripsConfig));
+ if (startEventIndex == -1
+ && (trips != deviceState.getMotionState() && deviceState.getMotionPosition() != null
+ || trips == deviceState.getMotionState() && event != null)) {
+ startEventIndex = i;
+ startNoEventIndex = -1;
+ } else if (trips != deviceState.getMotionState() && startEventIndex != -1
+ && deviceState.getMotionPosition() == null && event == null) {
+ startEventIndex = -1;
}
- if (!isMoving && startParkingIndex == -1) {
- if (tripsConfig.getGreedyParking()) {
- long tripDuration = positions.get(i).getFixTime().getTime()
- - positions.get(endParkingIndex).getFixTime().getTime();
- double tripDistance = ReportUtils.calculateDistance(positions.get(endParkingIndex),
- positions.get(i), false);
- tripFiltered = tripDuration < tripsConfig.getMinimalTripDuration()
- && tripDistance < tripsConfig.getMinimalTripDistance();
- if (tripFiltered) {
- startParkingIndex = previousStartParkingIndex;
- endParkingIndex = previousEndParkingIndex;
- tripFiltered = false;
- } else {
- previousStartParkingIndex = i;
- startParkingIndex = i;
- }
- } else {
- long tripDuration = positions.get(i).getFixTime().getTime()
- - positions.get(previousEndParkingIndex).getFixTime().getTime();
- double tripDistance = ReportUtils.calculateDistance(positions.get(previousEndParkingIndex),
- positions.get(i), false);
- tripFiltered = tripDuration < tripsConfig.getMinimalTripDuration()
- && tripDistance < tripsConfig.getMinimalTripDistance();
- startParkingIndex = i;
- }
+ if (startNoEventIndex == -1
+ && (trips == deviceState.getMotionState() && deviceState.getMotionPosition() != null
+ || trips != deviceState.getMotionState() && event != null)) {
+ startNoEventIndex = i;
+ } else if (startNoEventIndex != -1 && deviceState.getMotionPosition() == null && event == null) {
+ startNoEventIndex = -1;
}
- if (startParkingIndex != -1 && (endParkingIndex > startParkingIndex || isLast)) {
- long parkingDuration = positions.get(endParkingIndex).getFixTime().getTime()
- - positions.get(startParkingIndex).getFixTime().getTime();
- if ((parkingDuration >= tripsConfig.getMinimalParkingDuration() || isLast)
- && previousEndParkingIndex < startParkingIndex) {
- if (!tripFiltered) {
- if (trips) {
- result.add(calculateTrip(
- positions, previousEndParkingIndex, startParkingIndex, ignoreOdometer));
- } else {
- if (result.isEmpty() && previousEndParkingIndex > previousStartParkingIndex) {
- long previousParkingDuration = positions.get(previousEndParkingIndex)
- .getFixTime().getTime() - positions.get(previousStartParkingIndex)
- .getFixTime().getTime();
- if (previousParkingDuration >= tripsConfig.getMinimalParkingDuration()) {
- result.add(calculateStop(positions, previousStartParkingIndex,
- previousEndParkingIndex));
- }
- }
- result.add(calculateStop(positions, startParkingIndex, isLast ? i : endParkingIndex));
- }
- }
- previousEndParkingIndex = endParkingIndex;
- skipped = false;
- } else {
- skipped = true;
- }
- startParkingIndex = -1;
+ if (startEventIndex != -1 && startNoEventIndex != -1 && event != null
+ && trips != deviceState.getMotionState()) {
+ result.add(calculateTripOrStop(positions, startEventIndex, startNoEventIndex,
+ ignoreOdometer, reportClass));
+ startEventIndex = -1;
}
}
- if (result.isEmpty() && !trips) {
- int end = isMoving && !tripsConfig.getGreedyParking()
- ? Math.max(endParkingIndex, previousEndParkingIndex) : positions.size() - 1;
- long parkingDuration = positions.get(end).getFixTime().getTime()
- - positions.get(previousStartParkingIndex).getFixTime().getTime();
- if (parkingDuration >= tripsConfig.getMinimalParkingDuration()) {
- result.add(calculateStop(positions, previousStartParkingIndex, end));
- }
+ if (startEventIndex != -1 && (startNoEventIndex != -1 || !trips)) {
+ result.add(calculateTripOrStop(positions, startEventIndex,
+ startNoEventIndex != -1 ? startNoEventIndex : positions.size() - 1,
+ ignoreOdometer, reportClass));
}
}
-
return result;
}
}
diff --git a/src/org/traccar/reports/Route.java b/src/org/traccar/reports/Route.java
index aa6b7105b..6adb00aae 100644
--- a/src/org/traccar/reports/Route.java
+++ b/src/org/traccar/reports/Route.java
@@ -39,6 +39,7 @@ public final class Route {
public static Collection<Position> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
Date from, Date to) throws SQLException {
+ ReportUtils.checkPeriodLimit(from, to);
ArrayList<Position> result = new ArrayList<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
Context.getPermissionsManager().checkDevice(userId, deviceId);
@@ -50,6 +51,7 @@ public final class Route {
public static void getExcel(OutputStream outputStream,
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
Date from, Date to) throws SQLException, IOException {
+ ReportUtils.checkPeriodLimit(from, to);
ArrayList<DeviceReport> devicesRoutes = new ArrayList<>();
ArrayList<String> sheetNames = new ArrayList<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
@@ -57,11 +59,11 @@ public final class Route {
Collection<Position> positions = Context.getDataManager()
.getPositions(deviceId, from, to);
DeviceReport deviceRoutes = new DeviceReport();
- Device device = Context.getIdentityManager().getDeviceById(deviceId);
+ Device device = Context.getIdentityManager().getById(deviceId);
deviceRoutes.setDeviceName(device.getName());
sheetNames.add(WorkbookUtil.createSafeSheetName(deviceRoutes.getDeviceName()));
if (device.getGroupId() != 0) {
- Group group = Context.getDeviceManager().getGroupById(device.getGroupId());
+ Group group = Context.getGroupsManager().getById(device.getGroupId());
if (group != null) {
deviceRoutes.setGroupName(group.getName());
}
diff --git a/src/org/traccar/reports/Stops.java b/src/org/traccar/reports/Stops.java
index 64e589be1..14b3a2437 100644
--- a/src/org/traccar/reports/Stops.java
+++ b/src/org/traccar/reports/Stops.java
@@ -30,10 +30,8 @@ import org.apache.poi.ss.util.WorkbookUtil;
import org.traccar.Context;
import org.traccar.model.Device;
import org.traccar.model.Group;
-import org.traccar.reports.model.BaseReport;
import org.traccar.reports.model.DeviceReport;
import org.traccar.reports.model.StopReport;
-import org.traccar.reports.model.TripsConfig;
public final class Stops {
@@ -41,22 +39,20 @@ public final class Stops {
}
private static Collection<StopReport> detectStops(long deviceId, Date from, Date to) throws SQLException {
- double speedThreshold = Context.getConfig().getDouble("event.motion.speedThreshold", 0.01);
-
- TripsConfig tripsConfig = ReportUtils.initTripsConfig();
-
boolean ignoreOdometer = Context.getDeviceManager()
.lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
- Collection<? extends BaseReport> result = ReportUtils.detectTripsAndStops(tripsConfig,
- ignoreOdometer, speedThreshold,
- Context.getDataManager().getPositions(deviceId, from, to), false);
+ Collection<StopReport> result = ReportUtils.detectTripsAndStops(
+ Context.getDataManager().getPositions(deviceId, from, to),
+ Context.getTripsConfig(), ignoreOdometer, StopReport.class);
- return (Collection<StopReport>) result;
+ return result;
}
- public static Collection<StopReport> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ public static Collection<StopReport> getObjects(
+ long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
Date from, Date to) throws SQLException {
+ ReportUtils.checkPeriodLimit(from, to);
ArrayList<StopReport> result = new ArrayList<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
Context.getPermissionsManager().checkDevice(userId, deviceId);
@@ -65,20 +61,21 @@ public final class Stops {
return result;
}
- public static void getExcel(OutputStream outputStream,
- long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
+ public static void getExcel(
+ OutputStream outputStream, long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
Date from, Date to) throws SQLException, IOException {
+ ReportUtils.checkPeriodLimit(from, to);
ArrayList<DeviceReport> devicesStops = new ArrayList<>();
ArrayList<String> sheetNames = new ArrayList<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
Context.getPermissionsManager().checkDevice(userId, deviceId);
Collection<StopReport> stops = detectStops(deviceId, from, to);
DeviceReport deviceStops = new DeviceReport();
- Device device = Context.getIdentityManager().getDeviceById(deviceId);
+ Device device = Context.getIdentityManager().getById(deviceId);
deviceStops.setDeviceName(device.getName());
sheetNames.add(WorkbookUtil.createSafeSheetName(deviceStops.getDeviceName()));
if (device.getGroupId() != 0) {
- Group group = Context.getDeviceManager().getGroupById(device.getGroupId());
+ Group group = Context.getGroupsManager().getById(device.getGroupId());
if (group != null) {
deviceStops.setGroupName(group.getName());
}
diff --git a/src/org/traccar/reports/Summary.java b/src/org/traccar/reports/Summary.java
index 5aaf33fae..366e40421 100644
--- a/src/org/traccar/reports/Summary.java
+++ b/src/org/traccar/reports/Summary.java
@@ -38,7 +38,7 @@ public final class Summary {
private static SummaryReport calculateSummaryResult(long deviceId, Date from, Date to) throws SQLException {
SummaryReport result = new SummaryReport();
result.setDeviceId(deviceId);
- result.setDeviceName(Context.getIdentityManager().getDeviceById(deviceId).getName());
+ result.setDeviceName(Context.getIdentityManager().getById(deviceId).getName());
Collection<Position> positions = Context.getDataManager().getPositions(deviceId, from, to);
if (positions != null && !positions.isEmpty()) {
Position firstPosition = null;
@@ -68,6 +68,7 @@ public final class Summary {
public static Collection<SummaryReport> getObjects(long userId, Collection<Long> deviceIds,
Collection<Long> groupIds, Date from, Date to) throws SQLException {
+ ReportUtils.checkPeriodLimit(from, to);
ArrayList<SummaryReport> result = new ArrayList<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
Context.getPermissionsManager().checkDevice(userId, deviceId);
@@ -79,6 +80,7 @@ public final class Summary {
public static void getExcel(OutputStream outputStream,
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
Date from, Date to) throws SQLException, IOException {
+ ReportUtils.checkPeriodLimit(from, to);
Collection<SummaryReport> summaries = getObjects(userId, deviceIds, groupIds, from, to);
String templatePath = Context.getConfig().getString("report.templatesPath",
"templates/export/");
diff --git a/src/org/traccar/reports/Trips.java b/src/org/traccar/reports/Trips.java
index 5c26bea54..696defa94 100644
--- a/src/org/traccar/reports/Trips.java
+++ b/src/org/traccar/reports/Trips.java
@@ -29,10 +29,8 @@ import org.apache.poi.ss.util.WorkbookUtil;
import org.traccar.Context;
import org.traccar.model.Device;
import org.traccar.model.Group;
-import org.traccar.reports.model.BaseReport;
import org.traccar.reports.model.DeviceReport;
import org.traccar.reports.model.TripReport;
-import org.traccar.reports.model.TripsConfig;
public final class Trips {
@@ -40,22 +38,19 @@ public final class Trips {
}
private static Collection<TripReport> detectTrips(long deviceId, Date from, Date to) throws SQLException {
- double speedThreshold = Context.getConfig().getDouble("event.motion.speedThreshold", 0.01);
-
- TripsConfig tripsConfig = ReportUtils.initTripsConfig();
-
boolean ignoreOdometer = Context.getDeviceManager()
.lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
- Collection<? extends BaseReport> result = ReportUtils.detectTripsAndStops(tripsConfig,
- ignoreOdometer, speedThreshold,
- Context.getDataManager().getPositions(deviceId, from, to), true);
+ Collection<TripReport> result = ReportUtils.detectTripsAndStops(
+ Context.getDataManager().getPositions(deviceId, from, to),
+ Context.getTripsConfig(), ignoreOdometer, TripReport.class);
- return (Collection<TripReport>) result;
+ return result;
}
public static Collection<TripReport> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
Date from, Date to) throws SQLException {
+ ReportUtils.checkPeriodLimit(from, to);
ArrayList<TripReport> result = new ArrayList<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
Context.getPermissionsManager().checkDevice(userId, deviceId);
@@ -67,17 +62,18 @@ public final class Trips {
public static void getExcel(OutputStream outputStream,
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
Date from, Date to) throws SQLException, IOException {
+ ReportUtils.checkPeriodLimit(from, to);
ArrayList<DeviceReport> devicesTrips = new ArrayList<>();
ArrayList<String> sheetNames = new ArrayList<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
Context.getPermissionsManager().checkDevice(userId, deviceId);
Collection<TripReport> trips = detectTrips(deviceId, from, to);
DeviceReport deviceTrips = new DeviceReport();
- Device device = Context.getIdentityManager().getDeviceById(deviceId);
+ Device device = Context.getIdentityManager().getById(deviceId);
deviceTrips.setDeviceName(device.getName());
sheetNames.add(WorkbookUtil.createSafeSheetName(deviceTrips.getDeviceName()));
if (device.getGroupId() != 0) {
- Group group = Context.getDeviceManager().getGroupById(device.getGroupId());
+ Group group = Context.getGroupsManager().getById(device.getGroupId());
if (group != null) {
deviceTrips.setGroupName(group.getName());
}
diff --git a/src/org/traccar/reports/model/TripReport.java b/src/org/traccar/reports/model/TripReport.java
index efe556f79..42a4240b7 100644
--- a/src/org/traccar/reports/model/TripReport.java
+++ b/src/org/traccar/reports/model/TripReport.java
@@ -145,4 +145,24 @@ public class TripReport extends BaseReport {
public void setDuration(long duration) {
this.duration = duration;
}
+
+ private String driverUniqueId;
+
+ public String getDriverUniqueId() {
+ return driverUniqueId;
+ }
+
+ public void setDriverUniqueId(String driverUniqueId) {
+ this.driverUniqueId = driverUniqueId;
+ }
+
+ private String driverName;
+
+ public String getDriverName() {
+ return driverName;
+ }
+
+ public void setDriverName(String driverName) {
+ this.driverName = driverName;
+ }
}
diff --git a/src/org/traccar/reports/model/TripsConfig.java b/src/org/traccar/reports/model/TripsConfig.java
index 7067781d7..0f0c615d3 100644
--- a/src/org/traccar/reports/model/TripsConfig.java
+++ b/src/org/traccar/reports/model/TripsConfig.java
@@ -21,13 +21,15 @@ public class TripsConfig {
public TripsConfig() {
}
- public TripsConfig(double minimalTripDistance, long minimalTripDuration,
- long minimalParkingDuration, boolean greedyParking, long minimalNoDataDuration) {
+ public TripsConfig(double minimalTripDistance, long minimalTripDuration, long minimalParkingDuration,
+ long minimalNoDataDuration, boolean useIgnition, boolean processInvalidPositions, double speedThreshold) {
this.minimalTripDistance = minimalTripDistance;
this.minimalTripDuration = minimalTripDuration;
this.minimalParkingDuration = minimalParkingDuration;
- this.greedyParking = greedyParking;
this.minimalNoDataDuration = minimalNoDataDuration;
+ this.useIgnition = useIgnition;
+ this.processInvalidPositions = processInvalidPositions;
+ this.speedThreshold = speedThreshold;
}
private double minimalTripDistance;
@@ -60,16 +62,6 @@ public class TripsConfig {
this.minimalParkingDuration = minimalParkingDuration;
}
- private boolean greedyParking;
-
- public boolean getGreedyParking() {
- return greedyParking;
- }
-
- public void setGreedyParking(boolean greedyParking) {
- this.greedyParking = greedyParking;
- }
-
private long minimalNoDataDuration;
public long getMinimalNoDataDuration() {
@@ -80,4 +72,34 @@ public class TripsConfig {
this.minimalNoDataDuration = minimalNoDataDuration;
}
+ private boolean useIgnition;
+
+ public boolean getUseIgnition() {
+ return useIgnition;
+ }
+
+ public void setUseIgnition(boolean useIgnition) {
+ this.useIgnition = useIgnition;
+ }
+
+ private boolean processInvalidPositions;
+
+ public boolean getProcessInvalidPositions() {
+ return processInvalidPositions;
+ }
+
+ public void setProcessInvalidPositions(boolean processInvalidPositions) {
+ this.processInvalidPositions = processInvalidPositions;
+ }
+
+ private double speedThreshold;
+
+ public double getSpeedThreshold() {
+ return speedThreshold;
+ }
+
+ public void setSpeedThreshold(double speedThreshold) {
+ this.speedThreshold = speedThreshold;
+ }
+
}
diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java
index 83ead7ad8..e145ff554 100644
--- a/src/org/traccar/web/WebServer.java
+++ b/src/org/traccar/web/WebServer.java
@@ -15,7 +15,10 @@
*/
package org.traccar.web;
+import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.proxy.AsyncProxyServlet;
+import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SessionManager;
import org.eclipse.jetty.server.handler.ErrorHandler;
@@ -29,6 +32,7 @@ import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.traccar.Config;
+import org.traccar.Context;
import org.traccar.api.AsyncSocketServlet;
import org.traccar.api.CorsResponseFilter;
import org.traccar.api.ObjectMapperProvider;
@@ -38,7 +42,9 @@ import org.traccar.api.resource.ServerResource;
import org.traccar.helper.Log;
import javax.naming.InitialContext;
+import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.Writer;
@@ -86,6 +92,7 @@ public class WebServer {
initWebApp();
break;
}
+ initClientProxy();
server.setHandler(handlers);
server.addBean(new ErrorHandler() {
@@ -98,6 +105,26 @@ public class WebServer {
}, false);
}
+ private void initClientProxy() {
+ int port = Context.getConfig().getInteger("osmand.port");
+ if (port != 0) {
+ ServletContextHandler servletHandler = new ServletContextHandler() {
+ @Override
+ public void doScope(
+ String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
+ throws IOException, ServletException {
+ if (target.equals("/") && request.getMethod().equals(HttpMethod.POST.asString())) {
+ super.doScope(target, baseRequest, request, response);
+ }
+ }
+ };
+ ServletHolder servletHolder = new ServletHolder(new AsyncProxyServlet.Transparent());
+ servletHolder.setInitParameter("proxyTo", "http://localhost:" + port);
+ servletHandler.addServlet(servletHolder, "/");
+ handlers.addHandler(servletHandler);
+ }
+ }
+
private void initWebApp() {
ResourceHandler resourceHandler = new ResourceHandler();
resourceHandler.setResourceBase(config.getString("web.path"));
@@ -105,7 +132,10 @@ public class WebServer {
resourceHandler.setWelcomeFiles(new String[] {"debug.html", "index.html"});
resourceHandler.setMinMemoryMappedContentLength(-1); // avoid locking files on Windows
} else {
- resourceHandler.setCacheControl("max-age=3600,public");
+ String cache = config.getString("web.cacheControl");
+ if (cache != null && !cache.isEmpty()) {
+ resourceHandler.setCacheControl(cache);
+ }
resourceHandler.setWelcomeFiles(new String[] {"release.html", "index.html"});
}
handlers.addHandler(resourceHandler);