aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/org/traccar/BasePipelineFactory.java4
-rw-r--r--src/main/java/org/traccar/BaseProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/Context.java8
-rw-r--r--src/main/java/org/traccar/ExtendedObjectDecoder.java9
-rw-r--r--src/main/java/org/traccar/Main.java2
-rw-r--r--src/main/java/org/traccar/MainModule.java27
-rw-r--r--src/main/java/org/traccar/WebDataHandler.java4
-rw-r--r--src/main/java/org/traccar/WindowsService.java24
-rw-r--r--src/main/java/org/traccar/WrapperInboundHandler.java2
-rw-r--r--src/main/java/org/traccar/WrapperOutboundHandler.java2
-rw-r--r--src/main/java/org/traccar/api/AsyncSocket.java2
-rw-r--r--src/main/java/org/traccar/api/AsyncSocketServlet.java18
-rw-r--r--src/main/java/org/traccar/api/BaseObjectResource.java2
-rw-r--r--src/main/java/org/traccar/api/DateParameterConverterProvider.java52
-rw-r--r--src/main/java/org/traccar/api/HealthCheckService.java3
-rw-r--r--src/main/java/org/traccar/api/UserPrincipal.java4
-rw-r--r--src/main/java/org/traccar/api/resource/DeviceResource.java2
-rw-r--r--src/main/java/org/traccar/api/resource/PositionResource.java17
-rw-r--r--src/main/java/org/traccar/api/resource/ReportResource.java67
-rw-r--r--src/main/java/org/traccar/api/resource/ServerResource.java10
-rw-r--r--src/main/java/org/traccar/api/resource/StatisticsResource.java8
-rw-r--r--src/main/java/org/traccar/api/resource/UserResource.java2
-rw-r--r--src/main/java/org/traccar/config/Keys.java39
-rw-r--r--src/main/java/org/traccar/database/BaseObjectManager.java2
-rw-r--r--src/main/java/org/traccar/database/CommandsManager.java2
-rw-r--r--src/main/java/org/traccar/database/ConnectionManager.java10
-rw-r--r--src/main/java/org/traccar/database/DataManager.java2
-rw-r--r--src/main/java/org/traccar/database/DeviceManager.java4
-rw-r--r--src/main/java/org/traccar/database/PermissionsManager.java2
-rw-r--r--src/main/java/org/traccar/database/QueryBuilder.java122
-rw-r--r--src/main/java/org/traccar/geofence/GeofenceCircle.java2
-rw-r--r--src/main/java/org/traccar/handler/SpeedLimitHandler.java60
-rw-r--r--src/main/java/org/traccar/handler/TimeHandler.java6
-rw-r--r--src/main/java/org/traccar/handler/events/OverspeedEventHandler.java7
-rw-r--r--src/main/java/org/traccar/helper/BitBuffer.java2
-rw-r--r--src/main/java/org/traccar/helper/Checksum.java21
-rw-r--r--src/main/java/org/traccar/helper/DateBuilder.java2
-rw-r--r--src/main/java/org/traccar/helper/LogAction.java18
-rw-r--r--src/main/java/org/traccar/helper/ObdDecoder.java6
-rw-r--r--src/main/java/org/traccar/helper/Parser.java2
-rw-r--r--src/main/java/org/traccar/model/Command.java3
-rw-r--r--src/main/java/org/traccar/model/Event.java3
-rw-r--r--src/main/java/org/traccar/model/Position.java1
-rw-r--r--src/main/java/org/traccar/model/Server.java10
-rw-r--r--src/main/java/org/traccar/model/Typed.java19
-rw-r--r--src/main/java/org/traccar/notificators/Notificator.java12
-rw-r--r--src/main/java/org/traccar/protocol/AtrackFrameDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java61
-rw-r--r--src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java28
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/FutureWayFrameDecoder.java47
-rw-r--r--src/main/java/org/traccar/protocol/FutureWayProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java134
-rw-r--r--src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java103
-rw-r--r--src/main/java/org/traccar/protocol/GlobalSatProtocol.java8
-rw-r--r--src/main/java/org/traccar/protocol/GlobalSatProtocolEncoder.java58
-rw-r--r--src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java15
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java164
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java46
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java60
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/L100ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java27
-rw-r--r--src/main/java/org/traccar/protocol/MictrackProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java47
-rw-r--r--src/main/java/org/traccar/protocol/MoovboxProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java106
-rw-r--r--src/main/java/org/traccar/protocol/NetProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/NetProtocolDecoder.java92
-rw-r--r--src/main/java/org/traccar/protocol/NiotProtocolDecoder.java58
-rw-r--r--src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/PolteProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/PolteProtocolDecoder.java84
-rw-r--r--src/main/java/org/traccar/protocol/PstFrameEncoder.java39
-rw-r--r--src/main/java/org/traccar/protocol/PstProtocol.java9
-rw-r--r--src/main/java/org/traccar/protocol/PstProtocolDecoder.java21
-rw-r--r--src/main/java/org/traccar/protocol/PstProtocolEncoder.java62
-rw-r--r--src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/RadarProtocolDecoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/SpotProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java55
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java65
-rw-r--r--src/main/java/org/traccar/protocol/T55ProtocolDecoder.java48
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java59
-rw-r--r--src/main/java/org/traccar/protocol/V680ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/reports/ReportUtils.java22
-rw-r--r--src/main/java/org/traccar/reports/Summary.java58
-rw-r--r--src/main/java/org/traccar/reports/model/BaseReport.java22
-rw-r--r--src/main/java/org/traccar/reports/model/StopReport.java28
-rw-r--r--src/main/java/org/traccar/reports/model/SummaryReport.java6
-rw-r--r--src/main/java/org/traccar/reports/model/TripReport.java22
-rw-r--r--src/main/java/org/traccar/schedule/ScheduleManager.java42
-rw-r--r--src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java79
-rw-r--r--src/main/java/org/traccar/sms/smpp/SmppClient.java40
-rw-r--r--src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java74
-rw-r--r--src/main/java/org/traccar/speedlimit/SpeedLimitException.java24
-rw-r--r--src/main/java/org/traccar/speedlimit/SpeedLimitProvider.java30
-rw-r--r--src/main/java/org/traccar/web/CsvBuilder.java19
-rw-r--r--src/main/java/org/traccar/web/WebServer.java42
-rw-r--r--src/main/proto/StarLinkMessage.proto330
111 files changed, 2592 insertions, 618 deletions
diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java
index 5a5850a86..ffce45342 100644
--- a/src/main/java/org/traccar/BasePipelineFactory.java
+++ b/src/main/java/org/traccar/BasePipelineFactory.java
@@ -26,6 +26,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.config.Keys;
import org.traccar.handler.DefaultDataHandler;
+import org.traccar.handler.SpeedLimitHandler;
import org.traccar.handler.TimeHandler;
import org.traccar.handler.events.AlertEventHandler;
import org.traccar.handler.events.CommandResultEventHandler;
@@ -58,7 +59,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
private final TrackerServer server;
private final String protocol;
- private boolean eventsEnabled;
+ private final boolean eventsEnabled;
private int timeout;
public BasePipelineFactory(TrackerServer server, String protocol) {
@@ -133,6 +134,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
pipeline,
FilterHandler.class,
GeocoderHandler.class,
+ SpeedLimitHandler.class,
MotionHandler.class,
CopyAttributesHandler.class,
EngineHoursHandler.class,
diff --git a/src/main/java/org/traccar/BaseProtocolDecoder.java b/src/main/java/org/traccar/BaseProtocolDecoder.java
index 99f3bc627..50e80a457 100644
--- a/src/main/java/org/traccar/BaseProtocolDecoder.java
+++ b/src/main/java/org/traccar/BaseProtocolDecoder.java
@@ -104,7 +104,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
}
private DeviceSession channelDeviceSession; // connection-based protocols
- private Map<SocketAddress, DeviceSession> addressDeviceSessions = new HashMap<>(); // connectionless protocols
+ private final Map<SocketAddress, DeviceSession> addressDeviceSessions = new HashMap<>(); // connectionless protocols
private long findDeviceId(SocketAddress remoteAddress, String... uniqueIds) {
if (uniqueIds.length > 0) {
diff --git a/src/main/java/org/traccar/Context.java b/src/main/java/org/traccar/Context.java
index 9c20db9e4..af7e03837 100644
--- a/src/main/java/org/traccar/Context.java
+++ b/src/main/java/org/traccar/Context.java
@@ -40,6 +40,7 @@ import org.traccar.database.MaintenancesManager;
import org.traccar.database.MediaManager;
import org.traccar.database.NotificationManager;
import org.traccar.database.PermissionsManager;
+import org.traccar.schedule.ScheduleManager;
import org.traccar.database.UsersManager;
import org.traccar.geocoder.Geocoder;
import org.traccar.helper.Log;
@@ -165,6 +166,12 @@ public final class Context {
return serverManager;
}
+ private static ScheduleManager scheduleManager;
+
+ public static ScheduleManager getScheduleManager() {
+ return scheduleManager;
+ }
+
private static GeofenceManager geofenceManager;
public static GeofenceManager getGeofenceManager() {
@@ -332,6 +339,7 @@ public final class Context {
}
serverManager = new ServerManager();
+ scheduleManager = new ScheduleManager();
if (config.getBoolean("event.forward.enable")) {
eventForwarder = new JsonTypeEventForwarder();
diff --git a/src/main/java/org/traccar/ExtendedObjectDecoder.java b/src/main/java/org/traccar/ExtendedObjectDecoder.java
index 681924e87..46720da52 100644
--- a/src/main/java/org/traccar/ExtendedObjectDecoder.java
+++ b/src/main/java/org/traccar/ExtendedObjectDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 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 io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
+import org.traccar.config.Keys;
import org.traccar.helper.DataConverter;
import org.traccar.model.Position;
@@ -31,14 +32,14 @@ import java.util.Collection;
public abstract class ExtendedObjectDecoder extends ChannelInboundHandlerAdapter {
private void saveOriginal(Object decodedMessage, Object originalMessage) {
- if (Context.getConfig().getBoolean("database.saveOriginal") && decodedMessage instanceof Position) {
+ if (Context.getConfig().getBoolean(Keys.DATABASE_SAVE_ORIGINAL) && decodedMessage instanceof Position) {
Position position = (Position) decodedMessage;
if (originalMessage instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) originalMessage;
- position.set(Position.KEY_ORIGINAL, ByteBufUtil.hexDump(buf));
+ position.set(Position.KEY_ORIGINAL, ByteBufUtil.hexDump(buf, 0, buf.writerIndex()));
} else if (originalMessage instanceof String) {
position.set(Position.KEY_ORIGINAL, DataConverter.printHex(
- ((String) originalMessage).getBytes(StandardCharsets.US_ASCII)));
+ ((String) originalMessage).getBytes(StandardCharsets.US_ASCII)));
}
}
}
diff --git a/src/main/java/org/traccar/Main.java b/src/main/java/org/traccar/Main.java
index a0d93dbc8..59afab3a5 100644
--- a/src/main/java/org/traccar/Main.java
+++ b/src/main/java/org/traccar/Main.java
@@ -144,6 +144,7 @@ public final class Main {
if (Context.getWebServer() != null) {
Context.getWebServer().start();
}
+ Context.getScheduleManager().start();
scheduleHealthCheck();
scheduleDatabaseCleanup();
@@ -153,6 +154,7 @@ public final class Main {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
LOGGER.info("Shutting down server...");
+ Context.getScheduleManager().stop();
if (Context.getWebServer() != null) {
Context.getWebServer().stop();
}
diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java
index de6ec5b87..e751cb453 100644
--- a/src/main/java/org/traccar/MainModule.java
+++ b/src/main/java/org/traccar/MainModule.java
@@ -61,6 +61,7 @@ import org.traccar.handler.GeolocationHandler;
import org.traccar.handler.HemisphereHandler;
import org.traccar.handler.MotionHandler;
import org.traccar.handler.RemoteAddressHandler;
+import org.traccar.handler.SpeedLimitHandler;
import org.traccar.handler.TimeHandler;
import org.traccar.handler.events.AlertEventHandler;
import org.traccar.handler.events.CommandResultEventHandler;
@@ -76,6 +77,8 @@ import org.traccar.reports.model.TripsConfig;
import javax.annotation.Nullable;
import javax.ws.rs.client.Client;
import io.netty.util.Timer;
+import org.traccar.speedlimit.OverpassSpeedLimitProvider;
+import org.traccar.speedlimit.SpeedLimitProvider;
public class MainModule extends AbstractModule {
@@ -211,6 +214,21 @@ public class MainModule extends AbstractModule {
@Singleton
@Provides
+ public static SpeedLimitProvider provideSpeedLimitProvider(Config config) {
+ if (config.getBoolean(Keys.SPEED_LIMIT_ENABLE)) {
+ String type = config.getString(Keys.SPEED_LIMIT_TYPE, "overpass");
+ String url = config.getString(Keys.SPEED_LIMIT_URL);
+ switch (type) {
+ case "overpass":
+ default:
+ return new OverpassSpeedLimitProvider(url);
+ }
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
public static DistanceHandler provideDistanceHandler(Config config, IdentityManager identityManager) {
return new DistanceHandler(config, identityManager);
}
@@ -275,6 +293,15 @@ public class MainModule extends AbstractModule {
@Singleton
@Provides
+ public static SpeedLimitHandler provideSpeedLimitHandler(@Nullable SpeedLimitProvider speedLimitProvider) {
+ if (speedLimitProvider != null) {
+ return new SpeedLimitHandler(speedLimitProvider);
+ }
+ return null;
+ }
+
+ @Singleton
+ @Provides
public static MotionHandler provideMotionHandler(TripsConfig tripsConfig) {
return new MotionHandler(tripsConfig.getSpeedThreshold());
}
diff --git a/src/main/java/org/traccar/WebDataHandler.java b/src/main/java/org/traccar/WebDataHandler.java
index 3be575b48..a7ea0b2a5 100644
--- a/src/main/java/org/traccar/WebDataHandler.java
+++ b/src/main/java/org/traccar/WebDataHandler.java
@@ -74,7 +74,7 @@ public class WebDataHandler extends BaseDataHandler {
private final int retryCount;
private final int retryLimit;
- private AtomicInteger deliveryPending;
+ private final AtomicInteger deliveryPending;
@Inject
public WebDataHandler(
@@ -117,7 +117,7 @@ public class WebDataHandler extends BaseDataHandler {
f.format("%1$td%1$tm%1$ty,,", calendar);
}
- s.append(Checksum.nmea(s.toString()));
+ s.append(Checksum.nmea(s.substring(1)));
return s.toString();
}
diff --git a/src/main/java/org/traccar/WindowsService.java b/src/main/java/org/traccar/WindowsService.java
index 4a8955608..831cd3ebc 100644
--- a/src/main/java/org/traccar/WindowsService.java
+++ b/src/main/java/org/traccar/WindowsService.java
@@ -38,16 +38,14 @@ public abstract class WindowsService {
private final Object waitObject = new Object();
- private String serviceName;
- private ServiceMain serviceMain;
- private ServiceControl serviceControl;
+ private final String serviceName;
private SERVICE_STATUS_HANDLE serviceStatusHandle;
public WindowsService(String serviceName) {
this.serviceName = serviceName;
}
- public boolean install(
+ public void install(
String displayName, String description, String[] dependencies,
String account, String password, String config) throws URISyntaxException {
@@ -60,7 +58,6 @@ public abstract class WindowsService {
+ " -jar \"" + jar.getAbsolutePath() + "\""
+ " --service \"" + config + "\"";
- boolean success = false;
StringBuilder dep = new StringBuilder();
if (dependencies != null) {
@@ -84,29 +81,25 @@ public abstract class WindowsService {
null, null, dep.toString(), account, password);
if (service != null) {
- success = ADVAPI_32.ChangeServiceConfig2(service, Winsvc.SERVICE_CONFIG_DESCRIPTION, desc);
+ ADVAPI_32.ChangeServiceConfig2(service, Winsvc.SERVICE_CONFIG_DESCRIPTION, desc);
ADVAPI_32.CloseServiceHandle(service);
}
ADVAPI_32.CloseServiceHandle(serviceManager);
}
- return success;
}
- public boolean uninstall() {
- boolean success = false;
-
+ public void uninstall() {
SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS);
if (serviceManager != null) {
SC_HANDLE service = ADVAPI_32.OpenService(serviceManager, serviceName, Winsvc.SERVICE_ALL_ACCESS);
if (service != null) {
- success = ADVAPI_32.DeleteService(service);
+ ADVAPI_32.DeleteService(service);
ADVAPI_32.CloseServiceHandle(service);
}
ADVAPI_32.CloseServiceHandle(serviceManager);
}
- return success;
}
public boolean start() {
@@ -152,7 +145,7 @@ public abstract class WindowsService {
POSIXFactory.getPOSIX().chdir(path);
- serviceMain = new ServiceMain();
+ ServiceMain serviceMain = new ServiceMain();
SERVICE_TABLE_ENTRY entry = new SERVICE_TABLE_ENTRY();
entry.lpServiceName = serviceName;
entry.lpServiceProc = serviceMain;
@@ -180,7 +173,7 @@ public abstract class WindowsService {
private class ServiceMain implements SERVICE_MAIN_FUNCTION {
public void callback(int dwArgc, Pointer lpszArgv) {
- serviceControl = new ServiceControl();
+ ServiceControl serviceControl = new ServiceControl();
serviceStatusHandle = ADVAPI_32.RegisterServiceCtrlHandlerEx(serviceName, serviceControl, null);
reportStatus(Winsvc.SERVICE_START_PENDING, WinError.NO_ERROR, 3000);
@@ -194,7 +187,8 @@ public abstract class WindowsService {
synchronized (waitObject) {
waitObject.wait();
}
- } catch (InterruptedException ex) {
+ } catch (InterruptedException e) {
+ e.printStackTrace();
}
reportStatus(Winsvc.SERVICE_STOPPED, WinError.NO_ERROR, 0);
diff --git a/src/main/java/org/traccar/WrapperInboundHandler.java b/src/main/java/org/traccar/WrapperInboundHandler.java
index ca33d021f..5e2b1d304 100644
--- a/src/main/java/org/traccar/WrapperInboundHandler.java
+++ b/src/main/java/org/traccar/WrapperInboundHandler.java
@@ -20,7 +20,7 @@ import io.netty.channel.ChannelInboundHandler;
public class WrapperInboundHandler implements ChannelInboundHandler {
- private ChannelInboundHandler handler;
+ private final ChannelInboundHandler handler;
public ChannelInboundHandler getWrappedHandler() {
return handler;
diff --git a/src/main/java/org/traccar/WrapperOutboundHandler.java b/src/main/java/org/traccar/WrapperOutboundHandler.java
index 0136c5b22..ae2b06ad2 100644
--- a/src/main/java/org/traccar/WrapperOutboundHandler.java
+++ b/src/main/java/org/traccar/WrapperOutboundHandler.java
@@ -23,7 +23,7 @@ import java.net.SocketAddress;
public class WrapperOutboundHandler implements ChannelOutboundHandler {
- private ChannelOutboundHandler handler;
+ private final ChannelOutboundHandler handler;
public ChannelOutboundHandler getWrappedHandler() {
return handler;
diff --git a/src/main/java/org/traccar/api/AsyncSocket.java b/src/main/java/org/traccar/api/AsyncSocket.java
index 906d16b5b..b2ff5031a 100644
--- a/src/main/java/org/traccar/api/AsyncSocket.java
+++ b/src/main/java/org/traccar/api/AsyncSocket.java
@@ -39,7 +39,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
private static final String KEY_POSITIONS = "positions";
private static final String KEY_EVENTS = "events";
- private long userId;
+ private final long userId;
public AsyncSocket(long userId) {
this.userId = userId;
diff --git a/src/main/java/org/traccar/api/AsyncSocketServlet.java b/src/main/java/org/traccar/api/AsyncSocketServlet.java
index 9318b6fc6..51fbfb478 100644
--- a/src/main/java/org/traccar/api/AsyncSocketServlet.java
+++ b/src/main/java/org/traccar/api/AsyncSocketServlet.java
@@ -15,9 +15,6 @@
*/
package org.traccar.api;
-import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
-import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
-import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.traccar.Context;
@@ -30,15 +27,12 @@ public class AsyncSocketServlet extends WebSocketServlet {
@Override
public void configure(WebSocketServletFactory factory) {
factory.getPolicy().setIdleTimeout(Context.getConfig().getLong("web.timeout", ASYNC_TIMEOUT));
- factory.setCreator(new WebSocketCreator() {
- @Override
- public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) {
- if (req.getSession() != null) {
- long userId = (Long) req.getSession().getAttribute(SessionResource.USER_ID_KEY);
- return new AsyncSocket(userId);
- } else {
- return null;
- }
+ factory.setCreator((req, resp) -> {
+ if (req.getSession() != null) {
+ long userId = (Long) req.getSession().getAttribute(SessionResource.USER_ID_KEY);
+ return new AsyncSocket(userId);
+ } else {
+ return null;
}
});
}
diff --git a/src/main/java/org/traccar/api/BaseObjectResource.java b/src/main/java/org/traccar/api/BaseObjectResource.java
index c4206f385..bc7d3c466 100644
--- a/src/main/java/org/traccar/api/BaseObjectResource.java
+++ b/src/main/java/org/traccar/api/BaseObjectResource.java
@@ -44,7 +44,7 @@ import org.traccar.model.User;
public abstract class BaseObjectResource<T extends BaseModel> extends BaseResource {
- private Class<T> baseClass;
+ private final Class<T> baseClass;
public BaseObjectResource(Class<T> baseClass) {
this.baseClass = baseClass;
diff --git a/src/main/java/org/traccar/api/DateParameterConverterProvider.java b/src/main/java/org/traccar/api/DateParameterConverterProvider.java
new file mode 100644
index 000000000..f07ece984
--- /dev/null
+++ b/src/main/java/org/traccar/api/DateParameterConverterProvider.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020 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;
+
+import org.traccar.helper.DateUtil;
+
+import javax.ws.rs.ext.ParamConverter;
+import javax.ws.rs.ext.ParamConverterProvider;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+import java.util.Date;
+
+public class DateParameterConverterProvider implements ParamConverterProvider {
+
+ public static class DateParameterConverter implements ParamConverter<Date> {
+
+ @Override
+ public Date fromString(String value) {
+ return value != null ? DateUtil.parseDate(value) : null;
+ }
+
+ @Override
+ public String toString(Date value) {
+ return value != null ? DateUtil.formatDate(value) : null;
+ }
+
+ }
+
+ @Override
+ public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
+ if (Date.class.equals(rawType)) {
+ @SuppressWarnings("unchecked")
+ ParamConverter<T> paramConverter = (ParamConverter<T>) new DateParameterConverter();
+ return paramConverter;
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/api/HealthCheckService.java b/src/main/java/org/traccar/api/HealthCheckService.java
index 1e8f0d731..c9ce5a6c9 100644
--- a/src/main/java/org/traccar/api/HealthCheckService.java
+++ b/src/main/java/org/traccar/api/HealthCheckService.java
@@ -20,6 +20,7 @@ import com.sun.jna.Native;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.Context;
+import org.traccar.config.Keys;
import java.util.TimerTask;
@@ -33,7 +34,7 @@ public class HealthCheckService {
private long period;
public HealthCheckService() {
- if (Context.getConfig().getBoolean("web.healthCheck")
+ if (!Context.getConfig().getBoolean(Keys.WEB_DISABLE_HEALTH_CHECK)
&& System.getProperty("os.name").toLowerCase().startsWith("linux")) {
try {
systemD = Native.load("systemd", SystemD.class);
diff --git a/src/main/java/org/traccar/api/UserPrincipal.java b/src/main/java/org/traccar/api/UserPrincipal.java
index 80e92c2dd..175bca391 100644
--- a/src/main/java/org/traccar/api/UserPrincipal.java
+++ b/src/main/java/org/traccar/api/UserPrincipal.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 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.
@@ -19,7 +19,7 @@ import java.security.Principal;
public class UserPrincipal implements Principal {
- private long userId;
+ private final long userId;
public UserPrincipal(long userId) {
this.userId = userId;
diff --git a/src/main/java/org/traccar/api/resource/DeviceResource.java b/src/main/java/org/traccar/api/resource/DeviceResource.java
index f9c9a139d..7006cdb84 100644
--- a/src/main/java/org/traccar/api/resource/DeviceResource.java
+++ b/src/main/java/org/traccar/api/resource/DeviceResource.java
@@ -52,7 +52,7 @@ public class DeviceResource extends BaseObjectResource<Device> {
@QueryParam("uniqueId") List<String> uniqueIds,
@QueryParam("id") List<Long> deviceIds) throws SQLException {
DeviceManager deviceManager = Context.getDeviceManager();
- Set<Long> result = null;
+ Set<Long> result;
if (all) {
if (Context.getPermissionsManager().getUserAdmin(getUserId())) {
result = deviceManager.getAllItems();
diff --git a/src/main/java/org/traccar/api/resource/PositionResource.java b/src/main/java/org/traccar/api/resource/PositionResource.java
index 67aa6dd32..e93feaccf 100644
--- a/src/main/java/org/traccar/api/resource/PositionResource.java
+++ b/src/main/java/org/traccar/api/resource/PositionResource.java
@@ -17,7 +17,6 @@ package org.traccar.api.resource;
import org.traccar.Context;
import org.traccar.api.BaseResource;
-import org.traccar.helper.DateUtil;
import org.traccar.model.Position;
import org.traccar.web.CsvBuilder;
import org.traccar.web.GpxBuilder;
@@ -35,6 +34,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
@Path("positions")
@@ -50,7 +50,7 @@ public class PositionResource extends BaseResource {
@GET
public Collection<Position> getJson(
@QueryParam("deviceId") long deviceId, @QueryParam("id") List<Long> positionIds,
- @QueryParam("from") String from, @QueryParam("to") String to)
+ @QueryParam("from") Date from, @QueryParam("to") Date to)
throws SQLException {
if (!positionIds.isEmpty()) {
ArrayList<Position> positions = new ArrayList<>();
@@ -65,8 +65,7 @@ public class PositionResource extends BaseResource {
} else {
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
if (from != null && to != null) {
- return Context.getDataManager().getPositions(
- deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to));
+ return Context.getDataManager().getPositions(deviceId, from, to);
} else {
return Collections.singleton(Context.getDeviceManager().getLastPosition(deviceId));
}
@@ -76,25 +75,23 @@ public class PositionResource extends BaseResource {
@GET
@Produces(TEXT_CSV)
public Response getCsv(
- @QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to)
+ @QueryParam("deviceId") long deviceId, @QueryParam("from") Date from, @QueryParam("to") Date to)
throws SQLException {
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
CsvBuilder csv = new CsvBuilder();
csv.addHeaderLine(new Position());
- csv.addArray(Context.getDataManager().getPositions(
- deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to)));
+ csv.addArray(Context.getDataManager().getPositions(deviceId, from, to));
return Response.ok(csv.build()).header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_CSV).build();
}
@GET
@Produces(GPX)
public Response getGpx(
- @QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to)
+ @QueryParam("deviceId") long deviceId, @QueryParam("from") Date from, @QueryParam("to") Date to)
throws SQLException {
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
GpxBuilder gpx = new GpxBuilder(Context.getIdentityManager().getById(deviceId).getName());
- gpx.addPositions(Context.getDataManager().getPositions(
- deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to)));
+ gpx.addPositions(Context.getDataManager().getPositions(deviceId, from, to));
return Response.ok(gpx.build()).header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_GPX).build();
}
diff --git a/src/main/java/org/traccar/api/resource/ReportResource.java b/src/main/java/org/traccar/api/resource/ReportResource.java
index d371cf987..7347bfd64 100644
--- a/src/main/java/org/traccar/api/resource/ReportResource.java
+++ b/src/main/java/org/traccar/api/resource/ReportResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Collection;
+import java.util.Date;
import java.util.List;
import javax.activation.DataHandler;
@@ -39,7 +40,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.Context;
import org.traccar.api.BaseResource;
-import org.traccar.helper.DateUtil;
+import org.traccar.helper.LogAction;
import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.reports.Events;
@@ -97,9 +98,9 @@ public class ReportResource extends BaseResource {
@GET
public Collection<Position> getRoute(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
- return Route.getObjects(getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
+ @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds);
+ return Route.getObjects(getUserId(), deviceIds, groupIds, from, to);
}
@Path("route")
@@ -107,11 +108,11 @@ public class ReportResource extends BaseResource {
@Produces(XLSX)
public Response getRouteExcel(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail)
+ @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
throws SQLException, IOException {
return executeReport(getUserId(), mail, stream -> {
- Route.getExcel(stream, getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
+ LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds);
+ Route.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
});
}
@@ -120,9 +121,9 @@ public class ReportResource extends BaseResource {
public Collection<Event> getEvents(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("type") final List<String> types,
- @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
- return Events.getObjects(getUserId(), deviceIds, groupIds, types,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
+ @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds);
+ return Events.getObjects(getUserId(), deviceIds, groupIds, types, from, to);
}
@Path("events")
@@ -131,11 +132,11 @@ public class ReportResource extends BaseResource {
public Response getEventsExcel(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("type") final List<String> types,
- @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail)
+ @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
throws SQLException, IOException {
return executeReport(getUserId(), mail, stream -> {
- Events.getExcel(stream, getUserId(), deviceIds, groupIds, types,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
+ LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds);
+ Events.getExcel(stream, getUserId(), deviceIds, groupIds, types, from, to);
});
}
@@ -143,9 +144,10 @@ public class ReportResource extends BaseResource {
@GET
public Collection<SummaryReport> getSummary(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
- return Summary.getObjects(getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
+ @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily)
+ throws SQLException {
+ LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds);
+ return Summary.getObjects(getUserId(), deviceIds, groupIds, from, to, daily);
}
@Path("summary")
@@ -153,11 +155,12 @@ public class ReportResource extends BaseResource {
@Produces(XLSX)
public Response getSummaryExcel(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail)
+ @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily,
+ @QueryParam("mail") boolean mail)
throws SQLException, IOException {
return executeReport(getUserId(), mail, stream -> {
- Summary.getExcel(stream, getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
+ LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds);
+ Summary.getExcel(stream, getUserId(), deviceIds, groupIds, from, to, daily);
});
}
@@ -166,9 +169,9 @@ public class ReportResource extends BaseResource {
@Produces(MediaType.APPLICATION_JSON)
public Collection<TripReport> getTrips(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
- return Trips.getObjects(getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
+ @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds);
+ return Trips.getObjects(getUserId(), deviceIds, groupIds, from, to);
}
@Path("trips")
@@ -176,11 +179,11 @@ public class ReportResource extends BaseResource {
@Produces(XLSX)
public Response getTripsExcel(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail)
+ @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
throws SQLException, IOException {
return executeReport(getUserId(), mail, stream -> {
- Trips.getExcel(stream, getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
+ LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds);
+ Trips.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
});
}
@@ -189,9 +192,9 @@ public class ReportResource extends BaseResource {
@Produces(MediaType.APPLICATION_JSON)
public Collection<StopReport> getStops(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
- return Stops.getObjects(getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
+ @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds);
+ return Stops.getObjects(getUserId(), deviceIds, groupIds, from, to);
}
@Path("stops")
@@ -199,11 +202,11 @@ public class ReportResource extends BaseResource {
@Produces(XLSX)
public Response getStopsExcel(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
- @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail)
+ @QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
throws SQLException, IOException {
return executeReport(getUserId(), mail, stream -> {
- Stops.getExcel(stream, getUserId(), deviceIds, groupIds,
- DateUtil.parseDate(from), DateUtil.parseDate(to));
+ LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds);
+ Stops.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
});
}
diff --git a/src/main/java/org/traccar/api/resource/ServerResource.java b/src/main/java/org/traccar/api/resource/ServerResource.java
index e7cad2a0c..91488afff 100644
--- a/src/main/java/org/traccar/api/resource/ServerResource.java
+++ b/src/main/java/org/traccar/api/resource/ServerResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 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.
@@ -38,8 +38,12 @@ public class ServerResource extends BaseResource {
@PermitAll
@GET
- public Server get() throws SQLException {
- return Context.getPermissionsManager().getServer();
+ public Server get(@QueryParam("force") boolean force) throws SQLException {
+ if (force) {
+ return Context.getDataManager().getServer();
+ } else {
+ return Context.getPermissionsManager().getServer();
+ }
}
@PUT
diff --git a/src/main/java/org/traccar/api/resource/StatisticsResource.java b/src/main/java/org/traccar/api/resource/StatisticsResource.java
index e801d4ff3..58073e7d1 100644
--- a/src/main/java/org/traccar/api/resource/StatisticsResource.java
+++ b/src/main/java/org/traccar/api/resource/StatisticsResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 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.
@@ -17,7 +17,6 @@ package org.traccar.api.resource;
import org.traccar.Context;
import org.traccar.api.BaseResource;
-import org.traccar.helper.DateUtil;
import org.traccar.model.Statistics;
import javax.ws.rs.Consumes;
@@ -28,6 +27,7 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.sql.SQLException;
import java.util.Collection;
+import java.util.Date;
@Path("statistics")
@Produces(MediaType.APPLICATION_JSON)
@@ -36,9 +36,9 @@ public class StatisticsResource extends BaseResource {
@GET
public Collection<Statistics> get(
- @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
+ @QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
Context.getPermissionsManager().checkAdmin(getUserId());
- return Context.getDataManager().getStatistics(DateUtil.parseDate(from), DateUtil.parseDate(to));
+ return Context.getDataManager().getStatistics(from, to);
}
}
diff --git a/src/main/java/org/traccar/api/resource/UserResource.java b/src/main/java/org/traccar/api/resource/UserResource.java
index 0b42d8d92..813ace6d6 100644
--- a/src/main/java/org/traccar/api/resource/UserResource.java
+++ b/src/main/java/org/traccar/api/resource/UserResource.java
@@ -48,7 +48,7 @@ public class UserResource extends BaseObjectResource<User> {
@GET
public Collection<User> get(@QueryParam("userId") long userId) throws SQLException {
UsersManager usersManager = Context.getUsersManager();
- Set<Long> result = null;
+ Set<Long> result;
if (Context.getPermissionsManager().getUserAdmin(getUserId())) {
if (userId != 0) {
result = usersManager.getUserItems(userId);
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java
index 441898feb..77b42943b 100644
--- a/src/main/java/org/traccar/config/Keys.java
+++ b/src/main/java/org/traccar/config/Keys.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 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.
@@ -76,6 +76,12 @@ public final class Keys {
"extra.handlers", String.class);
/**
+ * Store original HEX or string data as "raw" attribute in the corresponding position.
+ */
+ public static final ConfigKey DATABASE_SAVE_ORIGINAL = new ConfigKey(
+ "database.saveOriginal", Boolean.class);
+
+ /**
* Enable positions forwarding to other web server.
*/
public static final ConfigKey FORWARD_ENABLE = new ConfigKey(
@@ -389,6 +395,24 @@ public final class Keys {
"geolocation.processInvalidPositions", Boolean.class);
/**
+ * Boolean flag to enable speed limit API to get speed limit values depending on location. Default value is false.
+ */
+ public static final ConfigKey SPEED_LIMIT_ENABLE = new ConfigKey(
+ "speedLimit.enable", Boolean.class);
+
+ /**
+ * Provider to use for speed limit. Available options: overpass. By default overpass is used.
+ */
+ public static final ConfigKey SPEED_LIMIT_TYPE = new ConfigKey(
+ "speedLimit.type", String.class);
+
+ /**
+ * Speed limit provider API URL address.
+ */
+ public static final ConfigKey SPEED_LIMIT_URL = new ConfigKey(
+ "speedLimit.url", String.class);
+
+ /**
* Override latitude sign / hemisphere. Useful in cases where value is incorrect because of device bug. Value can be
* N for North or S for South.
*/
@@ -423,6 +447,19 @@ public final class Keys {
public static final ConfigKey WEB_REQUEST_LOG_RETAIN_DAYS = new ConfigKey(
"web.requestLog.retainDays", Integer.class);
+ /**
+ * Disable systemd health checks.
+ */
+ public static final ConfigKey WEB_DISABLE_HEALTH_CHECK = new ConfigKey(
+ "web.disableHealthCheck", Boolean.class);
+
+ /**
+ * Sets SameSite cookie attribute value.
+ * Supported options: Lax, Strict, None.
+ */
+ public static final ConfigKey WEB_SAME_SITE_COOKIE = new ConfigKey(
+ "web.sameSiteCookie", String.class);
+
private Keys() {
}
diff --git a/src/main/java/org/traccar/database/BaseObjectManager.java b/src/main/java/org/traccar/database/BaseObjectManager.java
index d0024c005..be6310033 100644
--- a/src/main/java/org/traccar/database/BaseObjectManager.java
+++ b/src/main/java/org/traccar/database/BaseObjectManager.java
@@ -38,8 +38,8 @@ public class BaseObjectManager<T extends BaseModel> {
private final DataManager dataManager;
+ private final Class<T> baseClass;
private Map<Long, T> items;
- private Class<T> baseClass;
protected BaseObjectManager(DataManager dataManager, Class<T> baseClass) {
this.dataManager = dataManager;
diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java
index 485402807..843c89e82 100644
--- a/src/main/java/org/traccar/database/CommandsManager.java
+++ b/src/main/java/org/traccar/database/CommandsManager.java
@@ -41,7 +41,7 @@ public class CommandsManager extends ExtendedObjectManager<Command> {
private final Map<Long, Queue<Command>> deviceQueues = new ConcurrentHashMap<>();
- private boolean queueing;
+ private final boolean queueing;
public CommandsManager(DataManager dataManager, boolean queueing) {
super(dataManager, Command.class);
diff --git a/src/main/java/org/traccar/database/ConnectionManager.java b/src/main/java/org/traccar/database/ConnectionManager.java
index 4d43bc71b..9342fd3de 100644
--- a/src/main/java/org/traccar/database/ConnectionManager.java
+++ b/src/main/java/org/traccar/database/ConnectionManager.java
@@ -17,7 +17,6 @@ package org.traccar.database;
import io.netty.channel.Channel;
import io.netty.util.Timeout;
-import io.netty.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.Context;
@@ -122,12 +121,9 @@ public class ConnectionManager {
}
if (status.equals(Device.STATUS_ONLINE)) {
- timeouts.put(deviceId, GlobalTimer.getTimer().newTimeout(new TimerTask() {
- @Override
- public void run(Timeout timeout) {
- if (!timeout.isCancelled()) {
- updateDevice(deviceId, Device.STATUS_UNKNOWN, null);
- }
+ timeouts.put(deviceId, GlobalTimer.getTimer().newTimeout(timeout1 -> {
+ if (!timeout1.isCancelled()) {
+ updateDevice(deviceId, Device.STATUS_UNKNOWN, null);
}
}, deviceTimeout, TimeUnit.MILLISECONDS));
}
diff --git a/src/main/java/org/traccar/database/DataManager.java b/src/main/java/org/traccar/database/DataManager.java
index 8e9071736..8b9690f77 100644
--- a/src/main/java/org/traccar/database/DataManager.java
+++ b/src/main/java/org/traccar/database/DataManager.java
@@ -79,7 +79,7 @@ public class DataManager {
private boolean generateQueries;
- private boolean forceLdap;
+ private final boolean forceLdap;
public DataManager(Config config) throws Exception {
this.config = config;
diff --git a/src/main/java/org/traccar/database/DeviceManager.java b/src/main/java/org/traccar/database/DeviceManager.java
index fe17f7ced..e621e6058 100644
--- a/src/main/java/org/traccar/database/DeviceManager.java
+++ b/src/main/java/org/traccar/database/DeviceManager.java
@@ -45,11 +45,11 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
private final Config config;
private final long dataRefreshDelay;
- private boolean lookupGroupsAttribute;
+ private final boolean lookupGroupsAttribute;
private Map<String, Device> devicesByUniqueId;
private Map<String, Device> devicesByPhone;
- private AtomicLong devicesLastUpdate = new AtomicLong();
+ private final AtomicLong devicesLastUpdate = new AtomicLong();
private final Map<Long, Position> positions = new ConcurrentHashMap<>();
diff --git a/src/main/java/org/traccar/database/PermissionsManager.java b/src/main/java/org/traccar/database/PermissionsManager.java
index ced0df1c0..d2dc62394 100644
--- a/src/main/java/org/traccar/database/PermissionsManager.java
+++ b/src/main/java/org/traccar/database/PermissionsManager.java
@@ -196,7 +196,7 @@ public class PermissionsManager {
public void checkDeviceLimit(long userId) throws SecurityException {
int deviceLimit = getUser(userId).getDeviceLimit();
if (deviceLimit != -1) {
- int deviceCount = 0;
+ int deviceCount;
if (getUserManager(userId)) {
deviceCount = Context.getDeviceManager().getAllManagedItems(userId).size();
} else {
diff --git a/src/main/java/org/traccar/database/QueryBuilder.java b/src/main/java/org/traccar/database/QueryBuilder.java
index 5528b2320..084d2940b 100644
--- a/src/main/java/org/traccar/database/QueryBuilder.java
+++ b/src/main/java/org/traccar/database/QueryBuilder.java
@@ -114,11 +114,7 @@ public final class QueryBuilder {
name = name.toLowerCase();
// Add to list
- List<Integer> indexList = paramMap.get(name);
- if (indexList == null) {
- indexList = new LinkedList<>();
- paramMap.put(name, indexList);
- }
+ List<Integer> indexList = paramMap.computeIfAbsent(name, k -> new LinkedList<>());
indexList.add(index);
index++;
@@ -318,96 +314,72 @@ public final class QueryBuilder {
final Class<?> parameterType, final Method method, final String name) {
if (parameterType.equals(boolean.class)) {
- processors.add(new ResultSetProcessor<T>() {
- @Override
- public void process(T object, ResultSet resultSet) throws SQLException {
- try {
- method.invoke(object, resultSet.getBoolean(name));
- } catch (IllegalAccessException | InvocationTargetException error) {
- LOGGER.warn("Set property error", error);
- }
+ processors.add((object, resultSet) -> {
+ try {
+ method.invoke(object, resultSet.getBoolean(name));
+ } catch (IllegalAccessException | InvocationTargetException error) {
+ LOGGER.warn("Set property error", error);
}
});
} else if (parameterType.equals(int.class)) {
- processors.add(new ResultSetProcessor<T>() {
- @Override
- public void process(T object, ResultSet resultSet) throws SQLException {
- try {
- method.invoke(object, resultSet.getInt(name));
- } catch (IllegalAccessException | InvocationTargetException error) {
- LOGGER.warn("Set property error", error);
- }
+ processors.add((object, resultSet) -> {
+ try {
+ method.invoke(object, resultSet.getInt(name));
+ } catch (IllegalAccessException | InvocationTargetException error) {
+ LOGGER.warn("Set property error", error);
}
});
} else if (parameterType.equals(long.class)) {
- processors.add(new ResultSetProcessor<T>() {
- @Override
- public void process(T object, ResultSet resultSet) throws SQLException {
- try {
- method.invoke(object, resultSet.getLong(name));
- } catch (IllegalAccessException | InvocationTargetException error) {
- LOGGER.warn("Set property error", error);
- }
+ processors.add((object, resultSet) -> {
+ try {
+ method.invoke(object, resultSet.getLong(name));
+ } catch (IllegalAccessException | InvocationTargetException error) {
+ LOGGER.warn("Set property error", error);
}
});
} else if (parameterType.equals(double.class)) {
- processors.add(new ResultSetProcessor<T>() {
- @Override
- public void process(T object, ResultSet resultSet) throws SQLException {
- try {
- method.invoke(object, resultSet.getDouble(name));
- } catch (IllegalAccessException | InvocationTargetException error) {
- LOGGER.warn("Set property error", error);
- }
+ processors.add((object, resultSet) -> {
+ try {
+ method.invoke(object, resultSet.getDouble(name));
+ } catch (IllegalAccessException | InvocationTargetException error) {
+ LOGGER.warn("Set property error", error);
}
});
} else if (parameterType.equals(String.class)) {
- processors.add(new ResultSetProcessor<T>() {
- @Override
- public void process(T object, ResultSet resultSet) throws SQLException {
- try {
- method.invoke(object, resultSet.getString(name));
- } catch (IllegalAccessException | InvocationTargetException error) {
- LOGGER.warn("Set property error", error);
- }
+ processors.add((object, resultSet) -> {
+ try {
+ method.invoke(object, resultSet.getString(name));
+ } catch (IllegalAccessException | InvocationTargetException error) {
+ LOGGER.warn("Set property error", error);
}
});
} else if (parameterType.equals(Date.class)) {
- processors.add(new ResultSetProcessor<T>() {
- @Override
- public void process(T object, ResultSet resultSet) throws SQLException {
- try {
- Timestamp timestamp = resultSet.getTimestamp(name);
- if (timestamp != null) {
- method.invoke(object, new Date(timestamp.getTime()));
- }
- } catch (IllegalAccessException | InvocationTargetException error) {
- LOGGER.warn("Set property error", error);
+ processors.add((object, resultSet) -> {
+ try {
+ Timestamp timestamp = resultSet.getTimestamp(name);
+ if (timestamp != null) {
+ method.invoke(object, new Date(timestamp.getTime()));
}
+ } catch (IllegalAccessException | InvocationTargetException error) {
+ LOGGER.warn("Set property error", error);
}
});
} else if (parameterType.equals(byte[].class)) {
- processors.add(new ResultSetProcessor<T>() {
- @Override
- public void process(T object, ResultSet resultSet) throws SQLException {
- try {
- method.invoke(object, resultSet.getBytes(name));
- } catch (IllegalAccessException | InvocationTargetException error) {
- LOGGER.warn("Set property error", error);
- }
+ processors.add((object, resultSet) -> {
+ try {
+ method.invoke(object, resultSet.getBytes(name));
+ } catch (IllegalAccessException | InvocationTargetException error) {
+ LOGGER.warn("Set property error", error);
}
});
} else {
- processors.add(new ResultSetProcessor<T>() {
- @Override
- public void process(T object, ResultSet resultSet) throws SQLException {
- String value = resultSet.getString(name);
- if (value != null && !value.isEmpty()) {
- try {
- method.invoke(object, Context.getObjectMapper().readValue(value, parameterType));
- } catch (InvocationTargetException | IllegalAccessException | IOException error) {
- LOGGER.warn("Set property error", error);
- }
+ processors.add((object, resultSet) -> {
+ String value = resultSet.getString(name);
+ if (value != null && !value.isEmpty()) {
+ try {
+ method.invoke(object, Context.getObjectMapper().readValue(value, parameterType));
+ } catch (InvocationTargetException | IllegalAccessException | IOException error) {
+ LOGGER.warn("Set property error", error);
}
}
});
@@ -453,12 +425,12 @@ public final class QueryBuilder {
while (resultSet.next()) {
try {
- T object = clazz.newInstance();
+ T object = clazz.getDeclaredConstructor().newInstance();
for (ResultSetProcessor<T> processor : processors) {
processor.process(object, resultSet);
}
result.add(object);
- } catch (InstantiationException | IllegalAccessException e) {
+ } catch (ReflectiveOperationException e) {
throw new IllegalArgumentException();
}
}
diff --git a/src/main/java/org/traccar/geofence/GeofenceCircle.java b/src/main/java/org/traccar/geofence/GeofenceCircle.java
index f6fca63ca..eda63832d 100644
--- a/src/main/java/org/traccar/geofence/GeofenceCircle.java
+++ b/src/main/java/org/traccar/geofence/GeofenceCircle.java
@@ -50,7 +50,7 @@ public class GeofenceCircle extends GeofenceGeometry {
@Override
public String toWkt() {
- String wkt = "";
+ String wkt;
wkt = "CIRCLE (";
wkt += String.valueOf(centerLatitude);
wkt += " ";
diff --git a/src/main/java/org/traccar/handler/SpeedLimitHandler.java b/src/main/java/org/traccar/handler/SpeedLimitHandler.java
new file mode 100644
index 000000000..65f2c9cfe
--- /dev/null
+++ b/src/main/java/org/traccar/handler/SpeedLimitHandler.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 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.handler;
+
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.model.Position;
+import org.traccar.speedlimit.SpeedLimitProvider;
+
+@ChannelHandler.Sharable
+public class SpeedLimitHandler extends ChannelInboundHandlerAdapter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SpeedLimitHandler.class);
+
+ private final SpeedLimitProvider speedLimitProvider;
+
+ public SpeedLimitHandler(SpeedLimitProvider speedLimitProvider) {
+ this.speedLimitProvider = speedLimitProvider;
+ }
+
+ @Override
+ public void channelRead(final ChannelHandlerContext ctx, Object message) {
+ if (message instanceof Position) {
+ final Position position = (Position) message;
+ speedLimitProvider.getSpeedLimit(position.getLatitude(), position.getLongitude(),
+ new SpeedLimitProvider.SpeedLimitProviderCallback() {
+ @Override
+ public void onSuccess(double speedLimit) {
+ position.set(Position.KEY_SPEED_LIMIT, speedLimit);
+ ctx.fireChannelRead(position);
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ LOGGER.warn("Speed limit provider failed", e);
+ ctx.fireChannelRead(position);
+ }
+ });
+ } else {
+ ctx.fireChannelRead(message);
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/handler/TimeHandler.java b/src/main/java/org/traccar/handler/TimeHandler.java
index d8039518c..822c22a0a 100644
--- a/src/main/java/org/traccar/handler/TimeHandler.java
+++ b/src/main/java/org/traccar/handler/TimeHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 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.
@@ -53,8 +53,10 @@ public class TimeHandler extends ChannelInboundHandlerAdapter {
Position position = (Position) msg;
if (useServerTime) {
position.setDeviceTime(position.getServerTime());
+ position.setFixTime(position.getServerTime());
+ } else {
+ position.setFixTime(position.getDeviceTime());
}
- position.setFixTime(position.getDeviceTime());
}
ctx.fireChannelRead(msg);
diff --git a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
index e534df9de..c396b28e9 100644
--- a/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/OverspeedEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -122,6 +122,11 @@ public class OverspeedEventHandler extends BaseEventHandler {
double speedLimit = deviceManager.lookupAttributeDouble(deviceId, ATTRIBUTE_SPEED_LIMIT, 0, true, false);
+ double positionSpeedLimit = position.getDouble(Position.KEY_SPEED_LIMIT);
+ if (positionSpeedLimit > 0) {
+ speedLimit = positionSpeedLimit;
+ }
+
double geofenceSpeedLimit = 0;
long overspeedGeofenceId = 0;
diff --git a/src/main/java/org/traccar/helper/BitBuffer.java b/src/main/java/org/traccar/helper/BitBuffer.java
index f30a4557b..323807ba9 100644
--- a/src/main/java/org/traccar/helper/BitBuffer.java
+++ b/src/main/java/org/traccar/helper/BitBuffer.java
@@ -93,7 +93,7 @@ public class BitBuffer {
return result;
} else {
result &= signBit - 1;
- result += ~(signBit - 1);
+ result += -signBit;
return result;
}
}
diff --git a/src/main/java/org/traccar/helper/Checksum.java b/src/main/java/org/traccar/helper/Checksum.java
index d41dc2992..8c3d0063a 100644
--- a/src/main/java/org/traccar/helper/Checksum.java
+++ b/src/main/java/org/traccar/helper/Checksum.java
@@ -26,12 +26,12 @@ public final class Checksum {
public static class Algorithm {
- private int poly;
- private int init;
- private boolean refIn;
- private boolean refOut;
- private int xorOut;
- private int[] table;
+ private final int poly;
+ private final int init;
+ private final boolean refIn;
+ private final boolean refOut;
+ private final int xorOut;
+ private final int[] table;
public Algorithm(int bits, int poly, int init, boolean refIn, boolean refOut, int xorOut) {
this.poly = poly;
@@ -151,13 +151,8 @@ public final class Checksum {
return checksum;
}
- public static String nmea(String msg) {
- int checksum = 0;
- byte[] bytes = msg.getBytes(StandardCharsets.US_ASCII);
- for (int i = 1; i < bytes.length; i++) {
- checksum ^= bytes[i];
- }
- return String.format("*%02x", checksum).toUpperCase();
+ public static String nmea(String string) {
+ return String.format("*%02X", xor(string));
}
public static int sum(ByteBuffer buf) {
diff --git a/src/main/java/org/traccar/helper/DateBuilder.java b/src/main/java/org/traccar/helper/DateBuilder.java
index 9752f6977..24b7d027a 100644
--- a/src/main/java/org/traccar/helper/DateBuilder.java
+++ b/src/main/java/org/traccar/helper/DateBuilder.java
@@ -21,7 +21,7 @@ import java.util.TimeZone;
public class DateBuilder {
- private Calendar calendar;
+ private final Calendar calendar;
public DateBuilder() {
this(TimeZone.getTimeZone("UTC"));
diff --git a/src/main/java/org/traccar/helper/LogAction.java b/src/main/java/org/traccar/helper/LogAction.java
index 16d55ec60..d16b25483 100644
--- a/src/main/java/org/traccar/helper/LogAction.java
+++ b/src/main/java/org/traccar/helper/LogAction.java
@@ -17,6 +17,10 @@
package org.traccar.helper;
import java.beans.Introspector;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,6 +50,7 @@ public final class LogAction {
private static final String PATTERN_LOGIN = "user: %d, action: %s";
private static final String PATTERN_LOGIN_FAILED = "login failed from: %s";
private static final String PATTERN_DEVICE_ACCUMULATORS = "user: %d, action: %s, deviceId: %d";
+ private static final String PATTERN_REPORT = "user: %d, report: %s, from: %s, to: %s, devices: %s, groups: %s";
public static void create(long userId, BaseModel object) {
logObjectAction(ACTION_CREATE, userId, object.getClass(), object.getId());
@@ -92,8 +97,8 @@ public final class LogAction {
PATTERN_OBJECT, userId, action, Introspector.decapitalize(clazz.getSimpleName()), objectId));
}
- private static void logLinkAction(String action, long userId,
- Class<?> owner, long ownerId, Class<?> property, long propertyId) {
+ private static void logLinkAction(
+ String action, long userId, Class<?> owner, long ownerId, Class<?> property, long propertyId) {
LOGGER.info(String.format(
PATTERN_LINK, userId, action,
Introspector.decapitalize(owner.getSimpleName()), ownerId,
@@ -104,4 +109,13 @@ public final class LogAction {
LOGGER.info(String.format(PATTERN_LOGIN, userId, action));
}
+ public static void logReport(
+ long userId, String report, Date from, Date to, List<Long> deviceIds, List<Long> groupIds) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+ LOGGER.info(String.format(
+ PATTERN_REPORT, userId, report,
+ dateFormat.format(from), dateFormat.format(to),
+ deviceIds.toString(), groupIds.toString()));
+ }
+
}
diff --git a/src/main/java/org/traccar/helper/ObdDecoder.java b/src/main/java/org/traccar/helper/ObdDecoder.java
index 1bdcce352..b22065f4e 100644
--- a/src/main/java/org/traccar/helper/ObdDecoder.java
+++ b/src/main/java/org/traccar/helper/ObdDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 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.
@@ -35,7 +35,7 @@ public final class ObdDecoder {
case MODE_FREEZE_FRAME:
return decodeData(
Integer.parseInt(value.substring(0, 2), 16),
- Integer.parseInt(value.substring(2), 16), true);
+ Long.parseLong(value.substring(2), 16), true);
case MODE_CODES:
return decodeCodes(value);
default:
@@ -75,7 +75,7 @@ public final class ObdDecoder {
}
}
- public static Map.Entry<String, Object> decodeData(int pid, int value, boolean convert) {
+ public static Map.Entry<String, Object> decodeData(int pid, long value, boolean convert) {
switch (pid) {
case 0x04:
return createEntry(Position.KEY_ENGINE_LOAD, convert ? value * 100 / 255 : value);
diff --git a/src/main/java/org/traccar/helper/Parser.java b/src/main/java/org/traccar/helper/Parser.java
index 1471ec237..75106e2ba 100644
--- a/src/main/java/org/traccar/helper/Parser.java
+++ b/src/main/java/org/traccar/helper/Parser.java
@@ -242,7 +242,7 @@ public class Parser {
public Date nextDateTime(DateTimeFormat format, String timeZone) {
int year = 0, month = 0, day = 0;
- int hour = 0, minute = 0, second = 0, millisecond = 0;
+ int hour, minute, second, millisecond = 0;
switch (format) {
case HMS:
diff --git a/src/main/java/org/traccar/model/Command.java b/src/main/java/org/traccar/model/Command.java
index abe538a10..099fb152d 100644
--- a/src/main/java/org/traccar/model/Command.java
+++ b/src/main/java/org/traccar/model/Command.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 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.
@@ -31,6 +31,7 @@ public class Command extends Message implements Cloneable {
public static final String TYPE_ENGINE_RESUME = "engineResume";
public static final String TYPE_ALARM_ARM = "alarmArm";
public static final String TYPE_ALARM_DISARM = "alarmDisarm";
+ public static final String TYPE_ALARM_DISMISS = "alarmDismiss";
public static final String TYPE_SET_TIMEZONE = "setTimezone";
public static final String TYPE_REQUEST_PHOTO = "requestPhoto";
public static final String TYPE_POWER_OFF = "powerOff";
diff --git a/src/main/java/org/traccar/model/Event.java b/src/main/java/org/traccar/model/Event.java
index ee7fcc679..5eee2a0a0 100644
--- a/src/main/java/org/traccar/model/Event.java
+++ b/src/main/java/org/traccar/model/Event.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 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.
@@ -40,6 +40,7 @@ public class Event extends Message {
public static final String TYPE_DEVICE_ONLINE = "deviceOnline";
public static final String TYPE_DEVICE_UNKNOWN = "deviceUnknown";
public static final String TYPE_DEVICE_OFFLINE = "deviceOffline";
+ public static final String TYPE_DEVICE_INACTIVE = "deviceInactive";
public static final String TYPE_DEVICE_MOVING = "deviceMoving";
public static final String TYPE_DEVICE_STOPPED = "deviceStopped";
diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java
index 2c0e22c9e..6f70c8e21 100644
--- a/src/main/java/org/traccar/model/Position.java
+++ b/src/main/java/org/traccar/model/Position.java
@@ -85,6 +85,7 @@ public class Position extends Message {
public static final String KEY_G_SENSOR = "gSensor";
public static final String KEY_ICCID = "iccid";
public static final String KEY_PHONE = "phone";
+ public static final String KEY_SPEED_LIMIT = "speedLimit";
public static final String KEY_DTCS = "dtcs";
public static final String KEY_OBD_SPEED = "obdSpeed"; // knots
diff --git a/src/main/java/org/traccar/model/Server.java b/src/main/java/org/traccar/model/Server.java
index ad37e7078..112bfab26 100644
--- a/src/main/java/org/traccar/model/Server.java
+++ b/src/main/java/org/traccar/model/Server.java
@@ -166,4 +166,14 @@ public class Server extends ExtendedModel {
public void setPoiLayer(String poiLayer) {
this.poiLayer = poiLayer;
}
+
+ private String announcement;
+
+ public String getAnnouncement() {
+ return announcement;
+ }
+
+ public void setAnnouncement(String announcement) {
+ this.announcement = announcement;
+ }
}
diff --git a/src/main/java/org/traccar/model/Typed.java b/src/main/java/org/traccar/model/Typed.java
index 313ec7bcd..fc671ac70 100644
--- a/src/main/java/org/traccar/model/Typed.java
+++ b/src/main/java/org/traccar/model/Typed.java
@@ -1,4 +1,5 @@
/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Gabor Somogyi (gabor.g.somogyi@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,6 +16,8 @@
*/
package org.traccar.model;
+import java.util.Objects;
+
public class Typed {
private String type;
@@ -30,4 +33,20 @@ public class Typed {
public void setType(String type) {
this.type = type;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ return Objects.equals(type, ((Typed) o).type);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(type);
+ }
}
diff --git a/src/main/java/org/traccar/notificators/Notificator.java b/src/main/java/org/traccar/notificators/Notificator.java
index 5e40971c6..dd888bae9 100644
--- a/src/main/java/org/traccar/notificators/Notificator.java
+++ b/src/main/java/org/traccar/notificators/Notificator.java
@@ -27,13 +27,11 @@ public abstract class Notificator {
private static final Logger LOGGER = LoggerFactory.getLogger(Notificator.class);
public void sendAsync(final long userId, final Event event, final Position position) {
- new Thread(new Runnable() {
- public void run() {
- try {
- sendSync(userId, event, position);
- } catch (MessageException | InterruptedException error) {
- LOGGER.warn("Event send error", error);
- }
+ new Thread(() -> {
+ try {
+ sendSync(userId, event, position);
+ } catch (MessageException | InterruptedException error) {
+ LOGGER.warn("Event send error", error);
}
}).start();
}
diff --git a/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java b/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java
index f071e2d97..8ed1fc8e8 100644
--- a/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/AtrackFrameDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 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.
@@ -39,7 +39,7 @@ public class AtrackFrameDecoder extends BaseFrameDecoder {
return buf.readRetainedSlice(KEEPALIVE_LENGTH);
}
- } else if (buf.getUnsignedShort(buf.readerIndex()) == 0x4050 && buf.getByte(buf.readerIndex() + 2) != ',') {
+ } else if (buf.getUnsignedByte(buf.readerIndex()) == 0x40 && buf.getByte(buf.readerIndex() + 2) != ',') {
if (buf.readableBytes() > 6) {
int length = buf.getUnsignedShort(buf.readerIndex() + 4) + 4 + 2;
diff --git a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
index 428b69cd9..56c00c7c1 100644
--- a/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/AtrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2020 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.
@@ -50,10 +50,12 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
private static final int MIN_DATA_LENGTH = 40;
private boolean longDate;
- private boolean decimalFuel;
+ private final boolean decimalFuel;
private boolean custom;
private String form;
+ private ByteBuf photo;
+
private final Map<Integer, String> alarmMap = new HashMap<>();
public AtrackProtocolDecoder(Protocol protocol) {
@@ -510,20 +512,34 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
return position;
}
- private List<Position> decodeBinary(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+ private Position decodePhoto(DeviceSession deviceSession, ByteBuf buf, long id) {
- buf.skipBytes(2); // prefix
- buf.readUnsignedShort(); // checksum
- buf.readUnsignedShort(); // length
- int index = buf.readUnsignedShort();
+ long time = buf.readUnsignedInt();
+ int index = buf.readUnsignedByte();
+ int count = buf.readUnsignedByte();
- long id = buf.readLong();
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id));
- if (deviceSession == null) {
- return null;
+ if (photo == null) {
+ photo = Unpooled.buffer();
+ }
+ photo.writeBytes(buf.readSlice(buf.readUnsignedShort()));
+
+ if (index == count - 1) {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, new Date(time * 1000));
+
+ position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(String.valueOf(id), photo, "jpg"));
+ photo.release();
+ photo = null;
+
+ return position;
}
- sendResponse(channel, remoteAddress, id, index);
+ return null;
+ }
+
+ private List<Position> decodeBinary(DeviceSession deviceSession, ByteBuf buf) {
List<Position> positions = new LinkedList<>();
@@ -613,7 +629,26 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
} else if (buf.getByte(buf.readerIndex() + 2) == ',') {
return decodeText(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII).trim());
} else {
- return decodeBinary(channel, remoteAddress, buf);
+
+ String prefix = buf.readCharSequence(2, StandardCharsets.US_ASCII).toString();
+ buf.readUnsignedShort(); // checksum
+ buf.readUnsignedShort(); // length
+ int index = buf.readUnsignedShort();
+
+ long id = buf.readLong();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ sendResponse(channel, remoteAddress, id, index);
+
+ if (prefix.equals("@R")) {
+ return decodePhoto(deviceSession, buf, id);
+ } else {
+ return decodeBinary(deviceSession, buf);
+ }
+
}
}
diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
index bc74b6576..09bd3572f 100644
--- a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
@@ -73,7 +73,7 @@ public class CellocatorProtocolDecoder extends BaseProtocolDecoder {
content.writeByte(packetNumber);
content.writeZero(11);
- ByteBuf reply = encodeContent(MSG_SERVER_ACKNOWLEDGE, (int) deviceId, packetNumber, content);
+ ByteBuf reply = encodeContent(MSG_SERVER_ACKNOWLEDGE, (int) deviceId, 0, content);
channel.writeAndFlush(new NetworkMessage(reply, remoteAddress));
}
}
diff --git a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
index d22106a80..115884e2c 100644
--- a/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EasyTrackProtocolDecoder.java
@@ -97,7 +97,7 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder {
String sentence = (String) msg;
String type = sentence.substring(20, 22);
- if (type.equals("TX") && channel != null) {
+ if ((type.equals("TX") || type.equals("MQ")) && channel != null) {
channel.writeAndFlush(new NetworkMessage(sentence + "#", remoteAddress));
}
diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
index c3fe7121e..613710587 100644
--- a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2020 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.
@@ -35,6 +35,8 @@ 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;
import java.util.regex.Pattern;
public class EelinkProtocolDecoder extends BaseProtocolDecoder {
@@ -76,6 +78,8 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
case 0x08:
case 0x09:
return Position.ALARM_GPS_ANTENNA_CUT;
+ case 0x25:
+ return Position.ALARM_REMOVING;
case 0x81:
return Position.ALARM_LOW_SPEED;
case 0x82:
@@ -388,9 +392,29 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
deviceSession = getDeviceSession(channel, remoteAddress);
}
+ List<Position> positions = new LinkedList<>();
+
+ while (buf.isReadable()) {
+ Position position = decodePackage(channel, remoteAddress, buf, uniqueId, deviceSession);
+ if (position != null) {
+ positions.add(position);
+ }
+ }
+
+ if (!positions.isEmpty()) {
+ return positions.size() > 1 ? positions : positions.iterator().next();
+ } else {
+ return null;
+ }
+ }
+
+ protected Position decodePackage(
+ Channel channel, SocketAddress remoteAddress, ByteBuf buf,
+ String uniqueId, DeviceSession deviceSession) throws Exception {
+
buf.skipBytes(2); // header
int type = buf.readUnsignedByte();
- buf.readShort(); // length
+ buf = buf.readSlice(buf.readUnsignedShort());
int index = buf.readUnsignedShort();
if (type != MSG_GPS && type != MSG_DATA) {
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
index 9bc7cb504..fe42a44d7 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -60,7 +60,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
.number("(-?d+),") // altitude
.number("(d+),") // odometer
.number("d+,") // runtime
- .number("(x{4,8}),") // status
+ .number("(x+),") // status
.number("(x+)?,") // input
.number("(x+)?,") // output
.number("(d+)|") // mcc
diff --git a/src/main/java/org/traccar/protocol/FutureWayFrameDecoder.java b/src/main/java/org/traccar/protocol/FutureWayFrameDecoder.java
new file mode 100644
index 000000000..812f78d02
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FutureWayFrameDecoder.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2020 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 io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.helper.DataConverter;
+
+import java.nio.charset.StandardCharsets;
+
+public class FutureWayFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 10) {
+ return null;
+ }
+
+ int length = Unpooled.wrappedBuffer(DataConverter.parseHex(
+ buf.getCharSequence(buf.readerIndex() + 2, 8, StandardCharsets.US_ASCII).toString())).readInt() + 17;
+
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocol.java b/src/main/java/org/traccar/protocol/FutureWayProtocol.java
new file mode 100644
index 000000000..73b53ee12
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FutureWayProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 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 io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class FutureWayProtocol extends BaseProtocol {
+
+ public FutureWayProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new FutureWayFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new FutureWayProtocolDecoder(FutureWayProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java b/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java
new file mode 100644
index 000000000..4b0f46e34
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FutureWayProtocolDecoder.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2020 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 io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.DataConverter;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+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.regex.Pattern;
+
+public class FutureWayProtocolDecoder extends BaseProtocolDecoder {
+
+ public FutureWayProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN_GPS = new PatternBuilder()
+ .text("GPS:")
+ .expression("([AV]),") // validity
+ .number("(dd)(dd)(dd)") // date (yymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d+.d+)([NS]),") // latitude
+ .number("(d+.d+)([EW]),") // longitude
+ .number("(d+.d+),") // speed
+ .number("(d+.d+)") // course
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ ByteBuf header = Unpooled.wrappedBuffer(DataConverter.parseHex(sentence.substring(0, 16)));
+ sentence = sentence.substring(16, sentence.length() - 4);
+
+ header.readUnsignedByte(); // header
+ header.readUnsignedInt(); // length
+ int type = header.readUnsignedByte();
+ header.readUnsignedShort(); // index
+
+ if (type == 0x20) {
+
+ getDeviceSession(channel, remoteAddress, sentence.split(",")[1].substring(5));
+
+ } else if (type == 0xA0) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ Network network = new Network();
+
+ for (String line : sentence.split("\r\n")) {
+
+ if (line.startsWith("GPS")) {
+
+ Parser parser = new Parser(PATTERN_GPS, line);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ position.setValid(parser.next().equals("A"));
+ position.setTime(parser.nextDateTime());
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
+
+ } else if (line.startsWith("WIFI")) {
+
+ for (String item : line.substring(line.indexOf(',') + 1).split("&")) {
+ String[] values = item.split("\\|");
+ network.addWifiAccessPoint(
+ WifiAccessPoint.from(values[1].replace('-', ':'), Integer.parseInt(values[2])));
+ }
+
+ } else if (line.startsWith("LBS")) {
+
+ String[] values = line.substring("LBS:".length()).split(",");
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(values[0]),
+ Integer.parseInt(values[1]),
+ Integer.parseInt(values[3]),
+ Integer.parseInt(values[2])));
+
+ }
+
+ }
+
+ if (!network.getCellTowers().isEmpty() || !network.getWifiAccessPoints().isEmpty()) {
+ position.setNetwork(network);
+ }
+
+ if (position.getFixTime() == null) {
+ getLastLocation(position, null);
+ }
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
index 283dbeb37..ec569b71b 100644
--- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2020 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.
@@ -45,7 +45,7 @@ import java.util.regex.Pattern;
public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
- private boolean ignoreFixTime;
+ private final boolean ignoreFixTime;
public Gl200TextProtocolDecoder(Protocol protocol) {
super(protocol);
@@ -190,6 +190,12 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.expression(PATTERN_LOCATION.pattern())
.expression(")+)")
.groupBegin()
+ .number("d{1,2},,")
+ .number("(d{1,3}),") // battery
+ .number("[01],") // mode
+ .number("(?:[01])?,") // motion
+ .number("(?:-?d{1,2}.d)?,") // temperature
+ .or()
.number("(d{1,7}.d)?,") // odometer
.number("(d{5}:dd:dd)?,") // hour meter
.number("(x+)?,") // adc 1
@@ -226,6 +232,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.expression("((?:")
.expression(PATTERN_LOCATION.pattern())
.expression(")+)")
+ .groupBegin()
.number("(d{1,7}.d)?,") // odometer
.number("(d{5}:dd:dd)?,") // hour meter
.number("(x+)?,") // adc 1
@@ -233,6 +240,11 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(d{1,3})?,") // battery
.number("(?:(xx)(xx)(xx))?,") // device status
.expression("(.*)") // additional data
+ .or()
+ .number("d*,,")
+ .number("(d+),") // battery
+ .any()
+ .groupEnd()
.number("(dddd)(dd)(dd)") // date (yyyymmdd)
.number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
.text(",")
@@ -789,8 +801,14 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
} else if (BitUtil.check(ignition, 5)) {
position.set(Position.KEY_IGNITION, true);
}
- position.set(Position.KEY_INPUT, parser.nextHexInt());
- position.set(Position.KEY_OUTPUT, parser.nextHexInt());
+ int input = parser.nextHexInt();
+ int output = parser.nextHexInt();
+ position.set(Position.KEY_INPUT, input);
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(input, 1));
+ position.set(Position.PREFIX_IN + 2, BitUtil.check(input, 2));
+ position.set(Position.KEY_OUTPUT, output);
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(output, 0));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(output, 1));
}
}
@@ -835,6 +853,10 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
}
if (parser.hasNext()) {
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ }
+
+ if (parser.hasNext()) {
position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
}
position.set(Position.KEY_HOURS, parseHours(parser.next()));
@@ -897,49 +919,58 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
if (power != null) {
position.set(Position.KEY_POWER, power * 0.001);
}
- position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
- position.set(Position.KEY_HOURS, parseHours(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);
+ if (parser.hasNext(9)) {
- int index = 0;
- String[] data = parser.next().split(",");
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ position.set(Position.KEY_HOURS, parseHours(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());
- index += 1; // device type
+ decodeStatus(position, parser);
- if (BitUtil.check(mask, 0)) {
- index += 1; // digital fuel sensor data
- }
+ int index = 0;
+ String[] data = parser.next().split(",");
+
+ index += 1; // device type
+
+ if (BitUtil.check(mask, 0)) {
+ index += 1; // digital fuel sensor data
+ }
- if (BitUtil.check(mask, 1)) {
- int deviceCount = Integer.parseInt(data[index++]);
- for (int i = 1; i <= deviceCount; i++) {
- index += 1; // id
- index += 1; // type
- if (!data[index++].isEmpty()) {
- position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625);
+ if (BitUtil.check(mask, 1)) {
+ int deviceCount = Integer.parseInt(data[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index += 1; // id
+ index += 1; // type
+ if (!data[index++].isEmpty()) {
+ position.set(Position.PREFIX_TEMP + i, (short) Integer.parseInt(data[index - 1], 16) * 0.0625);
+ }
}
}
- }
- if (BitUtil.check(mask, 2)) {
- index += 1; // can data
- }
+ if (BitUtil.check(mask, 2)) {
+ index += 1; // can data
+ }
- if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) {
- int deviceCount = Integer.parseInt(data[index++]);
- for (int i = 1; i <= deviceCount; i++) {
- index += 1; // type
- if (BitUtil.check(mask, 3)) {
- position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++]));
- }
- if (BitUtil.check(mask, 4)) {
- index += 1; // volume
+ if (BitUtil.check(mask, 3) || BitUtil.check(mask, 4)) {
+ int deviceCount = Integer.parseInt(data[index++]);
+ for (int i = 1; i <= deviceCount; i++) {
+ index += 1; // type
+ if (BitUtil.check(mask, 3)) {
+ position.set(Position.KEY_FUEL_LEVEL, Double.parseDouble(data[index++]));
+ }
+ if (BitUtil.check(mask, 4)) {
+ index += 1; // volume
+ }
}
}
+
+ }
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
}
decodeDeviceTime(position, parser);
diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java
index 7243be72a..e86b5dc30 100644
--- a/src/main/java/org/traccar/protocol/GlobalSatProtocol.java
+++ b/src/main/java/org/traccar/protocol/GlobalSatProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 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,16 +21,22 @@ import org.traccar.BaseProtocol;
import org.traccar.CharacterDelimiterFrameDecoder;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.model.Command;
public class GlobalSatProtocol extends BaseProtocol {
public GlobalSatProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_ALARM_DISMISS,
+ Command.TYPE_OUTPUT_CONTROL);
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "!\r\n", "!"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new GlobalSatProtocolEncoder(GlobalSatProtocol.this));
pipeline.addLast(new GlobalSatProtocolDecoder(GlobalSatProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/GlobalSatProtocolEncoder.java b/src/main/java/org/traccar/protocol/GlobalSatProtocolEncoder.java
new file mode 100644
index 000000000..4f56274da
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GlobalSatProtocolEncoder.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 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.traccar.Protocol;
+import org.traccar.StringProtocolEncoder;
+import org.traccar.helper.Checksum;
+import org.traccar.model.Command;
+
+public class GlobalSatProtocolEncoder extends StringProtocolEncoder {
+
+ public GlobalSatProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ String formattedCommand = null;
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ formattedCommand = formatCommand(
+ command, "GSC,%s,%s", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
+ break;
+ case Command.TYPE_ALARM_DISMISS:
+ formattedCommand = formatCommand(
+ command, "GSC,%s,Na", Command.KEY_UNIQUE_ID);
+ break;
+ case Command.TYPE_OUTPUT_CONTROL:
+ formattedCommand = formatCommand(
+ command, "GSC,%s,Lo(%s,%s)", Command.KEY_UNIQUE_ID, Command.KEY_INDEX, Command.KEY_DATA);
+ break;
+ default:
+ break;
+ }
+
+ if (formattedCommand != null) {
+ return formattedCommand + Checksum.nmea(formattedCommand) + '!';
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
index de23ea170..b742d0cac 100644
--- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
@@ -60,9 +60,9 @@ import java.util.List;
public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
- private DocumentBuilder documentBuilder;
- private XPath xPath;
- private XPathExpression messageExpression;
+ private final DocumentBuilder documentBuilder;
+ private final XPath xPath;
+ private final XPathExpression messageExpression;
public GlobalstarProtocolDecoder(Protocol protocol) {
super(protocol);
@@ -161,17 +161,20 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
position.setLongitude(position.getLongitude() - 360);
}
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ int speed = buf.readUnsignedByte();
+ position.setSpeed(UnitsConverter.knotsFromKph(speed));
position.set("batteryReplace", BitUtil.check(buf.readUnsignedByte(), 7));
- positions.add(position);
+ if (speed != 0xff) {
+ positions.add(position);
+ }
}
}
sendResponse(channel, document.getFirstChild().getAttributes().getNamedItem("messageID").getNodeValue());
- return positions;
+ return !positions.isEmpty() ? positions : null;
}
}
diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
index e00d83061..9b672cacc 100644
--- a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -408,7 +408,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
}
}
- if (sentence.substring(21, 21 + 2).equals("vr")) {
+ if (sentence.startsWith("vr", 21)) {
return decodePhoto(channel, remoteAddress, sentence);
} else if (sentence.substring(21, 21 + 3).contains("OBD")) {
return decodeObd(channel, remoteAddress, sentence);
diff --git a/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java
index cc187225b..c158d3212 100644
--- a/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GpsGateProtocolDecoder.java
@@ -69,7 +69,8 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder {
private void send(Channel channel, SocketAddress remoteAddress, String message) {
if (channel != null) {
- channel.writeAndFlush(new NetworkMessage(message + Checksum.nmea(message) + "\r\n", remoteAddress));
+ channel.writeAndFlush(new NetworkMessage(
+ message + Checksum.nmea(message.substring(1)) + "\r\n", remoteAddress));
}
}
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
index 2c2ca7365..e003db51a 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -59,6 +59,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_LBS = 0x11;
public static final int MSG_GPS_LBS_1 = 0x12;
public static final int MSG_GPS_LBS_2 = 0x22;
+ public static final int MSG_GPS_LBS_3 = 0x37;
public static final int MSG_STATUS = 0x13;
public static final int MSG_SATELLITE = 0x14;
public static final int MSG_STRING = 0x15;
@@ -87,7 +88,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_COMMAND_2 = 0x82;
public static final int MSG_TIME_REQUEST = 0x8A;
public static final int MSG_INFO = 0x94;
- public static final int MSG_RFID = 0x9B;
+ public static final int MSG_SERIAL = 0x9B;
public static final int MSG_STRING_INFO = 0x21;
public static final int MSG_GPS_2 = 0xA0;
public static final int MSG_LBS_2 = 0xA1;
@@ -114,6 +115,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
case MSG_GPS:
case MSG_GPS_LBS_1:
case MSG_GPS_LBS_2:
+ case MSG_GPS_LBS_3:
case MSG_GPS_LBS_STATUS_1:
case MSG_GPS_LBS_STATUS_2:
case MSG_GPS_LBS_STATUS_3:
@@ -134,6 +136,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
case MSG_LBS_STATUS:
case MSG_GPS_LBS_1:
case MSG_GPS_LBS_2:
+ case MSG_GPS_LBS_3:
case MSG_GPS_LBS_STATUS_1:
case MSG_GPS_LBS_STATUS_2:
case MSG_GPS_LBS_STATUS_3:
@@ -736,57 +739,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else if (isSupported(type)) {
- if (hasGps(type)) {
- decodeGps(position, buf, false, deviceSession.getTimeZone());
- } else {
- getLastLocation(position, null);
- }
-
- if (hasLbs(type)) {
- decodeLbs(position, buf, hasStatus(type));
- }
-
- if (hasStatus(type)) {
- decodeStatus(position, buf);
- }
-
- if (type == MSG_GPS_LBS_1 && buf.readableBytes() > 75 + 6) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
- buf.readUnsignedByte(); // alarm
- buf.readUnsignedByte(); // swiped
- position.set("driverLicense", data.trim());
- }
-
- if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 2 + 6) {
- int mask = buf.readUnsignedShort();
- position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7));
- position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6));
- if (BitUtil.check(mask, 8 + 4)) {
- int value = BitUtil.to(mask, 8 + 1);
- if (BitUtil.check(mask, 8 + 1)) {
- value = -value;
- }
- position.set(Position.PREFIX_TEMP + 1, value);
- } else {
- int value = BitUtil.to(mask, 8 + 2);
- if (BitUtil.check(mask, 8 + 5)) {
- position.set(Position.PREFIX_ADC + 1, value);
- } else {
- position.set(Position.PREFIX_ADC + 1, value * 0.1);
- }
- }
- }
-
- 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);
- }
-
- if (buf.readableBytes() == 4 + 6) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
- }
+ decodeBasicUniversal(buf, deviceSession, type, position);
} else if (type == MSG_ALARM) {
boolean extendedAlarm = dataLength > 7;
@@ -850,6 +803,80 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private void decodeBasicUniversal(ByteBuf buf, DeviceSession deviceSession, int type, Position position) {
+
+ if (hasGps(type)) {
+ decodeGps(position, buf, false, deviceSession.getTimeZone());
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (hasLbs(type)) {
+ decodeLbs(position, buf, hasStatus(type));
+ }
+
+ if (hasStatus(type)) {
+ decodeStatus(position, buf);
+ }
+
+ if (type == MSG_GPS_LBS_1 && buf.readableBytes() > 75 + 6) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
+ buf.readUnsignedByte(); // alarm
+ buf.readUnsignedByte(); // swiped
+ position.set("driverLicense", data.trim());
+ }
+
+ if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 2 + 6) {
+ int mask = buf.readUnsignedShort();
+ position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7));
+ position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6));
+ if (BitUtil.check(mask, 8 + 4)) {
+ int value = BitUtil.to(mask, 8 + 1);
+ if (BitUtil.check(mask, 8 + 1)) {
+ value = -value;
+ }
+ position.set(Position.PREFIX_TEMP + 1, value);
+ } else {
+ int value = BitUtil.to(mask, 8 + 2);
+ if (BitUtil.check(mask, 8 + 5)) {
+ position.set(Position.PREFIX_ADC + 1, value);
+ } else {
+ position.set(Position.PREFIX_ADC + 1, value * 0.1);
+ }
+ }
+ }
+
+ if ((type == MSG_GPS_LBS_2 || type == MSG_GPS_LBS_3) && 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);
+ }
+
+ if (type == MSG_GPS_LBS_3) {
+ int module = buf.readUnsignedShort();
+ int length = buf.readUnsignedByte();
+ switch (module) {
+ case 0x0027:
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+ break;
+ case 0x002E:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ break;
+ case 0x003B:
+ position.setAccuracy(buf.readUnsignedShort() * 0.01);
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
+ }
+
+ if (buf.readableBytes() == 4 + 6) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ }
+ }
+
private Object decodeExtended(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
@@ -1026,18 +1053,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return position;
- } else if (type == MSG_RFID) {
-
- getLastLocation(position, null);
-
- buf.readUnsignedByte(); // external device type code
- buf.readUnsignedByte(); // card type
- position.set(
- Position.KEY_DRIVER_UNIQUE_ID,
- buf.readCharSequence(buf.readableBytes() - 9, StandardCharsets.US_ASCII).toString());
-
- return position;
-
} else if (type == MSG_GPS_MODULAR) {
return decodeExtendedModular(buf, deviceSession);
@@ -1205,6 +1220,27 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type == MSG_SERIAL) {
+
+ position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, null);
+
+ buf.readUnsignedByte(); // external device type code
+ int length = buf.readableBytes() - 9; // line break + checksum + index + checksum + footer
+ if (length < 8) {
+ position.set(
+ Position.PREFIX_TEMP + 1,
+ Double.parseDouble(buf.readCharSequence(length - 1, StandardCharsets.US_ASCII).toString()));
+ } else {
+ buf.readUnsignedByte(); // card type
+ position.set(
+ Position.KEY_DRIVER_UNIQUE_ID,
+ buf.readCharSequence(length - 1, StandardCharsets.US_ASCII).toString());
+ }
+
+ return position;
+
}
return null;
diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
index 8d39abead..f1d146bda 100644
--- a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
@@ -190,6 +190,9 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
position.set(
Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString());
break;
+ case 0x0011:
+ position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 0.05);
+ break;
case 0x0020:
String[] cells = buf.readCharSequence(
length, StandardCharsets.US_ASCII).toString().split("\\+");
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
index 841e2235d..b9b156f93 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -57,13 +57,17 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
public static final int RESULT_SUCCESS = 0;
- public static ByteBuf formatMessage(int type, ByteBuf id, ByteBuf data) {
+ public static ByteBuf formatMessage(int type, ByteBuf id, boolean shortIndex, ByteBuf data) {
ByteBuf buf = Unpooled.buffer();
buf.writeByte(0x7e);
buf.writeShort(type);
buf.writeShort(data.readableBytes());
buf.writeBytes(id);
- buf.writeShort(0); // index
+ if (shortIndex) {
+ buf.writeByte(1);
+ } else {
+ buf.writeShort(1);
+ }
buf.writeBytes(data);
data.release();
buf.writeByte(Checksum.xor(buf.nioBuffer(1, buf.readableBytes() - 1)));
@@ -79,18 +83,18 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
response.writeShort(type);
response.writeByte(RESULT_SUCCESS);
channel.writeAndFlush(new NetworkMessage(
- formatMessage(MSG_GENERAL_RESPONSE, id, response), remoteAddress));
+ formatMessage(MSG_GENERAL_RESPONSE, id, false, response), remoteAddress));
}
}
private void sendGeneralResponse2(
- Channel channel, SocketAddress remoteAddress, ByteBuf id, int index) {
+ Channel channel, SocketAddress remoteAddress, ByteBuf id, int type) {
if (channel != null) {
ByteBuf response = Unpooled.buffer();
- response.writeShort(index);
+ response.writeShort(type);
response.writeByte(RESULT_SUCCESS);
channel.writeAndFlush(new NetworkMessage(
- formatMessage(MSG_GENERAL_RESPONSE_2, id, response), remoteAddress));
+ formatMessage(MSG_GENERAL_RESPONSE_2, id, true, response), remoteAddress));
}
}
@@ -161,7 +165,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
response.writeByte(RESULT_SUCCESS);
response.writeBytes(ByteBufUtil.hexDump(id).getBytes(StandardCharsets.US_ASCII));
channel.writeAndFlush(new NetworkMessage(
- formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, response), remoteAddress));
+ formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress));
}
} else if (type == MSG_TERMINAL_AUTH) {
@@ -170,12 +174,14 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
} else if (type == MSG_LOCATION_REPORT) {
+ sendGeneralResponse(channel, remoteAddress, id, type, index);
+
return decodeLocation(deviceSession, buf);
} else if (type == MSG_LOCATION_REPORT_2 || type == MSG_LOCATION_REPORT_BLIND) {
if (BitUtil.check(attribute, 15)) {
- sendGeneralResponse2(channel, remoteAddress, id, index);
+ sendGeneralResponse2(channel, remoteAddress, id, type);
}
return decodeLocation2(deviceSession, buf, type);
@@ -296,11 +302,24 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
case 0xD3:
position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1);
break;
+ case 0xD4:
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ break;
+ case 0xD5:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+ break;
+ case 0xDA:
+ buf.readUnsignedShort(); // string cut count
+ int deviceStatus = buf.readUnsignedByte();
+ position.set("string", BitUtil.check(deviceStatus, 0));
+ position.set(Position.KEY_MOTION, BitUtil.check(deviceStatus, 2));
+ position.set("cover", BitUtil.check(deviceStatus, 3));
+ break;
case 0xEB:
while (buf.readerIndex() < endIndex) {
- int tenetLength = buf.readUnsignedShort();
- int tenetType = buf.readUnsignedShort();
- switch (tenetType) {
+ int extendedLength = buf.readUnsignedShort();
+ int extendedType = buf.readUnsignedShort();
+ switch (extendedType) {
case 0x0001:
position.set("fuel1", buf.readUnsignedShort() * 0.1);
buf.readUnsignedByte(); // unused
@@ -309,8 +328,11 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set("fuel2", Double.parseDouble(
buf.readCharSequence(6, StandardCharsets.US_ASCII).toString()));
break;
+ case 0x00CE:
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+ break;
default:
- buf.skipBytes(tenetLength - 2);
+ buf.skipBytes(extendedLength - 2);
break;
}
}
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java
index 40d07230d..55c1e0c3b 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 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.
@@ -50,22 +50,22 @@ public class HuabaoProtocolEncoder extends BaseProtocolEncoder {
data.writeByte(0x01);
data.writeBytes(time);
return HuabaoProtocolDecoder.formatMessage(
- HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, data);
+ HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, false, data);
} else {
data.writeByte(0xf0);
return HuabaoProtocolDecoder.formatMessage(
- HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, data);
+ HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, false, data);
}
case Command.TYPE_ENGINE_RESUME:
if (alternative) {
data.writeByte(0x00);
data.writeBytes(time);
return HuabaoProtocolDecoder.formatMessage(
- HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, data);
+ HuabaoProtocolDecoder.MSG_OIL_CONTROL, id, false, data);
} else {
data.writeByte(0xf1);
return HuabaoProtocolDecoder.formatMessage(
- HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, data);
+ HuabaoProtocolDecoder.MSG_TERMINAL_CONTROL, id, false, data);
}
default:
return null;
diff --git a/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java
index 897606270..930d4f23b 100644
--- a/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/IntellitracProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2020 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.
@@ -44,7 +44,7 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.?d*),") // course
.number("(-?d+.?d*),") // altitude
.number("(d+),") // satellites
- .number("(d+),") // index
+ .number("(d+),") // event
.number("(d+),") // input
.number("(d+),?") // output
.number("(d+.d+)?,?") // adc1
@@ -65,6 +65,30 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private String decodeAlarm(int value) {
+ switch (value) {
+ case 164:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 165:
+ return Position.ALARM_GEOFENCE_EXIT;
+ case 168:
+ case 169:
+ return Position.ALARM_LOW_POWER;
+ case 170:
+ return Position.ALARM_POWER_OFF;
+ case 176:
+ return Position.ALARM_POWER_RESTORED;
+ case 180:
+ return Position.ALARM_FALL_DOWN;
+ case 225:
+ return Position.ALARM_JAMMING;
+ case 995:
+ return Position.ALARM_SOS;
+ default:
+ return null;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -74,12 +98,12 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- Position position = new Position(getProtocolName());
-
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
position.setTime(parser.nextDateTime());
@@ -92,7 +116,11 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(parser.nextDouble());
position.set(Position.KEY_SATELLITES, parser.nextInt());
- position.set(Position.KEY_INDEX, parser.nextLong());
+
+ int event = parser.nextInt();
+ position.set(Position.KEY_ALARM, decodeAlarm(event));
+ position.set(Position.KEY_EVENT, event);
+
position.set(Position.KEY_INPUT, parser.nextInt());
position.set(Position.KEY_OUTPUT, parser.nextInt());
@@ -100,16 +128,18 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + 2, parser.nextDouble());
// J1939 data
- position.set(Position.KEY_OBD_SPEED, parser.nextInt());
- position.set(Position.KEY_RPM, parser.nextInt());
- position.set("coolant", parser.nextInt());
- position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
- position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt());
- position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
- position.set("chargerPressure", parser.nextInt());
- position.set("tpl", parser.nextInt());
- position.set(Position.KEY_AXLE_WEIGHT, parser.nextInt());
- position.set(Position.KEY_OBD_ODOMETER, parser.nextInt());
+ if (parser.hasNext(10)) {
+ position.set(Position.KEY_OBD_SPEED, parser.nextInt());
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set("coolant", parser.nextInt());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+ position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+ position.set("chargerPressure", parser.nextInt());
+ position.set("tpl", parser.nextInt());
+ position.set(Position.KEY_AXLE_WEIGHT, parser.nextInt());
+ position.set(Position.KEY_OBD_ODOMETER, parser.nextInt());
+ }
return position;
}
diff --git a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
index f66669a98..94c9a3038 100644
--- a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
@@ -90,6 +90,10 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
.number("(-?d+),") // tilt x
.or()
.number("d+,") // index
+ .number("(d+.?d*),") // odometer
+ .number("x+,") // checksum
+ .or()
+ .number("d+,") // index
.number("(d+.?d*),") // adc1
.number("(d+.?d*),") // adc2
.groupEnd("?")
@@ -244,6 +248,10 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
position.set("tiltX", parser.nextInt());
}
+ if (parser.hasNext()) {
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
+ }
+
if (parser.hasNext(2)) {
position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
position.set(Position.PREFIX_ADC + 2, parser.nextDouble());
diff --git a/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java
index 9868de435..5b5eb7d60 100644
--- a/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/L100ProtocolDecoder.java
@@ -138,7 +138,7 @@ public class L100ProtocolDecoder extends BaseProtocolDecoder {
String sentence = (String) msg;
if (sentence.startsWith("L") || sentence.startsWith("H")) {
- if (sentence.substring(2, 8).equals("ATLOBD")) {
+ if (sentence.startsWith("ATLOBD", 2)) {
return decodeObdData(channel, remoteAddress, sentence);
} else {
return decodeObdLocation(channel, remoteAddress, sentence);
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
index 0c72568f3..4abb75025 100644
--- a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
@@ -152,7 +152,7 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
if (responseCode != null) {
String response = "$AVCFG," + devicePassword + "," + responseCode;
- response += Checksum.nmea(response) + "\r\n";
+ response += Checksum.nmea(response.substring(1)) + "\r\n";
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
@@ -163,7 +163,7 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
if (Character.isLowerCase(status.charAt(0))) {
String response = "$EAVACK," + event + "," + checksum;
- response += Checksum.nmea(response) + "\r\n";
+ response += Checksum.nmea(response.substring(1)) + "\r\n";
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
index 0c9f8ebb8..aaa5a70f7 100644
--- a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
@@ -29,7 +29,7 @@ public class LaipacProtocolEncoder extends StringProtocolEncoder {
@Override
protected String formatCommand(Command command, String format, String... keys) {
String sentence = super.formatCommand(command, "$" + format, keys);
- sentence += Checksum.nmea(sentence) + "\r\n";
+ sentence += Checksum.nmea(sentence.substring(1)) + "\r\n";
return sentence;
}
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
index 529496928..c43f1ea83 100644
--- a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -177,8 +177,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
position.setNetwork(new Network(CellTower.from(
parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), rssi)));
- position.set(Position.KEY_OUTPUT, parser.nextHexInt());
position.set(Position.KEY_INPUT, parser.nextHexInt());
+ position.set(Position.KEY_OUTPUT, parser.nextHexInt());
for (int i = 1; i <= 3; i++) {
position.set(Position.PREFIX_ADC + i, parser.nextHexInt());
@@ -421,6 +421,22 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
case 0x1A:
position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.01);
break;
+ case 0x91:
+ case 0x92:
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShortLE());
+ break;
+ case 0x98:
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedShortLE());
+ break;
+ case 0x99:
+ position.set(Position.KEY_RPM, buf.readUnsignedShortLE());
+ break;
+ case 0x9C:
+ position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedShortLE());
+ break;
+ case 0xC9:
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShortLE());
+ break;
default:
buf.readUnsignedShortLE();
break;
@@ -440,9 +456,18 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
case 0x04:
position.setTime(new Date((946684800 + buf.readUnsignedIntLE()) * 1000)); // 2000-01-01
break;
+ case 0x0C:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ break;
case 0x0D:
position.set("runtime", buf.readUnsignedIntLE());
break;
+ case 0xA0:
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedIntLE() * 0.001);
+ break;
+ case 0xA2:
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedIntLE() * 0.01);
+ break;
default:
buf.readUnsignedIntLE();
break;
diff --git a/src/main/java/org/traccar/protocol/MictrackProtocol.java b/src/main/java/org/traccar/protocol/MictrackProtocol.java
index c8d64fd81..9fd9666e4 100644
--- a/src/main/java/org/traccar/protocol/MictrackProtocol.java
+++ b/src/main/java/org/traccar/protocol/MictrackProtocol.java
@@ -15,7 +15,6 @@
*/
package org.traccar.protocol;
-import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
@@ -28,7 +27,6 @@ public class MictrackProtocol extends BaseProtocol {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new MictrackProtocolDecoder(MictrackProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
index 0f815ca7b..5ea9f148c 100644
--- a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
@@ -66,6 +66,8 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder {
private String decodeAlarm(int event) {
switch (event) {
+ case 0:
+ return Position.ALARM_POWER_ON;
case 5:
return Position.ALARM_SOS;
case 8:
@@ -209,8 +211,12 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder {
}
private Object decodeLowAltitude(
- Channel channel, SocketAddress remoteAddress, String sentence) throws Exception {
- String deviceId = sentence.substring(0, sentence.indexOf("$"));
+ Channel channel, SocketAddress remoteAddress, String sentence) {
+ int separator = sentence.indexOf("$");
+ if (separator < 0) {
+ return null;
+ }
+ String deviceId = sentence.substring(0, separator);
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId);
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
index b8ab134c5..5b04992ec 100644
--- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 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.
@@ -34,6 +34,10 @@ import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
@@ -100,18 +104,33 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
if (type == MSG_DATA) {
+ List<Position> positions = new LinkedList<>();
+ Set<Integer> keys = new HashSet<>();
+ boolean hasLocation = false;
Position position = new Position(getProtocolName());
+ DeviceSession deviceSession = null;
+
while (buf.isReadable()) {
int endIndex = buf.readUnsignedByte() + buf.readerIndex();
int key = buf.readUnsignedByte();
+
+ if (keys.contains(key)) {
+ if (!hasLocation) {
+ getLastLocation(position, null);
+ }
+ positions.add(position);
+ keys.clear();
+ hasLocation = false;
+ position = new Position(getProtocolName());
+ }
+ keys.add(key);
+
switch (key) {
case 0x01:
- DeviceSession deviceSession = getDeviceSession(
+ deviceSession = getDeviceSession(
channel, remoteAddress, buf.readCharSequence(15, StandardCharsets.US_ASCII).toString());
- if (deviceSession == null) {
- return null;
- }
+
position.setDeviceId(deviceSession.getDeviceId());
break;
case 0x02:
@@ -122,6 +141,7 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
break;
case 0x20:
+ hasLocation = true;
position.setLatitude(buf.readIntLE() * 0.0000001);
position.setLongitude(buf.readIntLE() * 0.0000001);
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
@@ -169,6 +189,10 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY_LEVEL, BitUtil.from(status, 24));
position.set(Position.KEY_STATUS, status);
break;
+ case 0x30:
+ buf.readUnsignedInt(); // timestamp
+ position.set(Position.KEY_STEPS, buf.readUnsignedInt());
+ break;
case 0x40:
buf.readUnsignedIntLE(); // timestamp
int heartRate = buf.readUnsignedByte();
@@ -182,11 +206,20 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
buf.readerIndex(endIndex);
}
- if (!position.getAttributes().containsKey(Position.KEY_SATELLITES)) {
+ if (!hasLocation) {
getLastLocation(position, null);
}
+ positions.add(position);
+
+ if (deviceSession != null) {
+ for (Position p : positions) {
+ p.setDeviceId(deviceSession.getDeviceId());
+ }
+ } else {
+ return null;
+ }
- return position.getDeviceId() > 0 ? position : null;
+ return positions;
}
diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocol.java b/src/main/java/org/traccar/protocol/MoovboxProtocol.java
new file mode 100644
index 000000000..7b554266f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MoovboxProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 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 io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.codec.http.HttpResponseEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class MoovboxProtocol extends BaseProtocol {
+
+ public MoovboxProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(65535));
+ pipeline.addLast(new MoovboxProtocolDecoder(MoovboxProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java b/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java
new file mode 100644
index 000000000..3116d073c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MoovboxProtocolDecoder.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2020 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 com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream;
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.model.Position;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+import java.net.SocketAddress;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class MoovboxProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ private final DocumentBuilder documentBuilder;
+ private final XPath xPath;
+ private final XPathExpression messageExpression;
+
+ public MoovboxProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ try {
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+ builderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+ builderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ builderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
+ builderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ builderFactory.setXIncludeAware(false);
+ builderFactory.setExpandEntityReferences(false);
+ documentBuilder = builderFactory.newDocumentBuilder();
+ xPath = XPathFactory.newInstance().newXPath();
+ messageExpression = xPath.compile("//gps/coordinates/coordinate");
+ } catch (ParserConfigurationException | XPathExpressionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+
+ Document document = documentBuilder.parse(new ByteBufferBackedInputStream(request.content().nioBuffer()));
+
+ String id = document.getDocumentElement().getAttribute("id");
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ NodeList nodes = (NodeList) messageExpression.evaluate(document, XPathConstants.NODESET);
+ List<Position> positions = new LinkedList<>();
+
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setTime(new Date(Long.parseLong(xPath.evaluate("time", node)) * 1000));
+ position.setLatitude(Double.parseDouble(xPath.evaluate("longitude", node)));
+ position.setLongitude(Double.parseDouble(xPath.evaluate("latitude", node)));
+ position.setAltitude(Double.parseDouble(xPath.evaluate("altitude", node)));
+ position.setSpeed(Double.parseDouble(xPath.evaluate("speed", node)));
+
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(xPath.evaluate("satellites", node)));
+
+ positions.add(position);
+ }
+
+ sendResponse(channel, HttpResponseStatus.OK);
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NetProtocol.java b/src/main/java/org/traccar/protocol/NetProtocol.java
new file mode 100644
index 000000000..c114d19fc
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NetProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 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 io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class NetProtocol extends BaseProtocol {
+
+ public NetProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '!'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new NetProtocolDecoder(NetProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NetProtocolDecoder.java b/src/main/java/org/traccar/protocol/NetProtocolDecoder.java
new file mode 100644
index 000000000..61cb29b6b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NetProtocolDecoder.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2020 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 io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class NetProtocolDecoder extends BaseProtocolDecoder {
+
+ public NetProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("@L")
+ .number("ddd")
+ .number("(d{15})") // imei
+ .number("xx")
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .number("(dd)(dd)(dd)") // time (hhmmss)
+ .number("(x)") // flags
+ .number("(dd)(dd)(dddd)") // latitude
+ .number("(ddd)(dd)(dddd)") // longitude
+ .number("(x{8})") // status
+ .number("(x{4})") // speed
+ .number("(x{6})") // odometer
+ .number("(xxx)") // course
+ .number("(xxx)") // alarm
+ .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(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+
+ int flags = parser.nextHexInt();
+
+ position.setValid(BitUtil.check(flags, 3));
+ int hemisphereLatitude = BitUtil.check(flags, 1) ? -1 : 1;
+ int hemisphereLongitude = BitUtil.check(flags, 0) ? -1 : 1;
+
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN) * hemisphereLatitude);
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN) * hemisphereLongitude);
+
+ position.set(Position.KEY_STATUS, parser.nextHexLong());
+ position.setSpeed(parser.nextHexInt() * 0.01);
+ position.set(Position.KEY_ODOMETER, parser.nextHexInt());
+ position.setCourse(parser.nextHexInt());
+
+ parser.nextHexInt(); // alarm
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java b/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java
index 58de0d38f..47c6e2ffd 100644
--- a/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NiotProtocolDecoder.java
@@ -31,6 +31,7 @@ import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
public class NiotProtocolDecoder extends BaseProtocolDecoder {
@@ -95,12 +96,65 @@ public class NiotProtocolDecoder extends BaseProtocolDecoder {
.setSecond(BcdUtil.readInteger(buf, 2));
position.setTime(dateBuilder.getDate());
- position.setValid(true);
position.setLatitude(readCoordinate(buf));
position.setLongitude(readCoordinate(buf));
- position.setSpeed(UnitsConverter.knotsFromKph(BcdUtil.readInteger(buf, 4)));
+ BcdUtil.readInteger(buf, 4); // reserved
position.setCourse(BcdUtil.readInteger(buf, 4));
+ int statusX = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(statusX, 7));
+ switch (BitUtil.between(statusX, 3, 5)) {
+ case 0b10:
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
+ break;
+ case 0b01:
+ position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
+ break;
+ default:
+ break;
+ }
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+
+ int statusA = buf.readUnsignedByte();
+ position.set(Position.KEY_IGNITION, !BitUtil.check(statusA, 7));
+ if (!BitUtil.check(statusA, 6)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ }
+
+ buf.readUnsignedByte(); // statusB
+ buf.readUnsignedByte(); // statusC
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.1);
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1);
+ buf.readUnsignedByte(); // speed limit
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ buf.readUnsignedByte(); // sensor speed
+ buf.readUnsignedByte(); // reserved
+ buf.readUnsignedByte(); // reserved
+
+ while (buf.readableBytes() > 4) {
+ int extendedLength = buf.readUnsignedShort();
+ int extendedType = buf.readUnsignedShort();
+ switch (extendedType) {
+ case 0x0001:
+ position.set(Position.KEY_ICCID,
+ buf.readCharSequence(20, StandardCharsets.US_ASCII).toString());
+ break;
+ case 0x0002:
+ int statusD = buf.readUnsignedByte();
+ position.set(Position.KEY_ALARM, BitUtil.check(statusD, 5) ? Position.ALARM_REMOVING : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(statusD, 4) ? Position.ALARM_TAMPERING : null);
+ buf.readUnsignedByte(); // run mode
+ buf.readUnsignedByte(); // reserved
+ break;
+ default:
+ buf.skipBytes(extendedLength - 2);
+ break;
+ }
+
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java
index 323d97fa3..509d14ae4 100644
--- a/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OwnTracksProtocolDecoder.java
@@ -110,7 +110,7 @@ public class OwnTracksProtocolDecoder extends BaseHttpProtocolDecoder {
if (root.containsKey("t")) {
String trigger = root.getString("t");
position.set("t", trigger);
- Integer reportType = -1;
+ int reportType = -1;
if (root.containsKey("rty")) {
reportType = root.getInt("rty");
}
@@ -148,8 +148,8 @@ public class OwnTracksProtocolDecoder extends BaseHttpProtocolDecoder {
}
if (root.containsKey("anum")) {
- Integer numberOfAnalogueInputs = root.getInt("anum");
- for (Integer i = 0; i < numberOfAnalogueInputs; i++) {
+ int numberOfAnalogueInputs = root.getInt("anum");
+ for (int i = 0; i < numberOfAnalogueInputs; i++) {
String indexString = String.format("%02d", i);
if (root.containsKey("adda-" + indexString)) {
position.set(Position.PREFIX_ADC + (i + 1), root.getString("adda-" + indexString));
diff --git a/src/main/java/org/traccar/protocol/PolteProtocol.java b/src/main/java/org/traccar/protocol/PolteProtocol.java
new file mode 100644
index 000000000..a3e548716
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PolteProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 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 io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.codec.http.HttpResponseEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class PolteProtocol extends BaseProtocol {
+
+ public PolteProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(65535));
+ pipeline.addLast(new PolteProtocolDecoder(PolteProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java
new file mode 100644
index 000000000..ce45abef6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PolteProtocolDecoder.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2020 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 io.netty.channel.Channel;
+import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.model.Position;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class PolteProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public PolteProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+ String content = request.content().toString(StandardCharsets.UTF_8);
+ JsonObject json = Json.createReader(new StringReader(content)).readObject();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, json.getString("ueToken"));
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+
+ if (json.containsKey("location")) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ JsonObject location = json.getJsonObject("location");
+
+ position.setValid(true);
+ position.setTime(new Date(location.getInt("detected_at") * 1000L));
+ position.setLatitude(location.getJsonNumber("latitude").doubleValue());
+ position.setLongitude(location.getJsonNumber("longitude").doubleValue());
+ position.setAltitude(location.getJsonNumber("altitude").doubleValue());
+
+ if (json.containsKey("report")) {
+ JsonObject report = json.getJsonObject("report");
+ position.set(Position.KEY_EVENT, report.getInt("event"));
+ if (report.containsKey("battery")) {
+ JsonObject battery = report.getJsonObject("battery");
+ position.set(Position.KEY_BATTERY_LEVEL, battery.getInt("level"));
+ position.set(Position.KEY_BATTERY, battery.getJsonNumber("voltage").doubleValue());
+ }
+ }
+
+ return position;
+
+ }
+
+ sendResponse(channel, HttpResponseStatus.OK);
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PstFrameEncoder.java b/src/main/java/org/traccar/protocol/PstFrameEncoder.java
new file mode 100644
index 000000000..5a9ede3e6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PstFrameEncoder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 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 io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToByteEncoder;
+
+public class PstFrameEncoder extends MessageToByteEncoder<ByteBuf> {
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) {
+
+ out.writeByte('(');
+ while (msg.isReadable()) {
+ int b = msg.readUnsignedByte();
+ if (b == 0x27 || b == 0x28 || b == 0x29) {
+ out.writeByte(0x27);
+ out.writeByte(b ^ 0x40);
+ } else {
+ out.writeByte(b);
+ }
+ }
+ out.writeByte(')');
+ }
+}
diff --git a/src/main/java/org/traccar/protocol/PstProtocol.java b/src/main/java/org/traccar/protocol/PstProtocol.java
index 0ed9affd8..d8c7008cb 100644
--- a/src/main/java/org/traccar/protocol/PstProtocol.java
+++ b/src/main/java/org/traccar/protocol/PstProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 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,20 +18,27 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.model.Command;
public class PstProtocol extends BaseProtocol {
public PstProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_ENGINE_RESUME);
addServer(new TrackerServer(true, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new PstProtocolEncoder(PstProtocol.this));
pipeline.addLast(new PstProtocolDecoder(PstProtocol.this));
}
});
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new PstFrameEncoder());
pipeline.addLast(new PstFrameDecoder());
+ pipeline.addLast(new PstProtocolEncoder(PstProtocol.this));
pipeline.addLast(new PstProtocolDecoder(PstProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java
index 62cc203d2..e3fe1af62 100644
--- a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java
@@ -38,6 +38,7 @@ public class PstProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_ACK = 0x00;
public static final int MSG_STATUS = 0x05;
+ public static final int MSG_COMMAND = 0x06;
private Date readDate(ByteBuf buf) {
long value = buf.readUnsignedInt();
@@ -61,21 +62,13 @@ public class PstProtocolDecoder extends BaseProtocolDecoder {
Channel channel, SocketAddress remoteAddress, long id, int version, long index, int type) {
if (channel != null) {
- ByteBuf content = Unpooled.buffer();
- content.writeInt((int) id);
- content.writeByte(version);
- content.writeInt((int) index);
- content.writeByte(MSG_ACK);
- content.writeByte(type);
-
- int checksum = Checksum.crc16(Checksum.CRC16_XMODEM, content.nioBuffer());
-
ByteBuf response = Unpooled.buffer();
- response.writeByte('(');
- response.writeBytes(content);
- content.release();
- response.writeShort(checksum);
- response.writeByte(')');
+ response.writeInt((int) id);
+ response.writeByte(version);
+ response.writeInt((int) index);
+ response.writeByte(MSG_ACK);
+ response.writeByte(type);
+ response.writeShort(Checksum.crc16(Checksum.CRC16_XMODEM, response.nioBuffer()));
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
diff --git a/src/main/java/org/traccar/protocol/PstProtocolEncoder.java b/src/main/java/org/traccar/protocol/PstProtocolEncoder.java
new file mode 100644
index 000000000..f3d193324
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PstProtocolEncoder.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020 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 io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.traccar.BaseProtocolEncoder;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+import org.traccar.model.Command;
+
+public class PstProtocolEncoder extends BaseProtocolEncoder {
+
+ public PstProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeContent(long deviceId, int type, int data1, int data2) {
+
+ ByteBuf buf = Unpooled.buffer();
+
+ buf.writeInt((int) Long.parseLong(getUniqueId(deviceId)));
+ buf.writeByte(0x06); // version
+
+ buf.writeInt(1); // index
+ buf.writeByte(PstProtocolDecoder.MSG_COMMAND);
+ buf.writeShort(type);
+ buf.writeShort(data1);
+ buf.writeShort(data2);
+
+ buf.writeShort(Checksum.crc16(Checksum.CRC16_XMODEM, buf.nioBuffer()));
+
+ return buf;
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ switch (command.getType()) {
+ case Command.TYPE_ENGINE_STOP:
+ return encodeContent(command.getDeviceId(), 0x0002, 0xffff, 0xffff);
+ case Command.TYPE_ENGINE_RESUME:
+ return encodeContent(command.getDeviceId(), 0x0001, 0xffff, 0xffff);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java
index 0afec67ad..ff92b51f1 100644
--- a/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Pt502ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2012 Luis Parada (luis.parada@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -55,8 +55,8 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
.expression("([EW]),")
.number("(d+.d+)?,") // speed
.number("(d+.d+)?,") // course
- .number("(dd)(dd)(dd),,,") // date (ddmmyy)
- .expression("./")
+ .number("(dd)(dd)(dd),,,?") // date (ddmmyy)
+ .expression(".?/")
.expression("([01])+,") // input
.expression("([01])+/") // output
.expression("([^/]+)?/") // adc
@@ -108,7 +108,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setValid(parser.next().equals("A"));
position.setLatitude(parser.nextCoordinate());
@@ -116,7 +116,7 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(parser.nextDouble(0));
position.setCourse(parser.nextDouble(0));
- dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setTime(dateBuilder.getDate());
position.set(Position.KEY_INPUT, parser.next());
diff --git a/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java
index b56a081c7..d87f77b84 100644
--- a/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RadarProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 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.
@@ -70,6 +70,7 @@ public class RadarProtocolDecoder extends BaseProtocolDecoder {
for (int index = 0; index < count; index++) {
Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_EVENT, buf.readUnsignedShort());
diff --git a/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java
index da36c2048..34417d95f 100644
--- a/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SpotProtocolDecoder.java
@@ -42,9 +42,9 @@ import java.util.List;
public class SpotProtocolDecoder extends BaseHttpProtocolDecoder {
- private DocumentBuilder documentBuilder;
- private XPath xPath;
- private XPathExpression messageExpression;
+ private final DocumentBuilder documentBuilder;
+ private final XPath xPath;
+ private final XPathExpression messageExpression;
public SpotProtocolDecoder(Protocol protocol) {
super(protocol);
diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
index 2d1613e03..7ba41ad56 100644
--- a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
@@ -20,11 +20,13 @@ import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.Protocol;
+import org.traccar.helper.DataConverter;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
+import org.traccar.protobuf.StarLinkMessage;
import java.net.SocketAddress;
import java.text.DateFormat;
@@ -47,8 +49,8 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
.number("xx") // checksum
.compile();
- private String[] dataTags;
- private DateFormat dateFormat;
+ private String format;
+ private String dateFormat;
public StarLinkProtocolDecoder(Protocol protocol) {
super(protocol);
@@ -60,13 +62,24 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
setDateFormat(Context.getConfig().getString(getProtocolName() + ".dateFormat", "yyMMddHHmmss"));
}
+ public String[] getFormat(long deviceId) {
+ return Context.getIdentityManager().lookupAttributeString(
+ deviceId, getProtocolName() + ".format", format, false, false).split(",");
+ }
+
public void setFormat(String format) {
- dataTags = format.split(",");
+ this.format = format;
+ }
+
+ public DateFormat getDateFormat(long deviceId) {
+ DateFormat dateFormat = new SimpleDateFormat(Context.getIdentityManager().lookupAttributeString(
+ deviceId, getProtocolName() + ".dateFormat", this.dateFormat, false, false));
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat;
}
public void setDateFormat(String dateFormat) {
- this.dateFormat = new SimpleDateFormat(dateFormat);
- this.dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ this.dateFormat = dateFormat;
}
private double parseCoordinate(String value) {
@@ -128,6 +141,9 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
Integer lac = null, cid = null;
int event = 0;
+ String[] dataTags = getFormat(deviceSession.getDeviceId());
+ DateFormat dateFormat = getDateFormat(deviceSession.getDeviceId());
+
for (int i = 0; i < Math.min(data.length, dataTags.length); i++) {
if (data[i].isEmpty()) {
continue;
@@ -186,6 +202,9 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
cid = Integer.parseInt(data[i]);
}
break;
+ case "#CSS#":
+ position.set(Position.KEY_RSSI, Integer.parseInt(data[i]));
+ break;
case "#VIN#":
position.set(Position.KEY_POWER, Double.parseDouble(data[i]));
break;
@@ -201,6 +220,32 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
case "#ENG#":
position.set("engine", data[i].equals("1"));
break;
+ case "#SATU#":
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(data[i]));
+ break;
+ case "#TS1#":
+ position.set("sensor1State", Integer.parseInt(data[i]));
+ break;
+ case "#TS2#":
+ position.set("sensor2State", Integer.parseInt(data[i]));
+ break;
+ case "#TD1#":
+ case "#TD2#":
+ StarLinkMessage.mEventReport_TDx message =
+ StarLinkMessage.mEventReport_TDx.parseFrom(DataConverter.parseBase64(data[i]));
+ position.set(
+ "sensor" + message.getSensorNumber() + "Id",
+ message.getSensorID());
+ position.set(
+ "sensor" + message.getSensorNumber() + "Temp",
+ message.getTemperature() * 0.1);
+ position.set(
+ "sensor" + message.getSensorNumber() + "Humidity",
+ message.getTemperature() * 0.1);
+ position.set(
+ "sensor" + message.getSensorNumber() + "Voltage",
+ message.getVoltage() * 0.001);
+ break;
default:
break;
}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
index 451b9ba32..76e3e6ecc 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -479,20 +479,21 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.setTime(dateFormat.parse(values[index++] + values[index++]));
}
+ CellTower cellTower = new CellTower();
if (BitUtil.check(mask, 6)) {
- index += 1; // cell
+ cellTower.setCellId(Long.parseLong(values[index++], 16));
}
-
if (BitUtil.check(mask, 7)) {
- index += 1; // mcc
+ cellTower.setMobileCountryCode(Integer.parseInt(values[index++]));
}
-
if (BitUtil.check(mask, 8)) {
- index += 1; // mnc
+ cellTower.setMobileNetworkCode(Integer.parseInt(values[index++]));
}
-
if (BitUtil.check(mask, 9)) {
- index += 1; // lac
+ cellTower.setLocationAreaCode(Integer.parseInt(values[index++], 16));
+ }
+ if (cellTower.getCellId() != null) {
+ position.setNetwork(new Network(cellTower));
}
if (BitUtil.check(mask, 10)) {
@@ -531,16 +532,26 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OUTPUT, Integer.parseInt(values[index++]));
}
- if (BitUtil.check(mask, 19)) {
- position.set("alertId", values[index++]);
- }
-
- if (BitUtil.check(mask, 20)) {
- position.set("alertModifier", values[index++]);
- }
-
- if (BitUtil.check(mask, 21)) {
- position.set("alertData", values[index++]);
+ if (type.equals("ALT")) {
+ if (BitUtil.check(mask, 19)) {
+ position.set("alertId", values[index++]);
+ }
+ if (BitUtil.check(mask, 20)) {
+ position.set("alertModifier", values[index++]);
+ }
+ if (BitUtil.check(mask, 21)) {
+ position.set("alertData", values[index++]);
+ }
+ } else {
+ if (BitUtil.check(mask, 19)) {
+ position.set("mode", Integer.parseInt(values[index++]));
+ }
+ if (BitUtil.check(mask, 20)) {
+ position.set("reason", Integer.parseInt(values[index++]));
+ }
+ if (BitUtil.check(mask, 21)) {
+ position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
+ }
}
if (BitUtil.check(mask, 22)) {
@@ -676,6 +687,24 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeTravelReport(Channel channel, SocketAddress remoteAddress, String[] values) {
+ int index = 1;
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, values[values.length - 1]);
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -693,6 +722,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
if (prefix.length() < 5) {
return decodeUniversal(channel, remoteAddress, values);
+ } else if (prefix.endsWith("HTE")) {
+ return decodeTravelReport(channel, remoteAddress, values);
} else if (prefix.startsWith("ST9")) {
return decode9(channel, remoteAddress, values);
} else if (prefix.startsWith("ST4")) {
diff --git a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
index b75addfae..230d29216 100644
--- a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2020 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.Protocol;
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;
@@ -109,6 +110,20 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_QZE = new PatternBuilder()
+ .text("QZE,")
+ .number("(d{15}),") // imei
+ .number("(d+),") // event
+ .number("(dd)(dd)(dddd),") // date (mmddyyyy)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(-?d+.d+),") // latitude
+ .number("(-?d+.d+),") // longitude
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .expression("([AV]),") // validity
+ .expression("([01])") // ignition
+ .compile();
+
private Position position = null;
private Position decodeGprmc(
@@ -256,6 +271,35 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeQze(Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_QZE, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_EVENT, parser.nextInt());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setCourse(parser.nextInt());
+ position.setValid(parser.next().equals("A"));
+
+ position.set(Position.KEY_IGNITION, parser.nextInt() > 0);
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -308,6 +352,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return decodeTrccr(deviceSession, sentence);
} else if (sentence.startsWith("$GPIOP")) {
return decodeGpiop(deviceSession, sentence);
+ } else if (sentence.startsWith("QZE")) {
+ return decodeQze(channel, remoteAddress, sentence);
}
return null;
diff --git a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
index 3331ebb71..5822d6b8f 100644
--- a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
@@ -230,10 +230,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
int battery = BcdUtil.readInteger(buf, 2);
- if (battery == 0) {
- battery = 100;
- }
- position.set(Position.KEY_BATTERY, battery);
+ position.set(Position.KEY_BATTERY_LEVEL, battery > 0 ? battery : 100);
}
@@ -284,7 +281,8 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
}
position.set(Position.KEY_G_SENSOR, "[" + accelerationX + "," + accelerationY + "," + accelerationZ + "]");
- position.set(Position.KEY_BATTERY_LEVEL, BcdUtil.readInteger(buf, 2));
+ int battery = BcdUtil.readInteger(buf, 2);
+ position.set(Position.KEY_BATTERY_LEVEL, battery > 0 ? battery : 100);
position.set(Position.KEY_DEVICE_TEMP, (int) buf.readByte());
position.set("lightSensor", BcdUtil.readInteger(buf, 2) * 0.1);
position.set(Position.KEY_BATTERY, BcdUtil.readInteger(buf, 2) * 0.1);
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 40b769869..0ea02b157 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -43,9 +43,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
private static final int IMAGE_PACKET_MAX = 2048;
- private boolean connectionless;
+ private final boolean connectionless;
private boolean extended;
- private Map<Long, ByteBuf> photos = new HashMap<>();
+ private final Map<Long, ByteBuf> photos = new HashMap<>();
public void setExtended(boolean extended) {
this.extended = extended;
@@ -208,7 +208,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_ADC + 2, readValue(buf, length, false));
break;
case 16:
- case 87:
position.set(Position.KEY_ODOMETER, readValue(buf, length, false));
break;
case 17:
@@ -223,9 +222,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
case 21:
position.set(Position.KEY_RSSI, readValue(buf, length, false));
break;
- case 24:
- readValue(buf, length, false); // speed
- break;
case 25:
case 26:
case 27:
@@ -255,34 +251,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
case 80:
position.set("workMode", readValue(buf, length, false));
break;
- case 81:
- position.set(Position.KEY_OBD_SPEED, readValue(buf, length, false));
- break;
- case 82:
- position.set(Position.KEY_THROTTLE, readValue(buf, length, false));
- break;
- case 83:
- position.set(Position.KEY_FUEL_USED, readValue(buf, length, false) * 0.1);
- break;
- case 84:
- position.set(Position.KEY_FUEL_LEVEL, readValue(buf, length, false) * 0.1);
- break;
- case 85:
- position.set(Position.KEY_RPM, readValue(buf, length, false));
- break;
case 90:
position.set(Position.KEY_DOOR, readValue(buf, length, false));
break;
- case 110:
- position.set(Position.KEY_FUEL_CONSUMPTION, readValue(buf, length, true) * 0.1);
- break;
- case 113:
- if (length == 1) {
- position.set(Position.KEY_BATTERY_LEVEL, readValue(buf, length, true));
- } else {
- position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
- }
- break;
case 115:
position.set(Position.KEY_COOLANT_TEMP, readValue(buf, length, true) * 0.1);
break;
@@ -311,9 +282,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
case 199:
position.set(Position.KEY_ODOMETER_TRIP, readValue(buf, length, false));
break;
- case 235:
- position.set("oilLevel", readValue(buf, length, false));
- break;
case 236:
if (readValue(buf, length, false) == 1) {
position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
@@ -569,6 +537,29 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
int length = buf.readUnsignedShort();
if (id == 256) {
position.set(Position.KEY_VIN, buf.readSlice(length).toString(StandardCharsets.US_ASCII));
+ } else if (id == 385) {
+ ByteBuf data = buf.readSlice(length);
+ data.readUnsignedByte(); // data part
+ int index = 1;
+ while (data.isReadable()) {
+ int flags = data.readUnsignedByte();
+ if (BitUtil.from(flags, 4) > 0) {
+ position.set("beacon" + index + "Uuid", ByteBufUtil.hexDump(data.readSlice(16)));
+ position.set("beacon" + index + "Major", data.readUnsignedShort());
+ position.set("beacon" + index + "Minor", data.readUnsignedShort());
+ } else {
+ position.set("beacon" + index + "Namespace", ByteBufUtil.hexDump(data.readSlice(10)));
+ position.set("beacon" + index + "Instance", ByteBufUtil.hexDump(data.readSlice(6)));
+ }
+ position.set("beacon" + index + "Rssi", (int) data.readByte());
+ if (BitUtil.check(flags, 1)) {
+ position.set("beacon" + index + "Battery", data.readUnsignedShort() * 0.01);
+ }
+ if (BitUtil.check(flags, 2)) {
+ position.set("beacon" + index + "Temp", data.readUnsignedShort());
+ }
+ index += 1;
+ }
} else {
position.set(Position.PREFIX_IO + id, ByteBufUtil.hexDump(buf.readSlice(length)));
}
diff --git a/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java
index 0342404a6..40267022b 100644
--- a/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/V680ProtocolDecoder.java
@@ -62,7 +62,7 @@ public class V680ProtocolDecoder extends BaseProtocolDecoder {
if (sentence.length() == 16) {
- getDeviceSession(channel, remoteAddress, sentence.substring(1, sentence.length()));
+ getDeviceSession(channel, remoteAddress, sentence.substring(1));
} else {
diff --git a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java
index c132f194b..31a1bbb11 100644
--- a/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xt2400ProtocolDecoder.java
@@ -91,7 +91,7 @@ public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
return length;
}
- private Map<Short, byte[]> formats = new HashMap<>();
+ private final Map<Short, byte[]> formats = new HashMap<>();
public void setConfig(String configString) {
Pattern pattern = Pattern.compile(":wycfg pcr\\[\\d+] ([0-9a-fA-F]{2})[0-9a-fA-F]{2}([0-9a-fA-F]+)");
diff --git a/src/main/java/org/traccar/reports/ReportUtils.java b/src/main/java/org/traccar/reports/ReportUtils.java
index 3a631e0d9..870264fad 100644
--- a/src/main/java/org/traccar/reports/ReportUtils.java
+++ b/src/main/java/org/traccar/reports/ReportUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -81,8 +81,7 @@ public final class ReportUtils {
}
public static Collection<Long> getDeviceList(Collection<Long> deviceIds, Collection<Long> groupIds) {
- Collection<Long> result = new ArrayList<>();
- result.addAll(deviceIds);
+ Collection<Long> result = new ArrayList<>(deviceIds);
for (long groupId : groupIds) {
result.addAll(Context.getPermissionsManager().getGroupDevices(groupId));
}
@@ -98,7 +97,7 @@ public final class ReportUtils {
double firstOdometer = firstPosition.getDouble(Position.KEY_ODOMETER);
double lastOdometer = lastPosition.getDouble(Position.KEY_ODOMETER);
- if (useOdometer && (firstOdometer != 0.0 || lastOdometer != 0.0)) {
+ if (useOdometer && firstOdometer != 0.0 && lastOdometer != 0.0) {
distance = lastOdometer - firstOdometer;
} else if (firstPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)
&& lastPosition.getAttributes().containsKey(Position.KEY_TOTAL_DISTANCE)) {
@@ -113,7 +112,7 @@ public final class ReportUtils {
if (firstPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null
&& lastPosition.getAttributes().get(Position.KEY_FUEL_LEVEL) != null) {
- BigDecimal value = new BigDecimal(firstPosition.getDouble(Position.KEY_FUEL_LEVEL)
+ BigDecimal value = BigDecimal.valueOf(firstPosition.getDouble(Position.KEY_FUEL_LEVEL)
- lastPosition.getDouble(Position.KEY_FUEL_LEVEL));
return value.setScale(1, RoundingMode.HALF_EVEN).doubleValue();
}
@@ -264,21 +263,10 @@ public final class ReportUtils {
stop.setDuration(stopDuration);
stop.setSpentFuel(calculateFuel(startStop, endStop));
- long engineHours = 0;
if (startStop.getAttributes().containsKey(Position.KEY_HOURS)
&& endStop.getAttributes().containsKey(Position.KEY_HOURS)) {
- engineHours = endStop.getLong(Position.KEY_HOURS) - startStop.getLong(Position.KEY_HOURS);
- } else if (Context.getConfig().getBoolean("processing.engineHours.enable")) {
- // Temporary fallback for old data, to be removed in May 2019
- for (int i = startIndex + 1; i <= endIndex; i++) {
- if (positions.get(i).getBoolean(Position.KEY_IGNITION)
- && positions.get(i - 1).getBoolean(Position.KEY_IGNITION)) {
- engineHours += positions.get(i).getFixTime().getTime()
- - positions.get(i - 1).getFixTime().getTime();
- }
- }
+ stop.setEngineHours(endStop.getLong(Position.KEY_HOURS) - startStop.getLong(Position.KEY_HOURS));
}
- stop.setEngineHours(engineHours);
if (!ignoreOdometer
&& startStop.getDouble(Position.KEY_ODOMETER) != 0
diff --git a/src/main/java/org/traccar/reports/Summary.java b/src/main/java/org/traccar/reports/Summary.java
index 6d179a873..612761844 100644
--- a/src/main/java/org/traccar/reports/Summary.java
+++ b/src/main/java/org/traccar/reports/Summary.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2016 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +22,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.sql.SQLException;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
@@ -35,27 +36,18 @@ public final class Summary {
private Summary() {
}
- private static SummaryReport calculateSummaryResult(long deviceId, Date from, Date to) throws SQLException {
+ private static SummaryReport calculateSummaryResult(long deviceId, Collection<Position> positions) {
SummaryReport result = new SummaryReport();
result.setDeviceId(deviceId);
result.setDeviceName(Context.getIdentityManager().getById(deviceId).getName());
- Collection<Position> positions = Context.getDataManager().getPositions(deviceId, from, to);
if (positions != null && !positions.isEmpty()) {
Position firstPosition = null;
Position previousPosition = null;
double speedSum = 0;
- boolean engineHoursEnabled = Context.getConfig().getBoolean("processing.engineHours.enable");
for (Position position : positions) {
if (firstPosition == null) {
firstPosition = position;
}
- if (engineHoursEnabled && previousPosition != null
- && position.getBoolean(Position.KEY_IGNITION)
- && previousPosition.getBoolean(Position.KEY_IGNITION)) {
- // Temporary fallback for old data, to be removed in May 2019
- result.addEngineHours(position.getFixTime().getTime()
- - previousPosition.getFixTime().getTime());
- }
previousPosition = position;
speedSum += position.getSpeed();
result.setMaxSpeed(position.getSpeed());
@@ -66,8 +58,7 @@ public final class Summary {
result.setAverageSpeed(speedSum / positions.size());
result.setSpentFuel(ReportUtils.calculateFuel(firstPosition, previousPosition));
- if (engineHoursEnabled
- && firstPosition.getAttributes().containsKey(Position.KEY_HOURS)
+ if (firstPosition.getAttributes().containsKey(Position.KEY_HOURS)
&& previousPosition.getAttributes().containsKey(Position.KEY_HOURS)) {
result.setEngineHours(
previousPosition.getLong(Position.KEY_HOURS) - firstPosition.getLong(Position.KEY_HOURS));
@@ -83,26 +74,59 @@ public final class Summary {
result.setEndOdometer(previousPosition.getDouble(Position.KEY_TOTAL_DISTANCE));
}
+ result.setStartTime(firstPosition.getFixTime());
+ result.setEndTime(previousPosition.getServerTime());
}
return result;
}
+ private static int getDay(long userId, Date date) {
+ Calendar calendar = Calendar.getInstance(ReportUtils.getTimezone(userId));
+ calendar.setTime(date);
+ return calendar.get(Calendar.DAY_OF_MONTH);
+ }
+
+ private static Collection<SummaryReport> calculateSummaryResults(
+ long userId, long deviceId, Date from, Date to, boolean daily) throws SQLException {
+
+ ArrayList<Position> positions = new ArrayList<>(Context.getDataManager().getPositions(deviceId, from, to));
+
+ ArrayList<SummaryReport> results = new ArrayList<>();
+ if (daily && !positions.isEmpty()) {
+ int startIndex = 0;
+ int startDay = getDay(userId, positions.iterator().next().getFixTime());
+ for (int i = 0; i < positions.size(); i++) {
+ int currentDay = getDay(userId, positions.get(i).getFixTime());
+ if (currentDay != startDay) {
+ results.add(calculateSummaryResult(deviceId, positions.subList(startIndex, i)));
+ startIndex = i;
+ startDay = currentDay;
+ }
+ }
+ results.add(calculateSummaryResult(deviceId, positions.subList(startIndex, positions.size())));
+ } else {
+ results.add(calculateSummaryResult(deviceId, positions));
+ }
+
+ return results;
+ }
+
public static Collection<SummaryReport> getObjects(long userId, Collection<Long> deviceIds,
- Collection<Long> groupIds, Date from, Date to) throws SQLException {
+ Collection<Long> groupIds, Date from, Date to, boolean daily) throws SQLException {
ReportUtils.checkPeriodLimit(from, to);
ArrayList<SummaryReport> result = new ArrayList<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
Context.getPermissionsManager().checkDevice(userId, deviceId);
- result.add(calculateSummaryResult(deviceId, from, to));
+ result.addAll(calculateSummaryResults(userId, deviceId, from, to, daily));
}
return result;
}
public static void getExcel(OutputStream outputStream,
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- Date from, Date to) throws SQLException, IOException {
+ Date from, Date to, boolean daily) throws SQLException, IOException {
ReportUtils.checkPeriodLimit(from, to);
- Collection<SummaryReport> summaries = getObjects(userId, deviceIds, groupIds, from, to);
+ Collection<SummaryReport> summaries = getObjects(userId, deviceIds, groupIds, from, to, daily);
String templatePath = Context.getConfig().getString("report.templatesPath",
"templates/export/");
try (InputStream inputStream = new FileInputStream(templatePath + "/summary.xlsx")) {
diff --git a/src/main/java/org/traccar/reports/model/BaseReport.java b/src/main/java/org/traccar/reports/model/BaseReport.java
index 9f2d1188c..deb42bb8a 100644
--- a/src/main/java/org/traccar/reports/model/BaseReport.java
+++ b/src/main/java/org/traccar/reports/model/BaseReport.java
@@ -16,6 +16,8 @@
*/
package org.traccar.reports.model;
+import java.util.Date;
+
public class BaseReport {
private long deviceId;
@@ -103,4 +105,24 @@ public class BaseReport {
this.endOdometer = endOdometer;
}
+ private Date startTime;
+
+ public Date getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(Date startTime) {
+ this.startTime = startTime;
+ }
+
+ private Date endTime;
+
+ public Date getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(Date endTime) {
+ this.endTime = endTime;
+ }
+
}
diff --git a/src/main/java/org/traccar/reports/model/StopReport.java b/src/main/java/org/traccar/reports/model/StopReport.java
index 245292b63..e20f2c503 100644
--- a/src/main/java/org/traccar/reports/model/StopReport.java
+++ b/src/main/java/org/traccar/reports/model/StopReport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,8 +16,6 @@
*/
package org.traccar.reports.model;
-import java.util.Date;
-
public class StopReport extends BaseReport {
private long positionId;
@@ -50,26 +48,6 @@ public class StopReport extends BaseReport {
this.longitude = longitude;
}
- private Date startTime;
-
- public Date getStartTime() {
- return startTime;
- }
-
- public void setStartTime(Date startTime) {
- this.startTime = startTime;
- }
-
- private Date endTime;
-
- public Date getEndTime() {
- return endTime;
- }
-
- public void setEndTime(Date endTime) {
- this.endTime = endTime;
- }
-
private String address;
public String getAddress() {
@@ -99,8 +77,4 @@ public class StopReport extends BaseReport {
public void setEngineHours(long engineHours) {
this.engineHours = engineHours;
}
-
- public void addEngineHours(long engineHours) {
- this.engineHours += engineHours;
- }
}
diff --git a/src/main/java/org/traccar/reports/model/SummaryReport.java b/src/main/java/org/traccar/reports/model/SummaryReport.java
index 6f9e9459f..886f8b9e2 100644
--- a/src/main/java/org/traccar/reports/model/SummaryReport.java
+++ b/src/main/java/org/traccar/reports/model/SummaryReport.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,8 +27,4 @@ public class SummaryReport extends BaseReport {
public void setEngineHours(long engineHours) {
this.engineHours = engineHours;
}
-
- public void addEngineHours(long engineHours) {
- this.engineHours += engineHours;
- }
}
diff --git a/src/main/java/org/traccar/reports/model/TripReport.java b/src/main/java/org/traccar/reports/model/TripReport.java
index 3140f3019..151c34bd5 100644
--- a/src/main/java/org/traccar/reports/model/TripReport.java
+++ b/src/main/java/org/traccar/reports/model/TripReport.java
@@ -16,8 +16,6 @@
*/
package org.traccar.reports.model;
-import java.util.Date;
-
public class TripReport extends BaseReport {
private long startPositionId;
@@ -80,16 +78,6 @@ public class TripReport extends BaseReport {
this.endLon = endLon;
}
- private Date startTime;
-
- public Date getStartTime() {
- return startTime;
- }
-
- public void setStartTime(Date startTime) {
- this.startTime = startTime;
- }
-
private String startAddress;
public String getStartAddress() {
@@ -100,16 +88,6 @@ public class TripReport extends BaseReport {
this.startAddress = address;
}
- private Date endTime;
-
- public Date getEndTime() {
- return endTime;
- }
-
- public void setEndTime(Date endTime) {
- this.endTime = endTime;
- }
-
private String endAddress;
public String getEndAddress() {
diff --git a/src/main/java/org/traccar/schedule/ScheduleManager.java b/src/main/java/org/traccar/schedule/ScheduleManager.java
new file mode 100644
index 000000000..4def211d0
--- /dev/null
+++ b/src/main/java/org/traccar/schedule/ScheduleManager.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 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.schedule;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+public class ScheduleManager {
+
+ private ScheduledExecutorService executor;
+
+ public void start() {
+
+ executor = Executors.newSingleThreadScheduledExecutor();
+
+ new TaskDeviceInactivityCheck().schedule(executor);
+
+ }
+
+ public void stop() {
+
+ if (executor != null) {
+ executor.shutdown();
+ executor = null;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java
new file mode 100644
index 000000000..80641d7d4
--- /dev/null
+++ b/src/main/java/org/traccar/schedule/TaskDeviceInactivityCheck.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2020 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.schedule;
+
+import org.traccar.Context;
+import org.traccar.model.Device;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public class TaskDeviceInactivityCheck implements Runnable {
+
+ public static final String ATTRIBUTE_DEVICE_INACTIVITY_START = "deviceInactivityStart";
+ public static final String ATTRIBUTE_DEVICE_INACTIVITY_PERIOD = "deviceInactivityPeriod";
+ public static final String ATTRIBUTE_LAST_UPDATE = "lastUpdate";
+
+ private static final long CHECK_PERIOD_MINUTES = 15;
+
+ public void schedule(ScheduledExecutorService executor) {
+ executor.scheduleAtFixedRate(this, CHECK_PERIOD_MINUTES, CHECK_PERIOD_MINUTES, TimeUnit.MINUTES);
+ }
+
+ @Override
+ public void run() {
+ long currentTime = System.currentTimeMillis();
+ long checkPeriod = TimeUnit.MINUTES.toMillis(CHECK_PERIOD_MINUTES);
+
+ Map<Event, Position> events = new HashMap<>();
+ for (Device device : Context.getDeviceManager().getAllDevices()) {
+ if (device.getLastUpdate() != null && checkDevice(device, currentTime, checkPeriod)) {
+ Event event = new Event(Event.TYPE_DEVICE_INACTIVE, device.getId());
+ event.set(ATTRIBUTE_LAST_UPDATE, device.getLastUpdate().getTime());
+ events.put(event, null);
+ }
+ }
+
+ Context.getNotificationManager().updateEvents(events);
+ }
+
+ private boolean checkDevice(Device device, long currentTime, long checkPeriod) {
+ long deviceInactivityStart = device.getLong(ATTRIBUTE_DEVICE_INACTIVITY_START);
+ if (deviceInactivityStart > 0) {
+ long timeThreshold = device.getLastUpdate().getTime() + deviceInactivityStart;
+ if (currentTime >= timeThreshold) {
+
+ if (currentTime - checkPeriod < timeThreshold) {
+ return true;
+ }
+
+ long deviceInactivityPeriod = device.getLong(ATTRIBUTE_DEVICE_INACTIVITY_PERIOD);
+ if (deviceInactivityPeriod > 0) {
+ long count = (currentTime - timeThreshold - 1) / deviceInactivityPeriod;
+ timeThreshold += count * deviceInactivityPeriod;
+ return currentTime - checkPeriod < timeThreshold;
+ }
+
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/java/org/traccar/sms/smpp/SmppClient.java b/src/main/java/org/traccar/sms/smpp/SmppClient.java
index 874253d36..6eb850b6a 100644
--- a/src/main/java/org/traccar/sms/smpp/SmppClient.java
+++ b/src/main/java/org/traccar/sms/smpp/SmppClient.java
@@ -20,7 +20,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
@@ -122,25 +121,19 @@ public class SmppClient implements SmsManager {
enquireLinkPeriod = Context.getConfig().getInteger("sms.smpp.enquireLinkPeriod", 60000);
enquireLinkTimeout = Context.getConfig().getInteger("sms.smpp.enquireLinkTimeout", 10000);
- enquireLinkExecutor = Executors.newScheduledThreadPool(1, new ThreadFactory() {
- @Override
- public Thread newThread(Runnable runnable) {
- Thread thread = new Thread(runnable);
- String name = sessionConfig.getName();
- thread.setName("EnquireLink-" + name);
- return thread;
- }
+ enquireLinkExecutor = Executors.newScheduledThreadPool(1, runnable -> {
+ Thread thread = new Thread(runnable);
+ String name = sessionConfig.getName();
+ thread.setName("EnquireLink-" + name);
+ return thread;
});
reconnectionDelay = Context.getConfig().getInteger("sms.smpp.reconnectionDelay", 10000);
- reconnectionExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
- @Override
- public Thread newThread(Runnable runnable) {
- Thread thread = new Thread(runnable);
- String name = sessionConfig.getName();
- thread.setName("Reconnection-" + name);
- return thread;
- }
+ reconnectionExecutor = Executors.newSingleThreadScheduledExecutor(runnable -> {
+ Thread thread = new Thread(runnable);
+ String name = sessionConfig.getName();
+ thread.setName("Reconnection-" + name);
+ return thread;
});
scheduleReconnect();
@@ -257,14 +250,11 @@ public class SmppClient implements SmsManager {
@Override
public void sendMessageAsync(final String destAddress, final String message, final boolean command) {
- executorService.execute(new Runnable() {
- @Override
- public void run() {
- try {
- sendMessageSync(destAddress, message, command);
- } catch (MessageException | InterruptedException | IllegalStateException error) {
- LOGGER.warn("SMS sending error", error);
- }
+ executorService.execute(() -> {
+ try {
+ sendMessageSync(destAddress, message, command);
+ } catch (MessageException | InterruptedException | IllegalStateException error) {
+ LOGGER.warn("SMS sending error", error);
}
});
}
diff --git a/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java b/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java
new file mode 100644
index 000000000..429a47c76
--- /dev/null
+++ b/src/main/java/org/traccar/speedlimit/OverpassSpeedLimitProvider.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 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.speedlimit;
+
+import org.traccar.Context;
+import org.traccar.helper.UnitsConverter;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.ws.rs.client.AsyncInvoker;
+import javax.ws.rs.client.InvocationCallback;
+
+public class OverpassSpeedLimitProvider implements SpeedLimitProvider {
+
+ private final String url;
+
+ public OverpassSpeedLimitProvider(String url) {
+ this.url = url + "?data=[out:json];way[maxspeed](around:100.0,%f,%f);out%%20tags;";
+ }
+
+ private Double parseSpeed(String value) {
+ if (value.endsWith(" mph")) {
+ return UnitsConverter.knotsFromMph(Double.parseDouble(value.substring(0, value.length() - 4)));
+ } else if (value.endsWith(" knots")) {
+ return Double.parseDouble(value.substring(0, value.length() - 6));
+ } else if (value.matches("\\d+")) {
+ return UnitsConverter.knotsFromKph(Double.parseDouble(value));
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void getSpeedLimit(double latitude, double longitude, SpeedLimitProviderCallback callback) {
+ String formattedUrl = String.format(url, latitude, longitude);
+ AsyncInvoker invoker = Context.getClient().target(formattedUrl).request().async();
+ invoker.get(new InvocationCallback<JsonObject>() {
+ @Override
+ public void completed(JsonObject json) {
+ JsonArray elements = json.getJsonArray("elements");
+ if (!elements.isEmpty()) {
+ Double maxSpeed = parseSpeed(
+ elements.getJsonObject(0).getJsonObject("tags").getString("maxspeed"));
+ if (maxSpeed != null) {
+ callback.onSuccess(maxSpeed);
+ } else {
+ callback.onFailure(new SpeedLimitException("Parsing failed"));
+ }
+ } else {
+ callback.onFailure(new SpeedLimitException("Not found"));
+ }
+ }
+
+ @Override
+ public void failed(Throwable throwable) {
+ callback.onFailure(throwable);
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/speedlimit/SpeedLimitException.java b/src/main/java/org/traccar/speedlimit/SpeedLimitException.java
new file mode 100644
index 000000000..453c952d3
--- /dev/null
+++ b/src/main/java/org/traccar/speedlimit/SpeedLimitException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2020 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.speedlimit;
+
+public class SpeedLimitException extends RuntimeException {
+
+ public SpeedLimitException(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/main/java/org/traccar/speedlimit/SpeedLimitProvider.java b/src/main/java/org/traccar/speedlimit/SpeedLimitProvider.java
new file mode 100644
index 000000000..f6579f87f
--- /dev/null
+++ b/src/main/java/org/traccar/speedlimit/SpeedLimitProvider.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2020 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.speedlimit;
+
+public interface SpeedLimitProvider {
+
+ interface SpeedLimitProviderCallback {
+
+ void onSuccess(double speedLimit);
+
+ void onFailure(Throwable e);
+
+ }
+
+ void getSpeedLimit(double latitude, double longitude, SpeedLimitProviderCallback callback);
+
+}
diff --git a/src/main/java/org/traccar/web/CsvBuilder.java b/src/main/java/org/traccar/web/CsvBuilder.java
index 3fe7e408f..b962be072 100644
--- a/src/main/java/org/traccar/web/CsvBuilder.java
+++ b/src/main/java/org/traccar/web/CsvBuilder.java
@@ -21,7 +21,6 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Comparator;
import java.util.Date;
import java.util.Map;
import java.util.SortedSet;
@@ -45,23 +44,21 @@ public class CsvBuilder {
private void addLineEnding() {
builder.append(LINE_ENDING);
}
+
private void addSeparator() {
builder.append(SEPARATOR);
}
private SortedSet<Method> getSortedMethods(Object object) {
Method[] methodArray = object.getClass().getMethods();
- SortedSet<Method> methods = new TreeSet<>(new Comparator<Method>() {
- @Override
- public int compare(Method m1, Method m2) {
- if (m1.getName().equals("getAttributes") && !m1.getName().equals(m2.getName())) {
- return 1;
- }
- if (m2.getName().equals("getAttributes") && !m1.getName().equals(m2.getName())) {
- return -1;
- }
- return m1.getName().compareTo(m2.getName());
+ SortedSet<Method> methods = new TreeSet<>((m1, m2) -> {
+ if (m1.getName().equals("getAttributes") && !m1.getName().equals(m2.getName())) {
+ return 1;
+ }
+ if (m2.getName().equals("getAttributes") && !m1.getName().equals(m2.getName())) {
+ return -1;
}
+ return m1.getName().compareTo(m2.getName());
});
methods.addAll(Arrays.asList(methodArray));
return methods;
diff --git a/src/main/java/org/traccar/web/WebServer.java b/src/main/java/org/traccar/web/WebServer.java
index 12fa80d10..44d78cd27 100644
--- a/src/main/java/org/traccar/web/WebServer.java
+++ b/src/main/java/org/traccar/web/WebServer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2020 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.web;
+import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.proxy.AsyncProxyServlet;
@@ -32,6 +33,7 @@ import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.api.DateParameterConverterProvider;
import org.traccar.config.Config;
import org.traccar.api.AsyncSocketServlet;
import org.traccar.api.CorsResponseFilter;
@@ -44,6 +46,7 @@ import org.traccar.config.Keys;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
+import javax.servlet.SessionCookieConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
@@ -75,12 +78,8 @@ public class WebServer {
ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
- int sessionTimeout = config.getInteger("web.sessionTimeout");
- if (sessionTimeout > 0) {
- servletHandler.getSessionHandler().setMaxInactiveInterval(sessionTimeout);
- }
-
initApi(config, servletHandler);
+ initSessionConfig(config, servletHandler);
if (config.getBoolean("web.console")) {
servletHandler.addServlet(new ServletHolder(new ConsoleServlet()), "/console/*");
@@ -161,12 +160,39 @@ public class WebServer {
}
ResourceConfig resourceConfig = new ResourceConfig();
- resourceConfig.registerClasses(JacksonFeature.class, ObjectMapperProvider.class, ResourceErrorHandler.class);
- resourceConfig.registerClasses(SecurityRequestFilter.class, CorsResponseFilter.class);
+ resourceConfig.registerClasses(
+ JacksonFeature.class, ObjectMapperProvider.class, ResourceErrorHandler.class,
+ SecurityRequestFilter.class, CorsResponseFilter.class, DateParameterConverterProvider.class);
resourceConfig.packages(ServerResource.class.getPackage().getName());
servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/api/*");
}
+ private void initSessionConfig(Config config, ServletContextHandler servletHandler) {
+ int sessionTimeout = config.getInteger("web.sessionTimeout");
+ if (sessionTimeout > 0) {
+ servletHandler.getSessionHandler().setMaxInactiveInterval(sessionTimeout);
+ }
+
+ String sameSiteCookie = config.getString(Keys.WEB_SAME_SITE_COOKIE);
+ if (sameSiteCookie != null) {
+ SessionCookieConfig sessionCookieConfig = servletHandler.getServletContext().getSessionCookieConfig();
+ switch (sameSiteCookie.toLowerCase()) {
+ case "lax":
+ sessionCookieConfig.setComment(HttpCookie.SAME_SITE_LAX_COMMENT);
+ break;
+ case "strict":
+ sessionCookieConfig.setComment(HttpCookie.SAME_SITE_STRICT_COMMENT);
+ break;
+ case "none":
+ sessionCookieConfig.setSecure(true);
+ sessionCookieConfig.setComment(HttpCookie.SAME_SITE_NONE_COMMENT);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
public void start() {
try {
server.start();
diff --git a/src/main/proto/StarLinkMessage.proto b/src/main/proto/StarLinkMessage.proto
new file mode 100644
index 000000000..167b9f283
--- /dev/null
+++ b/src/main/proto/StarLinkMessage.proto
@@ -0,0 +1,330 @@
+syntax = "proto2";
+
+package org.traccar.protobuf;
+
+// StarLink Message
+message StarLink_Message {
+ optional uint32 FID = 1; // Unit factory serial
+ optional string IMEI = 2; // IMEI
+ optional uint32 UID = 3; // Unit serial
+ optional uint32 AckRef = 4; // Ack reference number
+
+ repeated mMessage Message = 5;
+}
+
+// Message body
+message mMessage {
+ optional uint64 ReferenceNumber = 100;
+ optional uint32 ReferenceNumberDiff = 101;
+ optional uint32 MessageType = 102;
+
+ optional mWakeUp WakeUp = 1;
+ optional mConfirmation Confirmation = 2;
+ optional mParamResponse ParamResponse = 3;
+ optional mEventReport EventReport = 6;
+ optional mGeoZone GeoZone = 10;
+ optional mOutputs Outputs = 12;
+ optional mEventConfig EventConfig = 14;
+ optional mInputs Inputs = 16;
+ optional mInputConfig InputConfig = 17;
+ optional mSpeedLimit SpeedLimit = 27;
+ optional mUnitIDChanged UnitIDChanged = 29;
+ optional mGenDeviceMsg GenericDeviceMsg= 30;
+ optional mGenDeviceFW GenericDeviceFW = 35;
+ optional mReadIButton ReadIButton = 42;
+ optional mGetPattern GetPattern = 43;
+ optional mMultiParam MultiParameter = 45;
+ optional mTempConfig TempConfig = 52;
+ optional mDeviceList DeviceList = 56;
+ optional mTeachRemote TeachRemote = 58;
+ optional mAuthenticate Authenticate = 95;
+ optional mEventReport CurrentLocation = 96;
+ optional mConfigServer ConfigServer = 98;
+}
+
+// Wake-Up event
+message mWakeUp {
+ required string UnitVersion = 1;
+ required string ProtocolVersion = 2;
+}
+
+// Confirmation message
+message mConfirmation {
+ // standard response
+ required uint32 ResultCode = 1;
+ optional uint32 ReferenceNumber = 2; // confirmation by reference number
+ optional uint32 MessageType = 3; // confirmation by message type
+
+ // parameter ack (response to command #4)
+ optional uint32 ParameterAck = 4;
+
+ // multi-parameter ack (response to command #46)
+ optional uint32 ParametersChanged = 5;
+ optional uint32 ParametersFailed = 6;
+ repeated uint32 ParameterFailedList = 9;
+
+ // modify list of allowed drivers (response to command #50)
+ optional uint32 CodesInList = 7;
+ optional uint32 EmptySlots = 8;
+}
+
+// Parameter response
+message mParamResponse {
+ required uint32 ParameterNumber = 1;
+ required string Value = 2;
+}
+
+// Event report
+message mEventReport {
+ optional uint32 EID = 1; // https://sweb.erm.co.il/protocol/?i=D12
+ optional string EDESC = 2; // https://sweb.erm.co.il/protocol/?i=D13
+ optional uint32 EDT = 3; // https://sweb.erm.co.il/protocol/?i=D27 (seconds since 1/1/2000)
+ optional sint32 EDT_Diff = 4; // seconds since previous message's EDT
+ optional uint32 NXT = 5; // https://sweb.erm.co.il/protocol/?i=D111
+ optional uint32 NXTS = 81; // https://sweb.erm.co.il/protocol/?i=D178
+ optional string ExtraData = 254;
+
+ optional uint32 PDT = 6; // https://sweb.erm.co.il/protocol/?i=D35 (seconds since 1/1/2000)
+ optional sint32 PDT_Diff = 7; // seconds since previous message's PDT
+ optional sint32 LAT = 8; // https://sweb.erm.co.il/protocol/?i=D49 (multiplied by 1000000)
+ optional sint32 LAT_Diff = 9; // difference between previous latitude (add this value to previous value)
+ optional sint32 LONG = 10; // https://sweb.erm.co.il/protocol/?i=D57 (multiplied by 1000000)
+ optional sint32 LONG_Diff = 11; // difference between previous longitude (add this value to previous value)
+ optional sint32 ALT = 12; // https://sweb.erm.co.il/protocol/?i=D69 (multiplied by 10)
+ optional sint32 ALT_Diff = 13; // difference between previous altitude (add this value to previous value)
+ optional uint32 SPD = 14; // https://sweb.erm.co.il/protocol/?i=D61 (multiplied by 10, in knots)
+ optional uint32 SPDK = 15; // https://sweb.erm.co.il/protocol/?i=D62
+ optional uint32 HEAD = 16; // https://sweb.erm.co.il/protocol/?i=D63
+ optional uint32 HDOP = 17; // https://sweb.erm.co.il/protocol/?i=D71 (multiplied by 10)
+ optional uint32 VDOP = 18; // https://sweb.erm.co.il/protocol/?i=D72 (multiplied by 10)
+ optional uint32 PDOP = 19; // https://sweb.erm.co.il/protocol/?i=D70 (multiplied by 10)
+ optional uint32 SAT = 20; // https://sweb.erm.co.il/protocol/?i=D73
+ optional uint32 SATN = 21; // https://sweb.erm.co.il/protocol/?i=D99
+ optional uint32 SATU = 22; // https://sweb.erm.co.il/protocol/?i=D74
+ optional uint32 GSS = 23; // https://sweb.erm.co.il/protocol/?i=D98
+ optional uint32 GSSN = 24; // https://sweb.erm.co.il/protocol/?i=D100
+ optional uint32 FIX = 25; // https://sweb.erm.co.il/protocol/?i=D75
+ optional uint32 LOCA = 26; // https://sweb.erm.co.il/protocol/?i=D126
+ optional uint32 LOCS = 79; // https://sweb.erm.co.il/protocol/?i=D167
+
+ optional uint32 ODO = 27; // https://sweb.erm.co.il/protocol/?i=D14
+ optional sint32 ODO_Diff = 28; // difference between previous odometer (add this value to previous value)
+ optional uint32 ODOD = 29; // https://sweb.erm.co.il/protocol/?i=D15 (multiplied by 1000)
+ optional sint32 ODOD_Diff = 30; // difference between previous odometer (add this value to previous value)
+
+ optional uint32 CID = 31; // https://sweb.erm.co.il/protocol/?i=D5
+ optional uint32 LAC = 32; // https://sweb.erm.co.il/protocol/?i=D4
+ optional uint32 CSS = 33; // https://sweb.erm.co.il/protocol/?i=D89
+ optional sint32 CSS_Diff = 34; // difference between previous rx power (add this value to previous value)
+ optional uint32 CTA = 35; // https://sweb.erm.co.il/protocol/?i=D95
+ optional uint32 CCN = 36; // https://sweb.erm.co.il/protocol/?i=D89
+ optional bool JAM = 37; // https://sweb.erm.co.il/protocol/?i=D66
+ optional uint32 NC = 38; // https://sweb.erm.co.il/protocol/?i=D94
+ optional uint32 NM = 39; // https://sweb.erm.co.il/protocol/?i=D125
+ optional uint32 NT = 76; // https://sweb.erm.co.il/protocol/?i=D153
+ optional uint32 CU = 77; // https://sweb.erm.co.il/protocol/?i=D154
+
+ optional uint32 VIN = 40; // https://sweb.erm.co.il/protocol/?i=D64 (multiplied by 100)
+ optional sint32 VIN_Diff = 41; // difference between previous VIN (add this value to previous value)
+ optional uint32 VBAT = 42; // https://sweb.erm.co.il/protocol/?i=D65 (multiplied by 100)
+ optional sint32 VBAT_Diff = 43; // difference between previous VBAT (add this value to previous value)
+ optional uint32 V3 = 44; // https://sweb.erm.co.il/protocol/?i=D92 (multiplied by 100)
+ optional sint32 V3_Diff = 45; // difference between previous V3 (add this value to previous value)
+ optional uint32 V4 = 46; // https://sweb.erm.co.il/protocol/?i=D93 (multiplied by 100)
+ optional sint32 V4_Diff = 47; // difference between previous V4 (add this value to previous value)
+ optional uint32 BATH = 48;
+ optional uint32 BATC = 49;
+ optional uint32 STRT = 50; // (multiplied by 100)
+ optional sint32 TVI = 51; // https://sweb.erm.co.il/protocol/?i=D110 (multiplied by 10)
+
+ optional uint32 Inputs = 52; // b0 = IN1/INC, b1 = IN2/IND, b2 = IN3/INE, b3 = IN4/INF
+ optional uint32 DInputs = 53; // b0 = DI1, b1 = DI2, b2 = DI3, b3 = DI4, b4 = DI5, b5 = DI6, b6 = DI7, b7 = DI8
+ optional uint32 IgnitionEngine = 54; // b0 = IGN, b1 = ENG, b2 = DRV
+ optional uint32 Outputs = 55; // b0 = OUT1/OUTA, b1 = OUT2/OUTB, b2 = OUT3/OUTC, b3 = OUT4/OUTD, b4 = OUT5, b5 = OUT6, b6 = OUT7, b7 = OUT8
+
+ optional string USER = 56; // https://sweb.erm.co.il/protocol/?i=D97
+ optional string EDV1 = 57; // https://sweb.erm.co.il/protocol/?i=D179
+ optional string EDV2 = 58; // https://sweb.erm.co.il/protocol/?i=D180
+ optional string EDV3 = 59; // https://sweb.erm.co.il/protocol/?i=D181
+ optional string EDV4 = 60; // https://sweb.erm.co.il/protocol/?i=D182
+
+ optional sint32 CV1 = 61; // https://sweb.erm.co.il/protocol/?i=D121
+ optional sint32 CV2 = 62; // https://sweb.erm.co.il/protocol/?i=D122
+ optional sint32 CV3 = 63; // https://sweb.erm.co.il/protocol/?i=D123
+ optional sint32 CV4 = 64; // https://sweb.erm.co.il/protocol/?i=D124
+ optional sint32 CV5 = 82; // https://sweb.erm.co.il/protocol/?i=D174
+ optional sint32 CV6 = 83; // https://sweb.erm.co.il/protocol/?i=D175
+ optional sint32 CV7 = 84; // https://sweb.erm.co.il/protocol/?i=D176
+ optional sint32 CV8 = 85; // https://sweb.erm.co.il/protocol/?i=D177
+ optional sint32 CV9 = 86; // https://sweb.erm.co.il/protocol/?i=D188
+ optional sint32 CV10 = 87; // https://sweb.erm.co.il/protocol/?i=D189
+ optional sint32 CV11 = 88; // https://sweb.erm.co.il/protocol/?i=D190
+ optional sint32 CV12 = 89; // https://sweb.erm.co.il/protocol/?i=D191
+
+ optional bool IARM = 65; // https://sweb.erm.co.il/protocol/?i=D7
+ optional string DID = 66; // https://sweb.erm.co.il/protocol/?i=D101
+ optional string DAL = 67; // https://sweb.erm.co.il/protocol/?i=D76
+ optional uint32 DEST = 68; // https://sweb.erm.co.il/protocol/?i=D8
+ optional bool OVM = 73; // https://sweb.erm.co.il/protocol/?i=D150
+
+ optional uint32 CFL = 69; // https://sweb.erm.co.il/protocol/?i=D26
+ optional uint32 CFL2 = 80; // https://sweb.erm.co.il/protocol/?i=D26
+ optional uint32 RPM = 70; // https://sweb.erm.co.il/protocol/?i=D144
+
+ optional uint32 DUR = 71; // https://sweb.erm.co.il/protocol/?i=D145
+ optional uint32 TDUR = 72; // https://sweb.erm.co.il/protocol/?i=D146
+ optional uint32 IDL = 74; // https://sweb.erm.co.il/protocol/?i=D151
+ optional uint32 STP = 75; // https://sweb.erm.co.il/protocol/?i=D152
+
+ repeated mEventReport_TDx TDx = 78; // https://sweb.erm.co.il/protocol/?i=D168
+}
+
+// Sensor data
+message mEventReport_TDx {
+ required uint32 SensorNumber = 1; // Sensor #
+ optional string SensorID = 3; // TIx (binary)
+
+ optional sint32 Temperature = 4; // TVx (multiplied by 10)
+ optional uint32 Temperature_State = 2; // TSx
+
+ optional uint32 Humidity = 5; // THx (multiplied by 10)
+ optional uint32 Humidity_State = 6;
+
+ optional uint32 RSSI = 7;
+ optional uint32 RSSI_State = 8;
+
+ optional uint32 Voltage = 9; // mV (multiplied by 1000)
+ optional uint32 Voltage_State = 10;
+
+ optional uint32 Light = 11; // percent
+ optional uint32 Light_State = 12;
+
+ optional uint32 MagnetSwitch = 13;
+ optional uint32 MagnetSwitch_State = 14;
+
+ optional uint32 Accident = 15;
+ optional uint32 Button = 16;
+ optional uint32 Movement = 17;
+}
+
+// Geozone reqsponse
+message mGeoZone {
+ required uint32 ZoneNumber = 1;
+ repeated mLatLong Point = 2; // single point for radius geozone, multiple points for polygon
+ optional uint32 Radius = 3;
+}
+
+// Outputs response
+message mOutputs {
+ required uint32 OutputBitmask = 1;
+}
+
+// Event configuration response
+message mEventConfig {
+ required uint32 EventID = 1;
+ required uint32 Configuration = 2; // bitmask
+}
+
+// Inputs response
+message mInputs {
+ required uint32 InputBitmask = 1;
+}
+
+// Inputs configuration
+message mInputConfig {
+ required uint32 InputID = 1;
+ required uint32 DebounceLow = 2;
+ required uint32 DebounceHigh = 3;
+}
+
+// Speed limit configuration
+message mSpeedLimit {
+ required uint32 Number = 1;
+ required bool Active = 2;
+ optional uint32 MinSpeed = 3; // when using speed limit with minimum speed
+ optional uint32 MaxSpeed = 4; // when using speed limit with maximum speed
+ required uint32 Duration = 5;
+ required bool ReportDuration = 6;
+ optional bool StartAndEnd = 7;
+ optional bool AutoTrack = 8;
+ optional bool OutputPattern = 9;
+ optional uint32 Step = 10;
+}
+
+// Unit ID changed
+message mUnitIDChanged {
+ required uint32 FID = 1;
+ required string IMEI = 2;
+ required sint32 PreviousSetting = 3;
+ required sint32 NewSetting = 4;
+}
+
+// Generic device message
+message mGenDeviceMsg {
+ required uint32 DeviceID = 1;
+ required uint32 DataType = 2;
+ optional string Data = 3;
+}
+
+// Generic device FW update
+message mGenDeviceFW {
+ optional uint32 BlockRequest = 1;
+ optional uint32 Status = 2;
+ optional uint32 MaxBlocks = 3;
+}
+
+// Configuration server message
+message mConfigServer {
+ required bool Success = 1;
+}
+
+// Read iButton
+message mReadIButton {
+ required string iButtonID = 1;
+ optional string Data = 2;
+}
+
+// Read pattern configuration
+message mGetPattern {
+ required uint32 PatternID = 1;
+ required string Pattern = 2;
+}
+
+// Multiple parameters response
+message mMultiParam {
+ repeated uint32 ParameterNumbers = 1;
+ repeated string Values = 2;
+}
+
+// Temperature sensor configuration
+message mTempConfig {
+ required uint32 SensorCount = 1;
+ repeated string SensorID = 2;
+}
+
+// Device list
+message mDeviceList {
+ repeated uint32 DeviceID = 1;
+ repeated uint32 Version = 2;
+}
+
+// Teach remote
+message mTeachRemote {
+ required uint32 RuleID = 1;
+ optional uint32 RemoteID = 2;
+}
+
+// Authentication message
+message mAuthenticate {
+ required uint32 Version = 1;
+ optional string Token = 2;
+ optional uint32 Key = 3;
+ optional uint32 Step = 4;
+ optional string Digest = 5;
+}
+
+// Point (Lat / Long)
+message mLatLong {
+ required sint32 Latitude = 1;
+ required sint32 Longitude = 2;
+}