aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.gradle2
-rw-r--r--pom.xml2
-rw-r--r--schema/changelog-4.11.xml17
-rw-r--r--schema/changelog-master.xml1
-rw-r--r--setup/default.xml5
-rwxr-xr-xsetup/package.sh2
-rwxr-xr-xsetup/setup.sh12
-rw-r--r--setup/traccar.service2
-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
-rw-r--r--src/test/java/org/traccar/ProtocolTest.java2
-rw-r--r--src/test/java/org/traccar/TestIdentityManager.java5
-rw-r--r--src/test/java/org/traccar/helper/ChecksumTest.java5
-rw-r--r--src/test/java/org/traccar/helper/ObdDecoderTest.java10
-rw-r--r--src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java7
-rw-r--r--src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java16
-rw-r--r--src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java39
-rw-r--r--src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java11
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java7
-rw-r--r--src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java8
-rw-r--r--src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java12
-rw-r--r--src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/PstFrameEncoderTest.java20
-rw-r--r--src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java35
-rw-r--r--src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java12
-rw-r--r--src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java20
-rw-r--r--src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java10
-rw-r--r--src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java2
-rw-r--r--src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java35
-rw-r--r--src/test/java/org/traccar/web/WebServerTest.java9
-rw-r--r--swagger.json5542
-rw-r--r--templates/export/summary.xlsxbin15311 -> 12067 bytes
-rw-r--r--templates/full/deviceInactive.vm12
-rw-r--r--templates/short/deviceInactive.vm3
-rwxr-xr-xtools/test-map.py39
-rwxr-xr-xtools/test-photo.sh103
163 files changed, 6547 insertions, 2826 deletions
diff --git a/build.gradle b/build.gradle
index 5e4fc3868..a63ff805e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -76,7 +76,7 @@ dependencies {
implementation "com.sun.xml.bind:jaxb-core:2.3.0.1"
implementation "com.sun.xml.bind:jaxb-impl:2.3.3"
implementation "javax.activation:activation:1.1.1"
- testImplementation "junit:junit:4.13"
+ testImplementation "junit:junit:4.13.1"
}
task copyDependencies(type: Copy) {
diff --git a/pom.xml b/pom.xml
index 8f803c096..8ef8637d8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.13</version>
+ <version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
diff --git a/schema/changelog-4.11.xml b/schema/changelog-4.11.xml
new file mode 100644
index 000000000..15916a8ba
--- /dev/null
+++ b/schema/changelog-4.11.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<databaseChangeLog
+ xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
+ logicalFilePath="changelog-4.11">
+
+ <changeSet author="author" id="changelog-4.11">
+
+ <addColumn tableName="tc_servers">
+ <column name="announcement" type="VARCHAR(4000)" />
+ </addColumn>
+
+ </changeSet>
+
+</databaseChangeLog>
diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml
index 5e89e746b..7d4475df7 100644
--- a/schema/changelog-master.xml
+++ b/schema/changelog-master.xml
@@ -26,5 +26,6 @@
<include file="changelog-4.7.xml" relativeToChangelogFile="true" />
<include file="changelog-4.9.xml" relativeToChangelogFile="true" />
<include file="changelog-4.10.xml" relativeToChangelogFile="true" />
+ <include file="changelog-4.11.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
diff --git a/setup/default.xml b/setup/default.xml
index 7e1572530..eb7b538ca 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -14,7 +14,6 @@
<entry key='web.port'>8082</entry>
<entry key='web.path'>./web</entry>
<entry key='web.cacheControl'>max-age=3600,public</entry>
- <entry key='web.healthCheck'>true</entry>
<entry key='geocoder.enable'>false</entry>
<entry key='geocoder.type'>google</entry>
@@ -289,5 +288,9 @@
<entry key='wli.port'>5209</entry>
<entry key='niot.port'>5210</entry>
<entry key='portman.port'>5211</entry>
+ <entry key='moovbox.port'>5212</entry>
+ <entry key='futureway.port'>5213</entry>
+ <entry key='polte.port'>5214</entry>
+ <entry key='net.port'>5215</entry>
</properties>
diff --git a/setup/package.sh b/setup/package.sh
index 8014aca36..093db481b 100755
--- a/setup/package.sh
+++ b/setup/package.sh
@@ -145,7 +145,7 @@ package_linux () {
unzip -q -o jdk-*-linux-$1.zip
jlink --module-path jdk-*-linux-$1/jmods --add-modules java.se,jdk.charsets,jdk.crypto.ec --output out/jre
rm -rf jdk-*-linux-$1
- makeself --quiet --notemp out traccar.run "traccar" ./setup.sh
+ makeself --needroot --quiet --notemp out traccar.run "traccar" ./setup.sh
rm -rf out/jre
zip -q -j traccar-linux-$2-$VERSION.zip traccar.run README.txt
diff --git a/setup/setup.sh b/setup/setup.sh
index 7443c3b4e..1b07990ae 100755
--- a/setup/setup.sh
+++ b/setup/setup.sh
@@ -1,9 +1,21 @@
#!/bin/sh
+PRESERVECONFIG=0
+if [ -f /opt/traccar/conf/traccar.xml ]
+then
+ cp /opt/traccar/conf/traccar.xml /opt/traccar/conf/traccar.xml.saved
+ PRESERVECONFIG=1
+fi
+
mkdir -p /opt/traccar
cp -r * /opt/traccar
chmod -R go+rX /opt/traccar
+if [ ${PRESERVECONFIG} -eq 1 ] && [ -f /opt/traccar/conf/traccar.xml.saved ]
+then
+ mv -f /opt/traccar/conf/traccar.xml.saved /opt/traccar/conf/traccar.xml
+fi
+
mv /opt/traccar/traccar.service /etc/systemd/system
chmod 664 /etc/systemd/system/traccar.service
diff --git a/setup/traccar.service b/setup/traccar.service
index 4fb87a6cd..6c11ea1ea 100644
--- a/setup/traccar.service
+++ b/setup/traccar.service
@@ -8,7 +8,7 @@ WorkingDirectory=/opt/traccar
ExecStart=/opt/traccar/jre/bin/java -jar tracker-server.jar conf/traccar.xml
SyslogIdentifier=traccar
SuccessExitStatus=143
-WatchdogSec=300
+WatchdogSec=600
Restart=on-failure
RestartSec=10
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;
+}
diff --git a/src/test/java/org/traccar/ProtocolTest.java b/src/test/java/org/traccar/ProtocolTest.java
index bfff4eef3..56551f247 100644
--- a/src/test/java/org/traccar/ProtocolTest.java
+++ b/src/test/java/org/traccar/ProtocolTest.java
@@ -205,6 +205,8 @@ public class ProtocolTest extends BaseTest {
assertNotNull("protocol is null", position.getProtocol());
+ assertTrue("deviceId > 0", position.getDeviceId() > 0);
+
}
Map<String, Object> attributes = position.getAttributes();
diff --git a/src/test/java/org/traccar/TestIdentityManager.java b/src/test/java/org/traccar/TestIdentityManager.java
index af5dd22df..7d2865e74 100644
--- a/src/test/java/org/traccar/TestIdentityManager.java
+++ b/src/test/java/org/traccar/TestIdentityManager.java
@@ -53,7 +53,10 @@ public final class TestIdentityManager implements IdentityManager {
@Override
public String lookupAttributeString(
long deviceId, String attributeName, String defaultValue, boolean lookupServer, boolean lookupConfig) {
- return "alarm,result";
+ if (attributeName.equals("filter.skipAttributes")) {
+ return "alarm,result";
+ }
+ return defaultValue;
}
@Override
diff --git a/src/test/java/org/traccar/helper/ChecksumTest.java b/src/test/java/org/traccar/helper/ChecksumTest.java
index ff48527bc..248f4dcae 100644
--- a/src/test/java/org/traccar/helper/ChecksumTest.java
+++ b/src/test/java/org/traccar/helper/ChecksumTest.java
@@ -42,8 +42,11 @@ public class ChecksumTest {
assertEquals(0x00, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x00})));
assertEquals(0x00, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x00, 0x00, 0x00})));
assertEquals(0xca, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x77, 0x77, 0x77, 0x77, 0x77, 0x77})));
+ }
-
+ @Test
+ public void testNmea() {
+ assertEquals("*2A", Checksum.nmea("GSC,011412000010789,M4(Ro=500)"));
}
}
diff --git a/src/test/java/org/traccar/helper/ObdDecoderTest.java b/src/test/java/org/traccar/helper/ObdDecoderTest.java
index 1ffe68c8b..d5071bd51 100644
--- a/src/test/java/org/traccar/helper/ObdDecoderTest.java
+++ b/src/test/java/org/traccar/helper/ObdDecoderTest.java
@@ -9,11 +9,11 @@ public class ObdDecoderTest {
@Test
public void testDecode() {
- assertEquals(83, ObdDecoder.decode(0x01, "057b").getValue());
- assertEquals(1225, ObdDecoder.decode(0x01, "0C1324").getValue());
- assertEquals(20, ObdDecoder.decode(0x01, "0D14").getValue());
- assertEquals(64050, ObdDecoder.decode(0x01, "31fa32").getValue());
- assertEquals(25, ObdDecoder.decode(0x01, "2F41").getValue());
+ assertEquals(83L, ObdDecoder.decode(0x01, "057b").getValue());
+ assertEquals(1225L, ObdDecoder.decode(0x01, "0C1324").getValue());
+ assertEquals(20L, ObdDecoder.decode(0x01, "0D14").getValue());
+ assertEquals(64050L, ObdDecoder.decode(0x01, "31fa32").getValue());
+ assertEquals(25L, ObdDecoder.decode(0x01, "2F41").getValue());
}
diff --git a/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java
index 3a26bb7a7..bb86a9629 100644
--- a/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/AtrackFrameDecoderTest.java
@@ -11,6 +11,10 @@ public class AtrackFrameDecoderTest extends ProtocolTest {
AtrackFrameDecoder decoder = new AtrackFrameDecoder();
verifyFrame(
+ binary("4052698c032a924f000147027fe5d7425f642e56060f031847bb68cb500719e26752c25bebc11c7fddce2b8ed4eff4ed863b187cc6653b5b1c1fc6803884d21aeeedae2ec6e72781d97e95b965610c1d107e5400cd5a7b7b3b592e676091c6a5893d80af9b3c63ae4de20d6e5bc60440bf2c299fbabfe268039d558e4b8589dd5173c926b7f51b916ba29f21d46ff9170793fe450072d691896e114fddce4fd29f7f2f9b74e41ca83814015e8a00ffd1f9bd475e2a44624e074a009455ab5628e39fce8036a09368cf1d2ba0d2653b979c0a00e9edc82335a56d1ee6071401d468b0f4cd761a743d011401d15b4636015721870dd0500695b2edabeabf2f4a00a514645cc83a739ad165f320c1ed401617a0a2800a2803ffd2faa68a004660aa598e00acd8f4d866d54eab3c7994284881fe11ebf5e68034e8a0086e674850927f0ae2bc4dafa5844659674451d39e49a00e1dae23d76ed67bb72211d109e4d5bd756da3b68a4b755021e30076a00cf31431a064e41e6a19a68d5396518f7a00f1ff008bfe27f31068766dbb7e1a723d3fbbf8d79aeb764748b489662be7ccbbb6820e07d4500734caa727765aa32ac0720e28026b4bb9ed7ccf2594798bb5b2a0f1f8f4a82800a2803fffd3f9b97352ae02e45004c808e4f7a997823bd005e86600618f26b7b4a9cab819fa500767a749b9403cd74162b903de803acd1e3c28aebf4d4c81401d05a4441fad682444738a00b712f2055f03e502802b14c5dffbc2ac106343ed4012a905411e94b40051401fffd4faa6992488980c793d0773400d54676df2f1e8b9e054b400564ebb77750c463b442d2119247f08f53e9401e7da85d6a12cd221d427217a856c60fe15caea9689292f2832bfac8777f3a00e67538ef150ff00665d9b4b95fba4aee46f623fa8ae26fbe24f88b49b87d3b5bd2a12e38ca3950e3d41e7228008be2ac02d423dadc09071c1047e791fcab96d77c79acdf92969279113f1c1cb7e7401876c4c939b8ba73230e5998e49ac4d66ee6bebd796462ddb9f4ed40140a12339e9dea225b1824d0025140051401fffd5f9bc676f6a7ae4af6e280255cf5c7153a7b0a0052d8715bba64bf32f39a00ed74694902bb1d306e65c500763a5afca2baed2"),
+ decoder.decode(null, null, binary("4052698c032a924f000147027fe5d7425f642e56060f031847bb68cb500719e26752c25bebc11c7fddce2b8ed4eff4ed863b187cc6653b5b1c1fc6803884d21aeeedae2ec6e72781d97e95b965610c1d107e5400cd5a7b7b3b592e676091c6a5893d80af9b3c63ae4de20d6e5bc60440bf2c299fbabfe268039d558e4b8589dd5173c926b7f51b916ba29f21d46ff9170793fe450072d691896e114fddce4fd29f7f2f9b74e41ca83814015e8a00ffd1f9bd475e2a44624e074a009455ab5628e39fce8036a09368cf1d2ba0d2653b979c0a00e9edc82335a56d1ee6071401d468b0f4cd761a743d011401d15b4636015721870dd0500695b2edabeabf2f4a00a514645cc83a739ad165f320c1ed401617a0a2800a2803ffd2faa68a004660aa598e00acd8f4d866d54eab3c7994284881fe11ebf5e68034e8a0086e674850927f0ae2bc4dafa5844659674451d39e49a00e1dae23d76ed67bb72211d109e4d5bd756da3b68a4b755021e30076a00cf31431a064e41e6a19a68d5396518f7a00f1ff008bfe27f31068766dbb7e1a723d3fbbf8d79aeb764748b489662be7ccbbb6820e07d4500734caa727765aa32ac0720e28026b4bb9ed7ccf2594798bb5b2a0f1f8f4a82800a2803fffd3f9b97352ae02e45004c808e4f7a997823bd005e86600618f26b7b4a9cab819fa500767a749b9403cd74162b903de803acd1e3c28aebf4d4c81401d05a4441fad682444738a00b712f2055f03e502802b14c5dffbc2ac106343ed4012a905411e94b40051401fffd4faa6992488980c793d0773400d54676df2f1e8b9e054b400564ebb77750c463b442d2119247f08f53e9401e7da85d6a12cd221d427217a856c60fe15caea9689292f2832bfac8777f3a00e67538ef150ff00665d9b4b95fba4aee46f623fa8ae26fbe24f88b49b87d3b5bd2a12e38ca3950e3d41e7228008be2ac02d423dadc09071c1047e791fcab96d77c79acdf92969279113f1c1cb7e7401876c4c939b8ba73230e5998e49ac4d66ee6bebd796462ddb9f4ed40140a12339e9dea225b1824d0025140051401fffd5f9bc676f6a7ae4af6e280255cf5c7153a7b0a0052d8715bba64bf32f39a00ed74694902bb1d306e65c500763a5afca2baed2")));
+
+ verifyFrame(
binary("40502c414246342c3532362c3939312c3335363936313037353933313136352c313533343338313635362c313533343338313635392c313533343338313635392c2d38383432393138382c34343237313232352c37302c322c3230303536332c392c312c302c302c302c2c323030302c323030302c1a2c25434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d502c302c3331303236302c31382c392c302c302c3254314b523332453238433730363138352c302c302c302c35342c383930313236303838313231353234373735392c3235322c36302c3132322c34312c3331303236303838313532343737352c302c687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237313232352c2d38382e343239313834201a2c3030393830303442303346343030393830303442303346343030393830303442303346333030393830303442303346343030393830303442303346343030393930303442303346343030393830303442303346343030393830303442303346343030393830303442303346343030393830303442303346331a2c3030343846463130303345381a2c302c3335363936313037353933313136352c302c3938302c31322c302c31382c35302c300d0a"),
decoder.decode(null, null, binary("40502c414246342c3532362c3939312c3335363936313037353933313136352c313533343338313635362c313533343338313635392c313533343338313635392c2d38383432393138382c34343237313232352c37302c322c3230303536332c392c312c302c302c302c2c323030302c323030302c1a2c25434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d502c302c3331303236302c31382c392c302c302c3254314b523332453238433730363138352c302c302c302c35342c383930313236303838313231353234373735392c3235322c36302c3132322c34312c3331303236303838313532343737352c302c687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237313232352c2d38382e343239313834201a2c3030393830303442303346343030393830303442303346343030393830303442303346333030393830303442303346343030393830303442303346343030393930303442303346343030393830303442303346343030393830303442303346343030393830303442303346343030393830303442303346331a2c3030343846463130303345381a2c302c3335363936313037353933313136352c302c3938302c31322c302c31382c35302c300d0a")));
diff --git a/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java
index a604473b8..b329f59f3 100644
--- a/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/AtrackProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class AtrackProtocolDecoderTest extends ProtocolTest {
AtrackProtocolDecoder decoder = new AtrackProtocolDecoder(null);
+ verifyNull(decoder, binary(
+ "4052698c032a924f000147027fe5d7425f642e56060f031847bb68cb500719e26752c25bebc11c7fddce2b8ed4eff4ed863b187cc6653b5b1c1fc6803884d21aeeedae2ec6e72781d97e95b965610c1d107e5400cd5a7b7b3b592e676091c6a5893d80af9b3c63ae4de20d6e5bc60440bf2c299fbabfe268039d558e4b8589dd5173c926b7f51b916ba29f21d46ff9170793fe450072d691896e114fddce4fd29f7f2f9b74e41ca83814015e8a00ffd1f9bd475e2a44624e074a009455ab5628e39fce8036a09368cf1d2ba0d2653b979c0a00e9edc82335a56d1ee6071401d468b0f4cd761a743d011401d15b4636015721870dd0500695b2edabeabf2f4a00a514645cc83a739ad165f320c1ed401617a0a2800a2803ffd2faa68a004660aa598e00acd8f4d866d54eab3c7994284881fe11ebf5e68034e8a0086e674850927f0ae2bc4dafa5844659674451d39e49a00e1dae23d76ed67bb72211d109e4d5bd756da3b68a4b755021e30076a00cf31431a064e41e6a19a68d5396518f7a00f1ff008bfe27f31068766dbb7e1a723d3fbbf8d79aeb764748b489662be7ccbbb6820e07d4500734caa727765aa32ac0720e28026b4bb9ed7ccf2594798bb5b2a0f1f8f4a82800a2803fffd3f9b97352ae02e45004c808e4f7a997823bd005e86600618f26b7b4a9cab819fa500767a749b9403cd74162b903de803acd1e3c28aebf4d4c81401d05a4441fad682444738a00b712f2055f03e502802b14c5dffbc2ac106343ed4012a905411e94b40051401fffd4faa6992488980c793d0773400d54676df2f1e8b9e054b400564ebb77750c463b442d2119247f08f53e9401e7da85d6a12cd221d427217a856c60fe15caea9689292f2832bfac8777f3a00e67538ef150ff00665d9b4b95fba4aee46f623fa8ae26fbe24f88b49b87d3b5bd2a12e38ca3950e3d41e7228008be2ac02d423dadc09071c1047e791fcab96d77c79acdf92969279113f1c1cb7e7401876c4c939b8ba73230e5998e49ac4d66ee6bebd796462ddb9f4ed40140a12339e9dea225b1824d0025140051401fffd5f9bc676f6a7ae4af6e280255cf5c7153a7b0a0052d8715bba64bf32f39a00ed74694902bb1d306e65c500763a5afca2baed2"));
+
verifyPositions(decoder, binary(
"03012c433538312c3135372c342c3335383838373039353933353839342c32303230303430313037353933312c32303230303430313037353933312c32303230303430313037353933312c32373933393534312c2d32363132313934332c3238382c302c3136322c31312c302c302c302c302c2c323030302c323030302c1a2c313537352c302c302c302c3132342c302c31302c302c302c302c302c3132352c302c372c302c0d0a"));
@@ -102,7 +105,7 @@ public class AtrackProtocolDecoderTest extends ProtocolTest {
}
- /*@Test
+ @Test
public void testDecodeCustom() throws Exception {
AtrackProtocolDecoder decoder = new AtrackProtocolDecoder(null);
@@ -119,6 +122,6 @@ public class AtrackProtocolDecoderTest extends ProtocolTest {
verifyPositions(decoder, buffer(
"@P,DCCE,422,5818,357766091026083,1557904779,1557904780,1557904780,-121899644,37406291,129,2,21,10,0,0,0,0,,2000,2000,,13,40,8942310017000752067,21096194,295050910083206,310260,0,FF00001F0393FF01001E0395FF01001E0394FF01001F0393FF02001D0393FF00001F0394FF0100200394FF01001F0393FF02001F0395FF0100200394,20,10,002C005C03B4,14953,357766091026083,125,38,11,0,1,Device:Fail,JN8AZ1MU1BW066090,0,0,0,0,0,99,0,0,0,0,0,264,5,0,0,0,0,0,0,0,0\r\n"));
- }*/
+ }
}
diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
index 24b419953..08b2641aa 100644
--- a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
@@ -11,10 +11,17 @@ public class EelinkProtocolDecoderTest extends ProtocolTest {
EelinkProtocolDecoder decoder = new EelinkProtocolDecoder(null);
+ verifyPositions(decoder, binary(
+ "454c029249a50354679090044671676712004321315f3cf43503fc94d3760c79328a0129000000000a01f9000190330905580d2e046f118a04ec00000000ccc7086c02fe000000000000000000000000000000000000676712004321325f3cf43e03fc94d3760c79328a0129000000000901f9000190330905580d2e046f117b04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321335f3cf44703fc94d3760c79328a0129000000000901f9000190330905580d2e046f117f04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321345f3cf45303fc94d3760c79328a0129000000000901f9000190330905580d2e046f119d04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321355f3cf45c03fc94d3760c79328a0129000000000801f9000190330905580d2e046f11a304ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321365f3cf46603fc94d3760c79328a0129000000000801f9000190330905580d2e046f118804df00000000ccc7086d02ff000000000000000000000000000000000000676712004321375f3cf47103fc94d3760c79328a0129000000000901f9000190330905580d2e046f119704ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321385f3cf47a03fc94d3760c79328a0129000000000901f9000190330905580d2e046f118204ec00000000ccc7086e0300000000000000000000000000000000000000676712004321395f3cf48303fc94d3760c79328a0129000000000901f9000190330905580d2e046f117604df00000000ccc7086e0300000000000000000000000000000000000000"));
+
+ verifyAttribute(decoder, binary(
+ "676714001500035f74a2940201360104591100a7160122250400"),
+ Position.KEY_ALARM, Position.ALARM_REMOVING);
+
verifyNull(decoder, binary(
"454C0027E753035254407167747167670100180002035254407167747100200205020500010432000086BD"));
- verifyAttributes(decoder, binary(
+ verifyPositions(decoder, false, binary(
"454C03EA2B040865284045749185676712013B001E5E8ED5A60000880ED8000000000000000000030003000000000000000000005C550000000000001102F1A00220F0FEA700B10C0BE91510001828D97ECC95D1C400B1030BFD16C00015B3B0FE237EC7C00001040BD816200019BDC691C032E6C000B1020C2015D00003C67480A034F7A30001040BDC1310001CE52429096FDCBA00B1020C1C15F0001F9AEB3CC9C8D49F0001040B7316200014C0A26B6495EFAB0001040ACC1A400013D6313E4CE3EED700B1030BF516800016BE49BD3E05EDB00001040BF01620001DDB15BDE209E4A800B10C0BE515300014BA13142CB0C7AA0001040BD815D00019D39722BB52DBA30001040BF317500011020C850118EFC50001020A8115F000188B659FD04CD1AD0001040BE816000014AC91F5C96BDBAC0001040BF214B0001495CDFA62B6D2AB00B10C0BDB15C00013676712014B001F5E8ED5AC0000880ED8000000000000000000030003000000000000000000005F8D0000000000001202B3B0FE237EC7C00001040BD816200019BDC691C032E6C000B1020C2015D00003C67480A034F7A30001040BDC1310001CE52429096FDCBA00B1020C1C15F0001FBE49BD3E05EDB00001040BF01620001DDB15BDE209E4A800B10C0BE515300014BA13142CB0C7AA0001040BD815D00019D39722BB52DBA30001040BF317500011020C850118EFC50001020A8115F000188B659FD04CD1AD0001040BE816000014AC91F5C96BDBAC0001040BF214B0001495CDFA62B6D2AB00B10C0BDB15C00013F1A00220F0FEA700B10C0BE9152000189AEB3CC9C8D4A30001040B731620001428D97ECC95D1C200B1030C0016C00015372CFB9E84C3A00029020F5100000000D6313E4CE3EED700B1030BF716800016C0A26B6495EFB10001040ACC1A400013676712014B00205E8ED5B10000880ED8000000000000000000030003000000000000000000005C550000000000001202B3B0FE237EC7C00001040BD816200019BDC691C032E6C000B1020C2015D00003DB15BDE209E4A800B10C0BE515300014BA13142CB0C7AA0001040BD815D00019D39722BB52DBA30001040BF317500011020C850118EFC50001020A8115F000188B659FD04CD1AD0001040BE81600001495CDFA62B6D2AB00B10C0BDB15C000139AEB3CC9C8D4A30001040B731620001428D97ECC95D1C200B1030C0016C00015372CFB9E84C3A00029020F5100000000C0A26B6495EFB10001040ACC1A400013BE49BD3E05EDA90001040BF01620001DAC91F5C96BDBA50001040BF014B00014F1A00220F0FE9F00B10C0BE915200018E52429096FDCC200B1020C1B15F0001FD6313E4CE3EED200B1030BF716800016C67480A034F7A40001040BDC1300001C"));
verifyAttribute(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
index 88a460854..48b74c904 100644
--- a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
@@ -12,6 +12,9 @@ public class FifotrackProtocolDecoderTest extends ProtocolTest {
FifotrackProtocolDecoder decoder = new FifotrackProtocolDecoder(null);
verifyPosition(decoder, buffer(
+ "$$118,863003046473534,258,A01,,201007231735,V,3.067783,101.672858,0,176,96,189890,0,A0,03,0,502|19|5C1|93349F,196|4E0|6C,1,*13"));
+
+ verifyPosition(decoder, buffer(
"$$116,869270049149999,5,A01,4,190925080127,V,-15.804260,35.061506,0,0,1198,0,0,900000C0,02,0,650|10|12C|B24,18B|4C8|72,1,*01"));
verifyAttribute(decoder, buffer(
diff --git a/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java b/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java
new file mode 100644
index 000000000..fb1e395f1
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FutureWayFrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FutureWayFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FutureWayFrameDecoder decoder = new FutureWayFrameDecoder();
+
+ verifyFrame(
+ binary("34313030303030303346323030303032302c494d45493a3335343832383130303132363436312c62617474657279206c6576656c3a362c6e6574776f726b20747970653a372c4353513a323336463432"),
+ decoder.decode(null, null, binary("34313030303030303346323030303032302c494d45493a3335343832383130303132363436312c62617474657279206c6576656c3a362c6e6574776f726b20747970653a372c4353513a323336463432")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java
new file mode 100644
index 000000000..951b3bb20
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FutureWayProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FutureWayProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ FutureWayProtocolDecoder decoder = new FutureWayProtocolDecoder(null);
+
+ verifyNull(decoder, text(
+ "410000003F2000020,IMEI:354828100126461,battery level:6,network type:7,CSQ:236F42"));
+
+ verifyPosition(decoder, text(
+ "410000009BA00004GPS:V,200902093333,0.000000N,0.000000E,0.000,0.000\r\nWIFI:3,1|90-67-1C-F7-21-6C|52&2|80-89-17-C6-79-A0|54&3|40-F4-20-EF-DD-2A|58\r\nLBS:460,0,46475066,69\r\n6A42"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
index 12e64cd6d..915876609 100644
--- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
@@ -11,6 +11,22 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
Gl200TextProtocolDecoder decoder = new Gl200TextProtocolDecoder(null);
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTERI,DE0115,865284042104863,gl500m,00000100,0,0,1,2,0.0,0,36.9,-1.844589,52.177779,20201006125701,0234,0015,0135,34A1,19,0,,79,1,,0,20201006125723,184D$"),
+ Position.KEY_BATTERY_LEVEL, 79);
+
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTFRI,DE0114,865284042140479,,0,0,1,1,0.0,0,28.0,-118.268093,33.975430,20200901105954,0311,0480,3500,00D07F02,18,0,,93,0,,,20200901110000,0355$"),
+ Position.KEY_BATTERY_LEVEL, 93);
+
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTFRI,DE0114,865284041308986,,0,0,1,0,0.0,0,245.6,-117.678624,34.032081,20200825030332,0311,0480,3304,00C7F601,24,0,,85,1,1,,20200825030738,03B1$"),
+ Position.KEY_BATTERY_LEVEL, 85);
+
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTFRI,380903,869606020188383,,,40,1,2,43.4,80,252.4,8.606297,50.700200,20200721090109,0262,0001,1932,1BA4,00,0.0,,,,0,220100,,,,20200721090110,00B9$"),
+ Position.PREFIX_IN + 1, false);
+
verifyPositions(decoder, buffer(
"+RESP:GTFRI,423031,355154083021002,Bolt4G,0,0,0,0,1,1.0,0.2,0,245.3,-85.630193,42.975280,20190729185934,310,410,500b,B0E320F,31,-1,100,20190729185934,0010$"));
diff --git a/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java
new file mode 100644
index 000000000..8cfa9299b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/GlobalSatProtocolEncoderTest.java
@@ -0,0 +1,39 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+import static org.junit.Assert.assertEquals;
+
+public class GlobalSatProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodeAlarmDismiss() {
+
+ GlobalSatProtocolEncoder encoder = new GlobalSatProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ALARM_DISMISS);
+
+ assertEquals("GSC,123456789012345,Na*48!", encoder.encodeCommand(command));
+
+ }
+
+ @Test
+ public void testEncodeOutputControl() {
+
+ GlobalSatProtocolEncoder encoder = new GlobalSatProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_OUTPUT_CONTROL);
+ command.set(Command.KEY_INDEX, 1);
+ command.set(Command.KEY_DATA, "1");
+
+ assertEquals("GSC,123456789012345,Lo(1,1)*69!", encoder.encodeCommand(command));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java
index f7bf8f514..75d30ec47 100644
--- a/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/GlobalstarProtocolDecoderTest.java
@@ -11,6 +11,17 @@ public class GlobalstarProtocolDecoderTest extends ProtocolTest {
GlobalstarProtocolDecoder decoder = new GlobalstarProtocolDecoder(null);
+ verifyNull(decoder, request(HttpMethod.POST, "/", buffer(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
+ "<stuMessages xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://cody.glpconnect.com/XSD/StuMessage_Rev1_0_1.xsd\" timeStamp=\"16/09/2020 01:33:07 GMT\" messageID=\"567207180ae9100687cef8c81978371a\">\n",
+ "<stuMessage>\n",
+ "<esn>0-4325340</esn>\n",
+ "<unixTime>1600220003</unixTime>\n",
+ "<gps>N</gps>\n",
+ "<payload length=\"9\" source=\"pc\" encoding=\"hex\">0x63FFFF1BB4FFFFFFFF</payload>\n",
+ "</stuMessage>\n",
+ "</stuMessages>")));
+
verifyPositions(decoder, request(HttpMethod.POST, "/", buffer(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
"<stuMessages xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"http://cody.glpconnect.com/XSD/StuMessage_Rev1_0_1.xsd\" timeStamp=\"25/03/2020 03:02:32 GMT\" messageID=\"300421a0fd2a100585bdde409d6f601a\">",
diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
index 25d6870bc..e6b7b7ce3 100644
--- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
@@ -18,6 +18,13 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest {
"78780D01086471700328358100093F040D0A"));
verifyAttribute(decoder, binary(
+ "7979000E9B0332382E33A1E60D0A0289BE490D0A"),
+ Position.PREFIX_TEMP + 1, 28.3);
+
+ verifyPosition(decoder, binary(
+ "7878353714080d05000ac500a886eb0b7522f000100001fe0a05ea004f1b000001002e0400002328003b0217c0003c0401020001002c468a0d0a"));
+
+ verifyAttribute(decoder, binary(
"79790020940a035985708236675805200502187214018966051912408052452f000355560d0a"),
Position.KEY_ICCID, "8966051912408052452");
diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
index b624f69ab..9ba31e08a 100644
--- a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class HuaShengProtocolDecoderTest extends ProtocolTest {
@@ -16,6 +17,10 @@ public class HuaShengProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"c000000077aa0200000000000e000100143347315f48312e315f56312e30372e54000300133335353835353035303434303635380004000b3531323030303000050005010006000400070004000800050000090018383936313032353431343533333239313833360d000a000f796573696e7465726e6574c0"));
+ verifyAttribute(decoder, binary(
+ "c000000049aa0000000000028e8800000032303038323630373534323800e1d47fffcd163d0000000000f30000000100157703f8000046000000000aade0ffffffff0011000800000496c0"),
+ Position.KEY_HOURS, 58.7);
+
verifyNotNull(decoder, binary(
"c000000077aa00000000000070020000003230303132373035313635330000000000000000000000000000000000010015ffffffff000000000000019dffffffffff0005000a1f00000e455a00200019313238354031406666666540386233663930634030000f0013333536373236313038313335343530c0"));
diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
index f600a77d4..f44accc57 100644
--- a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
@@ -15,6 +15,10 @@ public class HuabaoProtocolDecoderTest extends ProtocolTest {
"7E01000021013345678906000F002C012F373031313142534A2D4D3742203030303030303001D4C1423838383838B47E"));
verifyAttribute(decoder, binary(
+ "7e070400db07904116875014480003010046000000000000000b020057d40072fb9c0064017200f220090215230301040000092930011f31010ceb1c000c00b28921200272401241734f00060089ffffffef000400ce1d460046000000000000000b0200570c0072fdb2005f013600ee20090215230801040000092a30011f31010ceb1c000c00b28921200272401241734f00060089ffffffef000400ce1e800046000000000000000b020056ac0072fe9b005a00d200ec20090215231301040000092a30011b31010ceb1c000c00b28921200272401241734f00060089ffffffef000400ce1e35d77e"),
+ Position.KEY_POWER, 74.94);
+
+ verifyAttribute(decoder, binary(
"7e550104337401903111850622072002454206133574075359513a0000080100000001aa00005ded05e203000000000c06005affb5ffb40a0302dc65100100137e"),
Position.KEY_CHARGE, true);
diff --git a/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java
index 00b7de094..eb6480213 100644
--- a/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/IntellitracProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class IntellitracProtocolDecoderTest extends ProtocolTest {
IntellitracProtocolDecoder decoder = new IntellitracProtocolDecoder(null);
+ verifyPosition(decoder, text(
+ "359316075744331,20201008181424,12.014662,57.826301,0,76,24,10,997,3,0,0.000,4.208,20201008181424,0"));
+
verifyNull(decoder, text(
"$OK:TRACKING"));
diff --git a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
index 4f5a63188..69abfd837 100644
--- a/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/ItsProtocolDecoderTest.java
@@ -14,6 +14,10 @@ public class ItsProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, text(
"$LGN,,869867037009679,3.2AIH,9.99546000,N,76.35886167,E"));
+ verifyAttribute(decoder, text(
+ "$,C,CTPL,4.0.0,NR,01,L,869247045166383,NA00000000,1,12032020,144453,30.452524,N,077.610351,E,1.4,34.8,14,384.19,1.8,0.8,IDEA P,1,1,14.2,4.17,0,C,22,404,82,0FB1,3B26,516B,0FB1,18,3B25,0FB1,15,5169,0FB1,14,3B27,0FB1,13,0000,00,8083,194.9,0B,*,IP=106.67.5.173"),
+ Position.KEY_ODOMETER, 194900.0);
+
verifyPosition(decoder, text(
"$,EPB,SEM,868997031721531,NM,14072020112020,A,28.359959,N,076.927566,E,260.93,0.1,0.0,G,NA00000000,N.A0000000,*"));
diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
index 9f9da26ca..ea7ea19a2 100644
--- a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class MeitrackProtocolDecoderTest extends ProtocolTest {
@@ -10,6 +11,13 @@ public class MeitrackProtocolDecoderTest extends ProtocolTest {
MeitrackProtocolDecoder decoder = new MeitrackProtocolDecoder(null);
+ verifyPositions(decoder, binary(
+ "2424413132332c3836313538353034333230303836322c4343452c010000000100590015000305010609071b0b081c000939010a07000b1700199e011a9505921a0099c4089c5500c93e00405a000602a8b114000343f12e0604d18806270c654a2e000da20537009bb8963904010e0c0d020300aa7a0af69e0100002a35340d0a"));
+
+ verifyAttribute(decoder, buffer(
+ "$$F153,867144025101013,AAA,35,25.219431,55.279918,200916155923,V,0,25,0,0,0.0,0,249701532,98374503,424|2|101C|A3AE,0800,0000|0000|0000|02D3|0103,00000011,*A0"),
+ Position.KEY_INPUT, 8);
+
verifyPosition(decoder, buffer(
"$$O160,863835028611502,AAA,35,7.887840,98.375193,200202020238,A,12,4,0,279,0.6,45,32121,442492,520|3|12DF|015273E2,0000,0000|0000|0000|018D|04F0,00000001,,1,0000*F3"));
diff --git a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
index 605a02b92..22f0974ec 100644
--- a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class MictrackProtocolDecoderTest extends ProtocolTest {
MictrackProtocolDecoder decoder = new MictrackProtocolDecoder(null);
+ verifyNull(decoder, text(
+ "mode=Success!"));
+
verifyPosition(decoder, text(
"MT;6;866425031361423;R0;10+190109091803+22.63827+114.02922+2.14+69+2+3744+113"),
position("2019-01-09 09:18:03.000", true, 22.63827, 114.02922));
diff --git a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
index 5898b74a8..00c33ec82 100644
--- a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
@@ -13,16 +13,22 @@ public class Minifinder2ProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"ab10150076f1320003100133353534363530373130323933303602105a"));
+ verifyPositions(decoder, binary(
+ "ab10cf0382171a0c0110013836353036383034303030303132380924d2a85b5f010296411620d8d9cf13283b5fc601000000e4065500ac4001000b0924dca85b5f0102964116205fd4cf13c53d5fc6010099009f074300ac4001000b0924e6a85b5f010296411620dbd0cf13b83b5fc60d00df00d0074300ac4001000b0924f0a85b5f010296411620a0cccf1339195fc61c000a01fd074000ac4001000b0924faa85b5f010296411620efd9cf135f0b5fc61f00a600b0074200124101000b092404a95b5f010296411620c1eecf13de235fc627005400d3074300124101000b09240ea95b5f010296411620b6f0cf139d505fc628005400280846007d4101000b092418a95b5f01029641162041f2cf13d5835fc62b0056006a084600514201000b092422a95b5f0102964116204ee6cf137ea25fc62100a00091084000514201000b09242ca95b5f01029641162042d6cf130a9a5fc62100fd0085074300b74201000b092436a95b5f010296411620a6d4cf132b6a5fc628000a01250841001e4301000b092440a95b5f01029641162039d1cf1336445fc61200100127084000894301000b09244aa95b5f010296411620bad5cf13eb3d5fc601004e001d084c00894301000b092454a95b5f01029641162042d6cf13163e5fc6000004006e084500894301000b09245ea95b5f01029641162008d9cf1322395fc60100000032084000894301000b092468a95b5f010296411620bfd5cf1345395fc60000080069084c00894301000b092472a95b5f01029641162001d7cf13e8375fc6000000003e085200894301000b09247ca95b5f0102964116204fd7cf136c3a5fc60000000055085400894301000b092486a95b5f010296411620d2d7cf1364395fc600000000a8076c00894301000b092490a95b5f010296411620fdd6cf13383a5fc6000000003c084d00894301000b09249aa95b5f01029641162049d7cf13d9395fc600000000be076c00894301000b0924a4a95b5f010296411620e8d5cf133f375fc6000000001a085900894301000b0924aea95b5f01029641162080d9cf13e3395fc60000000080076c00894301000b0924b8a95b5f010296411620a1d9cf13ac395fc6000000006f075b00894301000b0924c2a95b5f010296411620f3d9cf13b53a5fc60000000090074f00894301000b0924cca95b5f010296411620a1d7cf13aa385fc6000000001d084d00894301000b0924d6a95b5f0102964116208fd8cf1393385fc600000000fc074d00894301000b0924e0a95b5f0102964116206dd7cf13ae395fc600000000ef074d00894301000b0924eaa95b5f01029641162044d8cf1388395fc600000000a5075200894301000b"));
+
+ verifyNotNull(decoder, binary(
+ "ab10ba034461360201100138363339323130333135313139363009245b364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda092464364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda09246e364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda09246f364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda092478364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda092482364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda09248c364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda092496364f5f3200ae646a22a76ce873a3ee50b8547595374807b2fad027610438b504d3b5b7819cb2943bb04351a3b094bf8087b878b8502b7306cb11aec85ba0a15044a7dcfe18bdcdf8ac1226a6964e1ab00c8447c13e5dbf480eecf0e436a1c486e987b794a5b8f88340d7b5ad9ca615c91cda"));
+
verifyNotNull(decoder, binary(
"ab1024009b3f9742011001383635323039303336333430303235113154cfc95d0a00000080d0c95d0a000000"));
- verifyPosition(decoder, binary(
+ verifyPositions(decoder, binary(
"ab103f007e2533000110013335353436353037313032393330360930e09d245d210100000924b49e245d01025b201620e6c03b1ef367420400000000aa026d00c90e0000100110"));
- verifyAttributes(decoder, binary(
+ verifyNotNull(decoder, binary(
"ab1845005d39370301100133353836383830303030303338303209245b92b55c84004b610502001000002221ca00050b4a005cc30f4a0056c80f4a003ba90e4a0055c8074a005dc3034a0057c8"));
- verifyAttributes(decoder, binary(
+ verifyNotNull(decoder, binary(
"ab185c001db78b03011001333538363838303030303033383032092448bd8a5c82003b130502010000003922ca923bad10f794bd30b5c2cb0595b2944a0c49a4f9b6a4b1e9991e79ba0026bb78c08fb4581faae7ee3fb0e091f5778e96b074a78ed46528"));
verifyNotNull(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java
new file mode 100644
index 000000000..9b3f6823a
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/MoovboxProtocolDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class MoovboxProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ MoovboxProtocolDecoder decoder = new MoovboxProtocolDecoder(null);
+
+ verifyPositions(decoder, request(HttpMethod.POST, "/",
+ buffer("<gps id=\"911\">\n<coordinates><coordinate>\n<fix>3</fix>\n<time>1597580050</time>\n<latitude>100.726257</latitude>\n<longitude>13.821351</longitude>\n<altitude>9.500000</altitude>\n<climb>0.000000</climb>\n<speed>0.064000</speed>\n<separation>-27.300000</separation>\n<track>0.000000</track>\n<satellites>9</satellites>\n</coordinate></coordinates>\n</gps>")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java
new file mode 100644
index 000000000..678e5c6c3
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NetProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NetProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ NetProtocolDecoder decoder = new NetProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "@L03686090604017761712271020161807037078881037233751000000010F850036980A4000"));
+
+ verifyPosition(decoder, text(
+ "@L0368609060401776171223102005072803703296103721462100008009000000300B12B000"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java
new file mode 100644
index 000000000..61ee6771f
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PolteProtocolDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpMethod;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PolteProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PolteProtocolDecoder decoder = new PolteProtocolDecoder(null);
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"_id\":\"5f75cf7b02c5023bfc0beaf7\",\"location\":{\"LocationMetrics\":{\"EnvironmentDensity\":1,\"LocationType\":2,\"carrierInfo\":{\"aux\":{\"PLMN\":\"310410\",\"country\":\"United States\",\"name\":\"ATT Wireless Inc\"},\"crs\":{\"PLMN\":\"310410\",\"country\":\"United States\",\"name\":\"ATT Wireless Inc\"}},\"hdop\":1850000,\"leversion\":\"2.2.18-20200729T140651\",\"towercount\":1},\"altitude\":0.0011297669261693954,\"confidence\":783.7972188868215,\"detected_at\":1601556342,\"latitude\":29.77368956725161,\"longitude\":-98.26530342694024,\"towerDB\":\"default\",\"ueToken\":\"ALT12503DE04336CB2E3A4A113FCDE05DF05A6F\",\"uid\":\"WZuDMv5Je\"},\"report\":{\"battery\":{\"count\":555,\"level\":100,\"voltage\":3.52},\"event\":3,\"time\":\"2020-10-01T12:45:48.207Z\"},\"time\":\"2020-10-01T12:45:42Z\",\"ueToken\":\"ALT12503DE04336CB2E3A4A113FCDE05DF05A6F\",\"uid\":\"WZuDMv5Je\"}")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java b/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java
new file mode 100644
index 000000000..bc458c398
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PstFrameEncoderTest.java
@@ -0,0 +1,20 @@
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class PstFrameEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ PstFrameEncoder encoder = new PstFrameEncoder();
+
+ ByteBuf result = Unpooled.buffer();
+ encoder.encode(null, binary("2FAF0B10059A0000B001022FAF0B10E91349A2AD3B1DAD2FF8A78228A58F"), result);
+ verifyFrame(binary("282FAF0B10059A0000B001022FAF0B10E91349A2AD3B1DAD2FF8A7822768A58F29"), result);
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java
new file mode 100644
index 000000000..ddf6d460c
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/PstProtocolEncoderTest.java
@@ -0,0 +1,35 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class PstProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncodeEngineStop() {
+
+ PstProtocolEncoder encoder = new PstProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_STOP);
+
+ verifyCommand(encoder, command, binary("860ddf790600000001060002ffffffffe42b"));
+
+ }
+
+ @Test
+ public void testEncodeEngineResume() {
+
+ PstProtocolEncoder encoder = new PstProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_ENGINE_RESUME);
+
+ verifyCommand(encoder, command, binary("860ddf790600000001060001ffffffff0af9"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java
index cb1c1eb0e..a7c8be21f 100644
--- a/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Pt502ProtocolDecoderTest.java
@@ -15,6 +15,9 @@ public class Pt502ProtocolDecoderTest extends ProtocolTest {
"24504844302c3936302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00e5292800ef450020a2800a2801d49400b450014b40052e2800a69340094a05007fffd0e5d14b10055b51b00c76a00527273494005250014500251400525001450015347c25003a928010d25007ffd1e52909a00290d0014b40052d0014500145002e297b50018a280109a6d002d2e2803fffd2e7a04da3777a94fbd0025140052500145002514005250014940054e381400b494008690d007fffd3e4f345001486800a5a005a2800a2801680280168a002909e280100cd028016a48937bfb5007fffd4c5038a42280128a004a280128a003ad2500251400945002a8cb0a9a80133450026692803ffd5e4e8a004a2801694500145002d18a005c5140052e280109a69a0029680140abb147b139eb401ffd6c62290d00251400949400114940052500252d002525003e31c93525002521a004a4a00ffd7e4a8a00281400a29d40094b40053ba500252d0018a31400d3cd250018cd2d005ab58777ccdd074ab645007ffd0c72290d00348a2801280"));
verifyPosition(decoder, buffer(
+ "$POS,718005258,121930.000,V,0514.0960,S,14547.3245,E,0.0,0.0,151020,,/00000,00000/0,0,0,0/106945600//f00//"));
+
+ verifyPosition(decoder, buffer(
"$PHO3821-1,1156802639,022125.000,A,0707.0014,N,07307.3725,W,0.0,0.1,110418,,,A/00000,00000/0,0,0,0/500//fd4//"));
verifyPosition(decoder, buffer(
diff --git a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
index 53f03730a..f31773e0c 100644
--- a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
@@ -12,7 +12,8 @@ public class SigfoxProtocolDecoderTest extends ProtocolTest {
SigfoxProtocolDecoder decoder = new SigfoxProtocolDecoder(null);
- verifyPosition(decoder, request(HttpMethod.POST, "/", buffer("{ \"device\":\"BFE47E\", \"time\":1590497040, \"data\":\"10297eb01e621122070000be\", \"seqNumber\":8, \"deviceTypeId\":\"5ecb8bfac563d620cc9e6798\", \"ack\":false }")));
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{ \"device\":\"BFE47E\", \"time\":1590497040, \"data\":\"10297eb01e621122070000be\", \"seqNumber\":8, \"deviceTypeId\":\"5ecb8bfac563d620cc9e6798\", \"ack\":false }")));
verifyAttributes(decoder, request(HttpMethod.POST, "/",
buffer("{\"messageType\":\"accelerometer\",\"deviceId\":\"testdev001\",\"snr\":\"1234\",\"rssi\":\"-120.00\",\"station\":\"5678\",\"seqNum\":\"9123\",\"newPosition\":false,\"latitude\":\"null\",\"longitude\":\"null\",\"positionTime\":\"null\",\"moving\":true,\"magChange\":\"true\",\"magStatus\":\"true\",\"temperature\":\"7.5\",\"battery\":\"null\",\"batteryPercentage\":\"null\",\"lastSeen\":\"1582560425\",\"fwVersion\":\"null\",\"dlConfig\":\"null\",\"recievedPayload\":\"cb020051\"}")));
diff --git a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java
index 459dad978..1dd96d8ca 100644
--- a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java
@@ -11,6 +11,18 @@ public class StarLinkProtocolDecoderTest extends ProtocolTest {
StarLinkProtocolDecoder decoder = new StarLinkProtocolDecoder(null);
+ decoder.setFormat("#IMEI#,#EDT#,#PDT#,#LAT#,#LONG#,#SPD#,#IGN#,#ODO#,#DUR#,#TDUR#,#LAC#,#CID#,#VIN#,#VBAT#,#EID#,#EDSC#,#DRV#,#SATU#,#CSS#,#OUT1#,#OUT2#,#IN2#,#IND#");
+
+ verifyAttribute(decoder, text(
+ "$SLU351580050543640,06,101,351580050543640,200927184734,200927184724,+4222.4186,+00153.1426,000.0,0,000008,,582,21269,214241628,00.213,03.407,09,Lost Power,0,5,96,0,0,0,1,,,,,,,,,,,,*10"),
+ Position.KEY_RSSI, 96);
+
+ decoder.setFormat("#EDT#,#EID#,#PDT#,#LAT#,#LONG#,#SPD#,#HEAD#,#ODO#,#LAC#,#CID#,#VIN#,#VBAT#,#TI1#,#TS1#,#TV1#,#TH1#,#TD1#,#EDSC#,#TI2#,#TS2#,#TV2#,#TH2#,#TD2#");
+
+ verifyAttribute(decoder, text(
+ "$SLU351580050356894,06,1192,200811104100,01,200811104058,+4121.8168,+00205.1158,000.0,069,000069,2080,15139043,12.221,03.533,D469062D44D6,1,29.7,56.9,CAEaBtRpBi1E1jhLSJoYKLkEINIEWABgAmgAcAJ4AIABAIgBAA==,Location,D2567108639E,1,30.2,55.3,CAIaBtJWcQhjnjhPSPIXKKkEINwEWEpgA2gAcAJ4AIABAIgBAA==,1,99*5A"),
+ "sensor2Voltage", 3058 * 0.001);
+
decoder.setFormat("#IMEI#,#EDT#,#PDT#,#LAT#,#LONG#,#SPD#,#IGN#,#ODO#,#DUR#,#TDUR#,#LAC#,#CID#,#VIN#,#VBAT#,#EID#,#EDSC#,#DRV#,#SATU#,#CSS#,#OUT1#,#OUT2#");
verifyAttribute(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
index 549402697..847f6707d 100644
--- a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
@@ -63,12 +63,28 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
}
@Test
+ public void testDecodeDriver() throws Exception {
+
+ SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
+
+ verifyAttribute(decoder, buffer(
+ "ST300HTE;511050566;45;308;20200909;13:38:38;0;12.50;001354;0.0;1;0;1;1;0;-27.636632;-052.277933;-27.636675;-052.277947;000.000;002.296;0;00000000000000"),
+ Position.KEY_DRIVER_UNIQUE_ID, "00000000000000");
+
+ verifyAttribute(decoder, buffer(
+ "ST300HTE;100850001;04;248;20110101;00:13:52;167559;12.28;004005;0.0;1;0;3;3;0;-22.881018;-047.070831;-22.881018;-047.070831;000.000;000.000;0;0;3;0;0;0;01E04D44160000"),
+ Position.KEY_DRIVER_UNIQUE_ID, "01E04D44160000");
+
+ }
+
+ @Test
public void testDecode() throws Exception {
SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
- verifyPosition(decoder, buffer(
- "ALT;0470001109;BFFFFF;47;1.0.2;0;20200511;22:39:38;0B29790F;310;410;2C13;30;+34.928093;-83.704295;0.94;30.95;10;1;00000001;00000000;15;;;00018003;4.1;14.13;629;11905"));
+ verifyAttribute(decoder, buffer(
+ "STT;0560001616;BFFFFF;56;1.0.15;1;20200219;20:52:25;00008D6C;334;20;0925;24;+20.741764;-103.430364;0.00;0.00;19;1;00000001;00000000;2;1;1765;00008003;0.0;12.14;136598"),
+ Position.KEY_INDEX, 1765);
verifyAttribute(decoder, buffer(
"STT;0560001616;BFFFFF;56;1.0.15;1;20200219;20:52:25;00008D6C;334;20;0925;24;+20.741764;-103.430364;0.00;0.00;19;1;00000001;00000000;2;1;1765;00008003;0.0;12.14;136598"),
diff --git a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java
index c0511f2a1..25fef58f1 100644
--- a/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/T55ProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class T55ProtocolDecoderTest extends ProtocolTest {
T55ProtocolDecoder decoder = new T55ProtocolDecoder(null);
+ verifyPosition(decoder, text(
+ "QZE,868994033976700,35,28062020,113553,22.13673,114.57263,0,22,A,0"));
+
verifyNull(decoder, text(
"$DEVID,0x0103846677F21422*41"));
diff --git a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
index 48c535c96..d2532d091 100644
--- a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
@@ -11,6 +11,10 @@ public class T800xProtocolDecoderTest extends ProtocolTest {
T800xProtocolDecoder decoder = new T800xProtocolDecoder(null);
+ verifyAttribute(decoder, binary(
+ "2727020049052e086528404072393849002008060310110000000068b7c8c286eaa441000000008000008100001617410700019ce782b0001e000002581e00000530d4801f00000000"),
+ Position.KEY_BATTERY_LEVEL, 100);
+
verifyPosition(decoder, binary(
"262602005308090865284040309670000f000f0f0000005a47c000050100000020000000008bfd0020022505185300004041dcc9d6c243b3c6410000012712400000000009e2ffffffffffffffffffffffff09"));
diff --git a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
index 5b2df38db..2f43733c4 100644
--- a/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
@@ -3,7 +3,6 @@ package org.traccar.protocol;
import org.junit.Ignore;
import org.junit.Test;
import org.traccar.ProtocolTest;
-import org.traccar.model.Position;
public class TeltonikaProtocolDecoderTest extends ProtocolTest {
@@ -15,6 +14,9 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"000F313233343536373839303132333435"));
+ verifyPositions(decoder, binary(
+ "00000000000000858e0200000174431aadc100061d888f21000e8a0032002e0c000001810001000000000000000000010181001711210102030405060708090a0b0c0d0e0f10020b010ad000000174431a389100061d888f21000e8a0033002e0d000001810001000000000000000000010181001711210102030405060708090a0b0c0d0e0f10020b010ad2020000492b"));
+
verifyPositions(decoder, false, binary(
"000000000000001C0D01050000001056E924222347455420444154414F524445520D0A0100004990"));
@@ -71,16 +73,16 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest {
verifyPositions(decoder, binary(
"000000000000003508010000014f8e016420002141bbaf0f4e96a7fffa0000120000000602010047030242669c92000002c7000000009100000000000100002df3"));
-
+
verifyPositions(decoder, binary(
"00000000000000A7080400000113fc208dff000f14f650209cca80006f00d60400040004030101150316030001460000015d0000000113fc17610b000f14ffe0209cc580006e00c00500010004030101150316010001460000015e0000000113fc284945000f150f00209cd200009501080400000004030101150016030001460000015d0000000113fc267c5b000f150a50209cccc0009300680400000004030101150016030001460000015b00040000"));
-
+
verifyPositions(decoder, binary(
"000000000000014708060000013e5a60a4cb003fa7b780fc424518004200000a000000090501010200b300b400f000034268a746011818000001c700000000000000013e5dc8ba28003fa7c080fc4246040001000005000000090501010200b300b400f001034268b44600ef18000001c700000000000000013e5dc90455003fa7b640fc424388003a0000070000f0090501010200b300b400f000034268dc4600f718000001c70000001d000000013e5dc9d368003fa7b800fc4244300049000004000000090501010200b300b400f001034267de46010718000001c700000000000000013e5dca311d003fa7b680fc4243cc00420000070000f0090501010200b300b400f0000342685346010b18000001c700000000000000013e5dcfafe9003fa7b600fc4242f0003d000008000000090501010200b300b400f0000342685246011918000001c700000000000600000275"));
verifyPositions(decoder, binary(
"000000000000002c08010000013eff8d6f9800173295002111f400008100ae0b0000000401010003090016432980422f7200000100007a5d"));
-
+
verifyPositions(decoder, binary(
"00000000000000c7070441bf9db00fff425adbd741ca6e1e009e1205070001030b160000601a02015e02000314006615000a160067010500000ce441bf9d920fff425adbb141ca6fc900a2b218070001030b160000601a02015e02000314006615000a160067010500000cc641bf9d740fff425adbee41ca739200b6c91e070001030b1f0000601a02015f02000314006615000a160066010500000ca841bf9cfc0fff425adba041ca70c100b93813070001030b1f0000601a02015f02000314002315000a160025010500000c3004000000"));
diff --git a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java
index 01d63bfa3..31e748f27 100644
--- a/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/UlbotechFrameDecoderTest.java
@@ -3,8 +3,6 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
-import static org.junit.Assert.assertEquals;
-
public class UlbotechFrameDecoderTest extends ProtocolTest {
@Test
diff --git a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java
index 8c820183b..a2bc1b46e 100644
--- a/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/UlbotechProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class UlbotechProtocolDecoderTest extends ProtocolTest {
UlbotechProtocolDecoder decoder = new UlbotechProtocolDecoder(null);
+ verifyPosition(decoder, binary(
+ "f801010353323083177450a703f6f0010efe55a31a0923d01400050070007003040a42000004040070cca00506039b1876220f060800000000000000000725310553410c0c9e310d05310f4641100440311119411f00476101810f8000310487411f00480804203a14c009033320159310f8"));
+
verifyNull(decoder, buffer(
"*TS01,353323081464660#"));
diff --git a/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java
index 8fc628bdb..b91cd0ebf 100644
--- a/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/XexunFrameDecoderTest.java
@@ -3,8 +3,6 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
-import static org.junit.Assert.assertEquals;
-
public class XexunFrameDecoderTest extends ProtocolTest {
@Test
@@ -12,11 +10,11 @@ public class XexunFrameDecoderTest extends ProtocolTest {
XexunFrameDecoder decoder = new XexunFrameDecoder();
- assertEquals(
+ verifyFrame(
binary("4750524d432c3230353933352e3030302c412c353134302e343335302c4e2c3530312e303638362c452c302e30302c302e30302c3132313031352c30302c303030302e302c412a37302c462c2c696d65693a3335393538373031343731383339322c"),
decoder.decode(null, null, binary("313531303132313435392c2b33313635323435343932372c4750524d432c3230353933352e3030302c412c353134302e343335302c4e2c3530312e303638362c452c302e30302c302e30302c3132313031352c30302c303030302e302c412a37302c462c2c696d65693a3335393538373031343731383339322c31323249")));
- assertEquals(
+ verifyFrame(
binary("4750524d432c3130333733312e3633362c412c343534352e353236362c4e2c30303434382e383235392c452c32312e31322c3237362e30312c3135303631352c2c2c412a35372c4c2c2c20696d65693a3031333934393030323032363637352c"),
decoder.decode(null, null, binary("3135303631353132333733312c2b33333634373338343631312c4750524d432c3130333733312e3633362c412c343534352e353236362c4e2c30303434382e383235392c452c32312e31322c3237362e30312c3135303631352c2c2c412a35372c4c2c2c20696d65693a3031333934393030323032363637352c30342c333532322e392c463a332e3732562c302c3134322c32313734342c3230382c30312c303730322c394338430a0d")));
diff --git a/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java b/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java
new file mode 100644
index 000000000..dcac78f80
--- /dev/null
+++ b/src/test/java/org/traccar/speedlimit/OverpassSpeedLimitProviderTest.java
@@ -0,0 +1,35 @@
+package org.traccar.speedlimit;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class OverpassSpeedLimitProviderTest {
+
+ @Ignore
+ @Test
+ public void test() throws Exception {
+ testLocationProvider();
+ }
+
+ public void testLocationProvider() throws Exception {
+ SpeedLimitProvider provider = new OverpassSpeedLimitProvider("http://8.8.8.8/api/interpreter");
+
+ provider.getSpeedLimit(34.74767, -82.48098, new SpeedLimitProvider.SpeedLimitProviderCallback() {
+ @Override
+ public void onSuccess(double speedLimit) {
+ assertEquals(52.1, speedLimit, 0.1);
+ }
+
+ @Override
+ public void onFailure(Throwable e) {
+ fail();
+ }
+ });
+
+ Thread.sleep(Long.MAX_VALUE);
+ }
+
+}
diff --git a/src/test/java/org/traccar/web/WebServerTest.java b/src/test/java/org/traccar/web/WebServerTest.java
index 5a79fbac2..ba4124e44 100644
--- a/src/test/java/org/traccar/web/WebServerTest.java
+++ b/src/test/java/org/traccar/web/WebServerTest.java
@@ -6,8 +6,6 @@ import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class WebServerTest {
@@ -15,12 +13,7 @@ public class WebServerTest {
@Test
public void contextTest() throws NamingException {
DataSource mockDataSource = (DataSource) Proxy.newProxyInstance(getClass().getClassLoader(),
- new Class[] { DataSource.class }, new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- return null;
- }
- });
+ new Class[]{DataSource.class}, (proxy, method, args) -> null);
Context context = new InitialContext();
context.bind("java:/DefaultDS", mockDataSource);
diff --git a/swagger.json b/swagger.json
index 334ed486a..e3df09515 100644
--- a/swagger.json
+++ b/swagger.json
@@ -1,2281 +1,3485 @@
{
- "swagger": "2.0",
- "info": {
- "version": "4.10",
- "title": "traccar"
+ "openapi": "3.0.1",
+ "info": {
+ "title": "Traccar",
+ "version": "4.10",
+ "description": "Traccar GPS tracking server API documentation. To use the API you need to have a server instance. For testing purposes you can use one of free [demo servers](https://www.traccar.org/demo-server/). For production use you can install your own server or get a [subscription service](https://www.traccar.org/product/tracking-server/).",
+ "contact": {
+ "name": "Traccar Support",
+ "url": "https://www.traccar.org/",
+ "email": "support@traccar.org"
},
- "host": "demo.traccar.org",
- "basePath": "/api",
- "schemes": [
- "http"
- ],
- "security": [
- {
- "basicAuth": []
- }
- ],
- "consumes": [
- "application/json"
- ],
- "produces": [
- "application/json"
- ],
- "paths": {
- "/commands": {
- "get": {
- "summary": "Fetch a list of Saved Commands",
- "description": "Without params, it returns a list of Drivers the user has access to",
- "parameters": [
- {
- "$ref": "#/parameters/all"
- },
- {
- "$ref": "#/parameters/userId"
- },
- {
- "$ref": "#/parameters/deviceId"
- },
- {
- "$ref": "#/parameters/groupId"
- },
- {
- "$ref": "#/parameters/refresh"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Command"
- }
- }
- }
- }
- },
- "post": {
- "summary": "Create a Saved Command",
- "parameters": [
- {
- "$ref": "#/parameters/Command"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Command"
- }
- }
- }
- }
- },
- "/commands/{id}": {
- "put": {
- "summary": "Update a Saved Command",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- },
- {
- "$ref": "#/parameters/Command"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Command"
- }
- }
- }
- },
- "delete": {
- "summary": "Delete a Saved Command",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
- }
- },
- "/commands/send": {
- "get": {
- "summary": "Fetch a list of Saved Commands supported by Device at the moment",
- "description": "Return a list of saved commands linked to Device and its groups, filtered by current Device protocol support",
- "parameters": [
- {
- "$ref": "#/parameters/deviceId"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Command"
- }
- }
- },
- "400": {
- "description": "Could happen when the user doesn't have permission for the device"
- }
- }
- },
- "post": {
- "summary": "Dispatch commands to device",
- "description": "Dispatch a new command or Saved Command if _body.id_ set",
- "parameters": [
- {
- "name": "body",
- "in": "body",
- "required": true,
- "schema": {
- "$ref": "#/definitions/Command"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "Command sent",
- "schema": {
- "$ref": "#/definitions/Command"
- }
- },
- "202": {
- "description": "Command queued",
- "schema": {
- "$ref": "#/definitions/Command"
- }
- },
- "400": {
- "description": "Could happen when the user doesn't have permission or an incorrect command _type_ for the device"
- }
- }
- }
- },
- "/commands/types": {
- "get": {
- "summary": "Fetch a list of available Commands for the Device or all possible Commands if Device ommited",
- "parameters": [
- {
- "name": "deviceId",
- "in": "query",
- "description" : "Internal device identifier. Only works if device has already reported some locations",
- "required" : false,
- "type": "integer"
- },
- {
- "name": "protocol",
- "in": "query",
- "description" : "Protocol name. Can be used instead of device id",
- "required" : false,
- "type": "string"
- },
- {
- "name": "textChannel",
- "in": "query",
- "description" : "When `true` return SMS commands. If not specified or `false` return data commands",
- "required" : false,
- "type": "boolean"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/CommandType"
- }
- }
- },
- "400": {
- "description": "Could happen when trying to fetch from a device the user does not have permission"
- }
- }
- }
- },
- "/devices": {
- "get": {
- "summary": "Fetch a list of Devices",
- "description": "Without any params, returns a list of the user's devices",
- "parameters": [
- {
- "$ref": "#/parameters/all"
- },
- {
- "$ref": "#/parameters/userId"
- },
- {
- "name" : "id",
- "in" : "query",
- "description" : "To fetch one or more devices. Multiple params can be passed like `id=31&id=42`",
- "required" : false,
- "type" : "integer",
- "collectionFormat" : "multi"
- },
- {
- "name" : "uniqueId",
- "in" : "query",
- "description" : "To fetch one or more devices. Multiple params can be passed like `uniqueId=333331&uniqieId=44442`",
- "required" : false,
- "type" : "string",
- "collectionFormat" : "multi"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Device"
- }
- }
- },
- "400": {
- "description": "No permission"
- }
- }
- },
- "post": {
- "summary": "Create a Device",
- "parameters": [
- {
- "$ref": "#/parameters/Device"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Device"
- }
- }
- }
+ "license": {
+ "name": "Apache 2.0",
+ "url": "https://www.apache.org/licenses/LICENSE-2.0.html"
+ }
+ },
+ "servers": [
+ {
+ "url": "https://demo.traccar.org/api",
+ "description": "Demo Server 1"
+ },
+ {
+ "url": "https://demo2.traccar.org/api",
+ "description": "Demo Server 2"
+ },
+ {
+ "url": "https://demo3.traccar.org/api",
+ "description": "Demo Server 3"
+ },
+ {
+ "url": "https://server.traccar.org/api",
+ "description": "Subscription Server"
+ },
+ {
+ "url": "http://{host}:{port}/api",
+ "description": "Other Server",
+ "variables": {
+ "host": {
+ "default": "localhost"
+ },
+ "port": {
+ "enum": [
+ "8082",
+ "80"
+ ],
+ "default": "8082"
+ }
+ }
+ }
+ ],
+ "security": [
+ {
+ "basicAuth": []
+ }
+ ],
+ "tags": [
+ {
+ "name": "Server",
+ "description": "Server information"
+ },
+ {
+ "name": "Session",
+ "description": "User session management"
+ },
+ {
+ "name": "Devices",
+ "description": "Device management"
+ },
+ {
+ "name": "Groups",
+ "description": "Group management"
+ },
+ {
+ "name": "Users",
+ "description": "User management"
+ },
+ {
+ "name": "Permissions",
+ "description": "User permissions and other object linking"
+ },
+ {
+ "name": "Positions",
+ "description": "Retrieving raw location information"
+ },
+ {
+ "name": "Events",
+ "description": "Retrieving event information"
+ },
+ {
+ "name": "Reports",
+ "description": "Reports generation"
+ },
+ {
+ "name": "Notifications",
+ "description": "User notifications management"
+ },
+ {
+ "name": "Geofences",
+ "description": "Geofence management"
+ },
+ {
+ "name": "Commands",
+ "description": "Sending commands to devices and stored command management"
+ },
+ {
+ "name": "Attributes",
+ "description": "Computed attributes management"
+ },
+ {
+ "name": "Drivers",
+ "description": "Drivers management"
+ },
+ {
+ "name": "Maintenance",
+ "description": "Maintenance management"
+ },
+ {
+ "name": "Calendars",
+ "description": "Calendar management"
+ },
+ {
+ "name": "Statistics",
+ "description": "Retrieving server statistics"
+ }
+ ],
+ "paths": {
+ "/commands": {
+ "get": {
+ "summary": "Fetch a list of Saved Commands",
+ "tags": [
+ "Commands"
+ ],
+ "description": "Without params, it returns a list of Drivers the user has access to",
+ "parameters": [
+ {
+ "name": "all",
+ "in": "query",
+ "description": "Can only be used by admins or managers to fetch all entities",
+ "schema": {
+ "type": "boolean"
}
- },
- "/devices/{id}": {
- "put": {
- "summary": "Update a Device",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- },
- {
- "$ref": "#/parameters/Device"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Device"
- }
- }
- }
- },
- "delete": {
- "summary": "Delete a Device",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Standard users can use this only with their own _userId_",
+ "schema": {
+ "type": "integer"
}
- },
- "/devices/{id}/accumulators": {
- "put": {
- "summary": "Update total distance and hours of the Device",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- },
- {
- "name": "body",
- "in": "body",
- "required": true,
- "schema": {
- "$ref": "#/definitions/DeviceAccumulators"
- }
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ },
+ {
+ "name": "deviceId",
+ "in": "query",
+ "description": "Standard users can use this only with _deviceId_s, they have access to",
+ "schema": {
+ "type": "integer"
}
- },
- "/groups": {
- "get": {
- "summary": "Fetch a list of Groups",
- "description": "Without any params, returns a list of the Groups the user belongs to",
- "parameters": [
- {
- "$ref": "#/parameters/all"
- },
- {
- "$ref": "#/parameters/userId"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Group"
- }
- }
- }
- }
- },
- "post": {
- "summary": "Create a Group",
- "parameters": [
- {
- "$ref": "#/parameters/Group"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Group"
- }
- },
- "400": {
- "description": "No permission"
- }
- }
+ },
+ {
+ "name": "groupId",
+ "in": "query",
+ "description": "Standard users can use this only with _groupId_s, they have access to",
+ "schema": {
+ "type": "integer"
}
- },
- "/groups/{id}": {
- "put": {
- "summary": "Update a Group",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- },
- {
- "$ref": "#/parameters/Group"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Group"
- }
- }
- }
- },
- "delete": {
- "summary": "Delete a Group",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ },
+ {
+ "name": "refresh",
+ "in": "query",
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Command"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a Saved Command",
+ "tags": [
+ "Commands"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Command"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Command"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/commands/{id}": {
+ "put": {
+ "summary": "Update a Saved Command",
+ "tags": [
+ "Commands"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Command"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Command"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ },
+ "delete": {
+ "summary": "Delete a Saved Command",
+ "tags": [
+ "Commands"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/commands/send": {
+ "get": {
+ "summary": "Fetch a list of Saved Commands supported by Device at the moment",
+ "description": "Return a list of saved commands linked to Device and its groups, filtered by current Device protocol support",
+ "tags": [
+ "Commands"
+ ],
+ "parameters": [
+ {
+ "name": "deviceId",
+ "in": "query",
+ "description": "Standard users can use this only with _deviceId_s, they have access to",
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Command"
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Could happen when the user doesn't have permission for the device",
+ "content": {}
+ }
+ }
+ },
+ "post": {
+ "summary": "Dispatch commands to device",
+ "description": "Dispatch a new command or Saved Command if _body.id_ set",
+ "tags": [
+ "Commands"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Command"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "Command sent",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Command"
+ }
+ }
+ }
+ },
+ "202": {
+ "description": "Command queued",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Command"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Could happen when the user doesn't have permission or an incorrect command _type_ for the device",
+ "content": {}
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/commands/types": {
+ "get": {
+ "summary": "Fetch a list of available Commands for the Device or all possible Commands if Device ommited",
+ "tags": [
+ "Commands"
+ ],
+ "parameters": [
+ {
+ "name": "deviceId",
+ "in": "query",
+ "description": "Internal device identifier. Only works if device has already reported some locations",
+ "schema": {
+ "type": "integer"
}
- },
- "/permissions": {
- "post": {
- "summary": "Link an Object to another Object",
- "parameters": [
- {
- "$ref": "#/parameters/Permission"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Permission"
- }
- },
- "400": {
- "description": "No permission"
- }
- }
- },
- "delete": {
- "summary": "Unlink an Object from another Object",
- "parameters": [
- {
- "$ref": "#/parameters/Permission"
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ },
+ {
+ "name": "protocol",
+ "in": "query",
+ "description": "Protocol name. Can be used instead of device id",
+ "schema": {
+ "type": "string"
}
- },
- "/positions": {
- "get": {
- "summary" : "Fetches a list of Positions",
- "description" : "Without any params, it returns a list of last known positions for all the user's Devices. _from_ and _to_ fields are not required with _id_",
- "consumes": [
- "application/json",
- "text/csv",
- "application/gpx+xml"
- ],
- "produces": [
- "application/json",
- "text/csv",
- "application/gpx+xml"
- ],
- "parameters": [
- {
- "name": "deviceId",
- "in": "query",
- "description": "_deviceId_ is optional, but requires the _from_ and _to_ parameters when used",
- "required": false,
- "type": "integer"
- },
- {
- "name": "from",
- "in": "query",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
- "required": false,
- "type": "string",
- "format": "date-time"
- },
- {
- "name": "to",
- "in": "query",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
- "required": false,
- "type": "string",
- "format": "date-time"
- },
- {
- "name" : "id",
- "in" : "query",
- "description" : "To fetch one or more positions. Multiple params can be passed like `id=31&id=42`",
- "required" : false,
- "type" : "integer",
- "collectionFormat" : "multi"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Position"
- }
- }
- }
- }
+ },
+ {
+ "name": "textChannel",
+ "in": "query",
+ "description": "When `true` return SMS commands. If not specified or `false` return data commands",
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/CommandType"
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "Could happen when trying to fetch from a device the user does not have permission",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/devices": {
+ "get": {
+ "summary": "Fetch a list of Devices",
+ "description": "Without any params, returns a list of the user's devices",
+ "tags": [
+ "Devices"
+ ],
+ "parameters": [
+ {
+ "name": "all",
+ "in": "query",
+ "description": "Can only be used by admins or managers to fetch all entities",
+ "schema": {
+ "type": "boolean"
}
- },
- "/server": {
- "get": {
- "summary": "Fetch Server information",
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Server"
- }
- }
- }
- },
- "put": {
- "summary": "Update Server information",
- "parameters": [
- {
- "name": "body",
- "in": "body",
- "required": true,
- "schema": {
- "$ref": "#/definitions/Server"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Server"
- }
- }
- }
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Standard users can use this only with their own _userId_",
+ "schema": {
+ "type": "integer"
}
- },
- "/session": {
- "get": {
- "summary": "Fetch Session information",
- "consumes": [
- "application/x-www-form-urlencoded"
- ],
- "parameters": [
- {
- "name": "token",
- "in": "query",
- "required": false,
- "type": "string"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/User"
- }
- },
- "404": {
- "description": "Not Found"
- }
- }
- },
- "post": {
- "summary": "Create a new Session",
- "consumes": [
- "application/x-www-form-urlencoded"
- ],
- "parameters": [
- {
- "name": "email",
- "in": "formData",
- "required": true,
- "type": "string"
- },
- {
- "name": "password",
- "in": "formData",
- "required": true,
- "type": "string",
- "format": "password"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/User"
- }
- },
- "401": {
- "description": "Unauthorized"
- }
- }
- },
- "delete": {
- "summary": "Close the Session",
- "consumes": [
- "application/x-www-form-urlencoded"
- ],
- "parameters": [],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ },
+ {
+ "name": "id",
+ "in": "query",
+ "description": "To fetch one or more devices. Multiple params can be passed like `id=31&id=42`",
+ "schema": {
+ "type": "integer"
}
- },
- "/users": {
- "get": {
- "summary": "Fetch a list of Users",
- "parameters": [
- {
- "name": "userId",
- "in": "query",
- "description": "Can only be used by admin or manager users",
- "required": false,
- "type": "string"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/User"
- }
- }
- },
- "400": {
- "description": "No Permission"
- }
- }
- },
- "post": {
- "summary": "Create a User",
- "parameters": [
- {
- "$ref": "#/parameters/User"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/User"
- }
- }
- }
+ },
+ {
+ "name": "uniqueId",
+ "in": "query",
+ "description": "To fetch one or more devices. Multiple params can be passed like `uniqueId=333331&uniqieId=44442`",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Device"
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "No permission",
+ "content": {}
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a Device",
+ "tags": [
+ "Devices"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Device"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Device"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/devices/{id}": {
+ "put": {
+ "summary": "Update a Device",
+ "tags": [
+ "Devices"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Device"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Device"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ },
+ "delete": {
+ "summary": "Delete a Device",
+ "tags": [
+ "Devices"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/devices/{id}/accumulators": {
+ "put": {
+ "summary": "Update total distance and hours of the Device",
+ "tags": [
+ "Devices"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/DeviceAccumulators"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/groups": {
+ "get": {
+ "summary": "Fetch a list of Groups",
+ "description": "Without any params, returns a list of the Groups the user belongs to",
+ "tags": [
+ "Groups"
+ ],
+ "parameters": [
+ {
+ "name": "all",
+ "in": "query",
+ "description": "Can only be used by admins or managers to fetch all entities",
+ "schema": {
+ "type": "boolean"
}
- },
- "/users/{id}": {
- "put": {
- "summary": "Update a User",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- },
- {
- "$ref": "#/parameters/User"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/User"
- }
- }
- }
- },
- "delete": {
- "summary": "Delete a User",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Standard users can use this only with their own _userId_",
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Group"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a Group",
+ "tags": [
+ "Groups"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Group"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Group"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "No permission",
+ "content": {}
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/groups/{id}": {
+ "put": {
+ "summary": "Update a Group",
+ "tags": [
+ "Groups"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Group"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Group"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ },
+ "delete": {
+ "summary": "Delete a Group",
+ "tags": [
+ "Groups"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/permissions": {
+ "post": {
+ "summary": "Link an Object to another Object",
+ "tags": [
+ "Permissions"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Permission"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Permission"
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "No permission",
+ "content": {}
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ },
+ "delete": {
+ "summary": "Unlink an Object from another Object",
+ "tags": [
+ "Permissions"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Permission"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/positions": {
+ "get": {
+ "summary": "Fetches a list of Positions",
+ "description": "We strongly recommend using [Traccar WebSocket API](https://www.traccar.org/traccar-api/) instead of periodically polling positions endpoint. Without any params, it returns a list of last known positions for all the user's Devices. _from_ and _to_ fields are not required with _id_.",
+ "tags": [
+ "Positions"
+ ],
+ "parameters": [
+ {
+ "name": "deviceId",
+ "in": "query",
+ "description": "_deviceId_ is optional, but requires the _from_ and _to_ parameters when used",
+ "schema": {
+ "type": "integer"
}
- },
- "/notifications": {
- "get": {
- "summary": "Fetch a list of Notifications",
- "description": "Without params, it returns a list of Notifications the user has access to",
- "parameters": [
- {
- "$ref": "#/parameters/all"
- },
- {
- "$ref": "#/parameters/userId"
- },
- {
- "$ref": "#/parameters/deviceId"
- },
- {
- "$ref": "#/parameters/groupId"
- },
- {
- "$ref": "#/parameters/refresh"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Notification"
- }
- }
- }
- }
- },
- "post": {
- "summary": "Create a Notification",
- "parameters": [
- {
- "$ref": "#/parameters/Notification"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Notification"
- }
- }
- }
+ },
+ {
+ "name": "from",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "schema": {
+ "type": "string",
+ "format": "date-time"
}
- },
- "/notifications/{id}": {
- "put": {
- "summary": "Update a Notification",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- },
- {
- "$ref": "#/parameters/Notification"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Notification"
- }
- }
- }
- },
- "delete": {
- "summary": "Delete a Notification",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ },
+ {
+ "name": "to",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "schema": {
+ "type": "string",
+ "format": "date-time"
}
- },
- "/notifications/types": {
- "get": {
- "summary": "Fetch a list of available Notification types",
- "parameters": [],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/NotificationType"
- }
- }
- }
+ },
+ {
+ "name": "id",
+ "in": "query",
+ "description": "To fetch one or more positions. Multiple params can be passed like `id=31&id=42`",
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Position"
+ }
+ }
+ },
+ "text/csv": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Position"
+ }
+ }
+ },
+ "application/gpx+xml": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Position"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/server": {
+ "get": {
+ "summary": "Fetch Server information",
+ "tags": [
+ "Server"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Server"
+ }
+ }
+ }
+ }
+ }
+ },
+ "put": {
+ "summary": "Update Server information",
+ "tags": [
+ "Server"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Server"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Server"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/session": {
+ "get": {
+ "summary": "Fetch Session information",
+ "tags": [
+ "Session"
+ ],
+ "parameters": [
+ {
+ "name": "token",
+ "in": "query",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ },
+ "404": {
+ "description": "Not Found",
+ "content": {}
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a new Session",
+ "tags": [
+ "Session"
+ ],
+ "requestBody": {
+ "content": {
+ "application/x-www-form-urlencoded": {
+ "schema": {
+ "required": [
+ "email",
+ "password"
+ ],
+ "properties": {
+ "email": {
+ "type": "string"
+ },
+ "password": {
+ "type": "string",
+ "format": "password"
+ }
}
+ }
}
+ },
+ "required": true
},
- "/notifications/test": {
- "post": {
- "summary": "Send test notification to current user via Email and SMS",
- "parameters": [],
- "responses": {
- "204": {
- "description": "Successful sending"
- },
- "400": {
- "description": "Could happen if sending has failed"
- }
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
}
+ }
}
- },
- "/geofences": {
- "get": {
- "summary": "Fetch a list of Geofences",
- "description": "Without params, it returns a list of Geofences the user has access to",
- "parameters": [
- {
- "$ref": "#/parameters/all"
- },
- {
- "$ref": "#/parameters/userId"
- },
- {
- "$ref": "#/parameters/deviceId"
- },
- {
- "$ref": "#/parameters/groupId"
- },
- {
- "$ref": "#/parameters/refresh"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Geofence"
- }
- }
- }
- }
- },
- "post": {
- "summary": "Create a Geofence",
- "parameters": [
- {
- "$ref": "#/parameters/Geofence"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Geofence"
- }
- }
- }
+ },
+ "401": {
+ "description": "Unauthorized",
+ "content": {}
+ }
+ }
+ },
+ "delete": {
+ "summary": "Close the Session",
+ "tags": [
+ "Session"
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/users": {
+ "get": {
+ "summary": "Fetch a list of Users",
+ "tags": [
+ "Users"
+ ],
+ "parameters": [
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Can only be used by admin or manager users",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ }
+ },
+ "400": {
+ "description": "No Permission",
+ "content": {}
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a User",
+ "tags": [
+ "Users"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/users/{id}": {
+ "put": {
+ "summary": "Update a User",
+ "tags": [
+ "Users"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ },
+ "delete": {
+ "summary": "Delete a User",
+ "tags": [
+ "Users"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/notifications": {
+ "get": {
+ "summary": "Fetch a list of Notifications",
+ "description": "Without params, it returns a list of Notifications the user has access to",
+ "tags": [
+ "Notifications"
+ ],
+ "parameters": [
+ {
+ "name": "all",
+ "in": "query",
+ "description": "Can only be used by admins or managers to fetch all entities",
+ "schema": {
+ "type": "boolean"
}
- },
- "/geofences/{id}": {
- "put": {
- "summary": "Update a Geofence",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- },
- {
- "$ref": "#/parameters/Geofence"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Geofence"
- }
- }
- }
- },
- "delete": {
- "summary": "Delete a Geofence",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Standard users can use this only with their own _userId_",
+ "schema": {
+ "type": "integer"
}
- },
- "/events/{id}": {
- "get": {
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Event"
- }
- }
- }
+ },
+ {
+ "name": "deviceId",
+ "in": "query",
+ "description": "Standard users can use this only with _deviceId_s, they have access to",
+ "schema": {
+ "type": "integer"
}
- },
- "/reports/route": {
- "get": {
- "summary": "Fetch a list of Positions within the time period for the Devices or Groups",
- "description": "At least one _deviceId_ or one _groupId_ must be passed",
- "consumes": [
- "application/json",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
- ],
- "produces": [
- "application/json",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
- ],
- "parameters": [
- {
- "$ref": "#/parameters/deviceIdArray"
- },
- {
- "$ref": "#/parameters/groupIdArray"
- },
- {
- "$ref": "#/parameters/fromTime"
- },
- {
- "$ref": "#/parameters/toTime"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Position"
- }
- }
- }
- }
+ },
+ {
+ "name": "groupId",
+ "in": "query",
+ "description": "Standard users can use this only with _groupId_s, they have access to",
+ "schema": {
+ "type": "integer"
}
- },
- "/reports/events": {
- "get": {
- "summary": "Fetch a list of Events within the time period for the Devices or Groups",
- "description": "At least one _deviceId_ or one _groupId_ must be passed",
- "consumes": [
- "application/json",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
- ],
- "produces": [
- "application/json",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
- ],
- "parameters": [
- {
- "$ref": "#/parameters/deviceIdArray"
- },
- {
- "$ref": "#/parameters/groupIdArray"
- },
- {
- "name": "type",
- "in": "query",
- "description": "% can be used to return events of all types",
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- {
- "$ref": "#/parameters/fromTime"
- },
- {
- "$ref": "#/parameters/toTime"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Event"
- }
- }
- }
- }
+ },
+ {
+ "name": "refresh",
+ "in": "query",
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Notification"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a Notification",
+ "tags": [
+ "Notifications"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Notification"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Notification"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/notifications/{id}": {
+ "put": {
+ "summary": "Update a Notification",
+ "tags": [
+ "Notifications"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Notification"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Notification"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ },
+ "delete": {
+ "summary": "Delete a Notification",
+ "tags": [
+ "Notifications"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/notifications/types": {
+ "get": {
+ "summary": "Fetch a list of available Notification types",
+ "tags": [
+ "Notifications"
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/NotificationType"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/notifications/test": {
+ "post": {
+ "summary": "Send test notification to current user via Email and SMS",
+ "tags": [
+ "Notifications"
+ ],
+ "responses": {
+ "204": {
+ "description": "Successful sending",
+ "content": {}
+ },
+ "400": {
+ "description": "Could happen if sending has failed",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/geofences": {
+ "get": {
+ "summary": "Fetch a list of Geofences",
+ "description": "Without params, it returns a list of Geofences the user has access to",
+ "tags": [
+ "Geofences"
+ ],
+ "parameters": [
+ {
+ "name": "all",
+ "in": "query",
+ "description": "Can only be used by admins or managers to fetch all entities",
+ "schema": {
+ "type": "boolean"
}
- },
- "/reports/summary": {
- "get": {
- "summary": "Fetch a list of ReportSummary within the time period for the Devices or Groups",
- "description": "At least one _deviceId_ or one _groupId_ must be passed",
- "consumes": [
- "application/json",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
- ],
- "produces": [
- "application/json",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
- ],
- "parameters": [
- {
- "$ref": "#/parameters/deviceIdArray"
- },
- {
- "$ref": "#/parameters/groupIdArray"
- },
- {
- "$ref": "#/parameters/fromTime"
- },
- {
- "$ref": "#/parameters/toTime"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/ReportSummary"
- }
- }
- }
- }
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Standard users can use this only with their own _userId_",
+ "schema": {
+ "type": "integer"
}
- },
- "/reports/trips": {
- "get": {
- "summary": "Fetch a list of ReportTrips within the time period for the Devices or Groups",
- "description": "At least one _deviceId_ or one _groupId_ must be passed",
- "consumes": [
- "application/json",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
- ],
- "produces": [
- "application/json",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
- ],
- "parameters": [
- {
- "$ref": "#/parameters/deviceIdArray"
- },
- {
- "$ref": "#/parameters/groupIdArray"
- },
- {
- "$ref": "#/parameters/fromTime"
- },
- {
- "$ref": "#/parameters/toTime"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/ReportTrips"
- }
- }
- }
- }
+ },
+ {
+ "name": "deviceId",
+ "in": "query",
+ "description": "Standard users can use this only with _deviceId_s, they have access to",
+ "schema": {
+ "type": "integer"
}
- },
- "/reports/stops": {
- "get": {
- "summary": "Fetch a list of ReportStops within the time period for the Devices or Groups",
- "description": "At least one _deviceId_ or one _groupId_ must be passed",
- "consumes": [
- "application/json",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
- ],
- "produces": [
- "application/json",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
- ],
- "parameters": [
- {
- "$ref": "#/parameters/deviceIdArray"
- },
- {
- "$ref": "#/parameters/groupIdArray"
- },
- {
- "$ref": "#/parameters/fromTime"
- },
- {
- "$ref": "#/parameters/toTime"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/ReportStops"
- }
- }
- }
- }
+ },
+ {
+ "name": "groupId",
+ "in": "query",
+ "description": "Standard users can use this only with _groupId_s, they have access to",
+ "schema": {
+ "type": "integer"
}
- },
- "/statistics": {
- "get": {
- "summary": "Fetch server Statistics",
- "parameters": [
- {
- "$ref": "#/parameters/fromTime"
- },
- {
- "$ref": "#/parameters/toTime"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Statistics"
- }
- }
- }
- }
+ },
+ {
+ "name": "refresh",
+ "in": "query",
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Geofence"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a Geofence",
+ "tags": [
+ "Geofences"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Geofence"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Geofence"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/geofences/{id}": {
+ "put": {
+ "summary": "Update a Geofence",
+ "tags": [
+ "Geofences"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Geofence"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Geofence"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ },
+ "delete": {
+ "summary": "Delete a Geofence",
+ "tags": [
+ "Geofences"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/events/{id}": {
+ "get": {
+ "tags": [
+ "Events"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
}
- },
- "/calendars": {
- "get": {
- "summary": "Fetch a list of Calendars",
- "description": "Without params, it returns a list of Calendars the user has access to",
- "parameters": [
- {
- "$ref": "#/parameters/all"
- },
- {
- "$ref": "#/parameters/userId"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Calendar"
- }
- }
- }
- }
- },
- "post": {
- "summary": "Create a Calendar",
- "parameters": [
- {
- "$ref": "#/parameters/Calendar"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Calendar"
- }
- }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Event"
}
+ }
}
- },
- "/calendars/{id}": {
- "put": {
- "summary": "Update a Calendar",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- },
- {
- "$ref": "#/parameters/Calendar"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Calendar"
- }
- }
- }
- },
- "delete": {
- "summary": "Delete a Calendar",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ }
+ }
+ }
+ },
+ "/reports/route": {
+ "get": {
+ "summary": "Fetch a list of Positions within the time period for the Devices or Groups",
+ "description": "At least one _deviceId_ or one _groupId_ must be passed",
+ "tags": [
+ "Reports"
+ ],
+ "parameters": [
+ {
+ "name": "deviceId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
}
- },
- "/attributes/computed": {
- "get": {
- "summary": "Fetch a list of Attributes",
- "description": "Without params, it returns a list of Attributes the user has access to",
- "parameters": [
- {
- "$ref": "#/parameters/all"
- },
- {
- "$ref": "#/parameters/userId"
- },
- {
- "$ref": "#/parameters/deviceId"
- },
- {
- "$ref": "#/parameters/groupId"
- },
- {
- "$ref": "#/parameters/refresh"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Attribute"
- }
- }
- }
- }
- },
- "post": {
- "summary": "Create an Attribute",
- "parameters": [
- {
- "$ref": "#/parameters/Attribute"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Attribute"
- }
- }
- }
+ },
+ {
+ "name": "groupId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
}
- },
- "/attributes/computed/{id}": {
- "put": {
- "summary": "Update an Attribute",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- },
- {
- "$ref": "#/parameters/Attribute"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Attribute"
- }
- }
- }
- },
- "delete": {
- "summary": "Delete an Attribute",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ },
+ {
+ "name": "from",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
}
- },
- "/drivers": {
- "get": {
- "summary": "Fetch a list of Drivers",
- "description": "Without params, it returns a list of Drivers the user has access to",
- "parameters": [
- {
- "$ref": "#/parameters/all"
- },
- {
- "$ref": "#/parameters/userId"
- },
- {
- "$ref": "#/parameters/deviceId"
- },
- {
- "$ref": "#/parameters/groupId"
- },
- {
- "$ref": "#/parameters/refresh"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Driver"
- }
- }
- }
- }
- },
- "post": {
- "summary": "Create a Driver",
- "parameters": [
- {
- "$ref": "#/parameters/Driver"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Driver"
- }
- }
- }
+ },
+ {
+ "name": "to",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Position"
+ }
+ }
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Position"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/reports/events": {
+ "get": {
+ "summary": "Fetch a list of Events within the time period for the Devices or Groups",
+ "description": "At least one _deviceId_ or one _groupId_ must be passed",
+ "tags": [
+ "Reports"
+ ],
+ "parameters": [
+ {
+ "name": "deviceId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
}
- },
- "/drivers/{id}": {
- "put": {
- "summary": "Update a Driver",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- },
- {
- "$ref": "#/parameters/Driver"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Driver"
- }
- }
- }
- },
- "delete": {
- "summary": "Delete a Driver",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ },
+ {
+ "name": "groupId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
}
- },
- "/maintenance": {
- "get": {
- "summary": "Fetch a list of Maintenance",
- "description": "Without params, it returns a list of Maintenance the user has access to",
- "parameters": [
- {
- "$ref": "#/parameters/all"
- },
- {
- "$ref": "#/parameters/userId"
- },
- {
- "$ref": "#/parameters/deviceId"
- },
- {
- "$ref": "#/parameters/groupId"
- },
- {
- "$ref": "#/parameters/refresh"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Maintenance"
- }
- }
- }
- }
- },
- "post": {
- "summary": "Create a Maintenance",
- "parameters": [
- {
- "$ref": "#/parameters/Maintenance"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Maintenance"
- }
- }
- }
+ },
+ {
+ "name": "type",
+ "in": "query",
+ "description": "% can be used to return events of all types",
+ "style": "form",
+ "explode": false,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
}
- },
- "/maintenance/{id}": {
- "put": {
- "summary": "Update a Maintenance",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- },
- {
- "$ref": "#/parameters/Maintenance"
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "schema": {
- "$ref": "#/definitions/Maintenance"
- }
- }
- }
- },
- "delete": {
- "summary": "Delete a Maintenance",
- "parameters": [
- {
- "$ref": "#/parameters/entityId"
- }
- ],
- "responses": {
- "204": {
- "description": "No Content"
- }
- }
+ },
+ {
+ "name": "from",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
}
+ },
+ {
+ "name": "to",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Event"
+ }
+ }
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Event"
+ }
+ }
+ }
+ }
+ }
}
+ }
},
- "definitions": {
- "Position": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "deviceId": {
- "type": "integer"
- },
- "protocol": {
- "type": "string"
- },
- "deviceTime": {
- "type": "string",
- "format": "date-time",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`"
- },
- "fixTime": {
- "type": "string",
- "format": "date-time",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`"
- },
- "serverTime": {
- "type": "string",
- "format": "date-time",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`"
- },
- "outdated": {
- "type": "boolean"
- },
- "valid": {
- "type": "boolean"
- },
- "latitude": {
- "type": "number"
- },
- "longitude": {
- "type": "number"
- },
- "altitude": {
- "type": "number"
- },
- "speed": {
- "type": "number",
- "description": "in knots"
- },
- "course": {
- "type": "number"
- },
- "address": {
- "type": "string"
- },
- "accuracy": {
- "type": "number"
- },
- "network": {
- "type": "object",
- "additionalProperties": true
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
- }
- },
- "User": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "name": {
- "type": "string"
- },
- "email": {
- "type": "string"
- },
- "phone": {
- "type": "string"
- },
- "readonly": {
- "type": "boolean"
- },
- "administrator": {
- "type": "boolean"
- },
- "map": {
- "type": "string"
- },
- "latitude": {
- "type": "number"
- },
- "longitude": {
- "type": "number"
- },
- "zoom": {
- "type": "integer"
- },
- "password": {
- "type": "string"
- },
- "twelveHourFormat": {
- "type": "boolean"
- },
- "coordinateFormat": {
- "type": "string"
- },
- "disabled": {
- "type": "boolean"
- },
- "expirationTime": {
- "type": "string",
- "format": "date-time",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`"
- },
- "deviceLimit": {
- "type": "integer"
- },
- "userLimit": {
- "type": "integer"
- },
- "deviceReadonly": {
- "type": "boolean"
- },
- "limitCommands": {
- "type": "boolean"
- },
- "poiLayer": {
- "type": "string"
- },
- "token": {
- "type": "string"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
- }
- },
- "Server": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "registration": {
- "type": "boolean"
- },
- "readonly": {
- "type": "boolean"
- },
- "deviceReadonly": {
- "type": "boolean"
- },
- "limitCommands": {
- "type": "boolean"
- },
- "map": {
- "type": "string"
- },
- "bingKey": {
- "type": "string"
- },
- "mapUrl": {
- "type": "string"
- },
- "poiLayer": {
- "type": "string"
- },
- "latitude": {
- "type": "number"
- },
- "longitude": {
- "type": "number"
- },
- "zoom": {
- "type": "integer"
- },
- "twelveHourFormat": {
- "type": "boolean"
- },
- "version": {
- "type": "string"
- },
- "forceSettings": {
- "type": "boolean"
- },
- "coordinateFormat": {
- "type": "string"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
+ "/reports/summary": {
+ "get": {
+ "summary": "Fetch a list of ReportSummary within the time period for the Devices or Groups",
+ "description": "At least one _deviceId_ or one _groupId_ must be passed",
+ "tags": [
+ "Reports"
+ ],
+ "parameters": [
+ {
+ "name": "deviceId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
}
- },
- "Command": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "deviceId": {
- "type": "integer"
- },
- "description": {
- "type": "string"
- },
- "type": {
- "type": "string"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
+ },
+ {
+ "name": "groupId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
}
- },
- "Device": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "name": {
- "type": "string"
- },
- "uniqueId": {
- "type": "string"
- },
- "status": {
- "type": "string"
- },
- "disabled": {
- "type": "boolean"
- },
- "lastUpdate": {
- "type": "string",
- "format": "date-time",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`"
- },
- "positionId": {
- "type": "integer"
- },
- "groupId": {
- "type": "integer"
- },
- "phone": {
- "type": "string"
- },
- "model": {
- "type": "string"
- },
- "contact": {
- "type": "string"
- },
- "category": {
- "type": "string"
- },
- "geofenceIds": {
- "type": "array",
- "items": {
- "type": "integer"
- }
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
+ },
+ {
+ "name": "from",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
}
- },
- "Group": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "name": {
- "type": "string"
- },
- "groupId": {
- "type": "integer"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
+ },
+ {
+ "name": "to",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ReportSummary"
+ }
+ }
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ReportSummary"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/reports/trips": {
+ "get": {
+ "summary": "Fetch a list of ReportTrips within the time period for the Devices or Groups",
+ "description": "At least one _deviceId_ or one _groupId_ must be passed",
+ "tags": [
+ "Reports"
+ ],
+ "parameters": [
+ {
+ "name": "deviceId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
}
- },
- "Permission": {
- "description": "This is a permission map that contain two object indexes. It is used to link/unlink objects. Order is important. Example: { deviceId:8, geofenceId: 16 }",
- "properties": {
- "userId": {
- "description": "User Id, can be only first parameter",
- "type": "integer"
- },
- "deviceId": {
- "description": "Device Id, can be first parameter or second only in combination with userId",
- "type": "integer"
- },
- "groupId": {
- "description": "Group Id, can be first parameter or second only in combination with userId",
- "type": "integer"
- },
- "geofenceId": {
- "description": "Geofence Id, can be second parameter only",
- "type": "integer"
- },
- "calendarId": {
- "description": "Calendar Id, can be second parameter only and only in combination with userId",
- "type": "integer"
- },
- "attributeId": {
- "description": "Computed Attribute Id, can be second parameter only",
- "type": "integer"
- },
- "driverId": {
- "description": "Driver Id, can be second parameter only",
- "type": "integer"
- },
- "managedUserId": {
- "description": "User Id, can be second parameter only and only in combination with userId",
- "type": "integer"
- }
+ },
+ {
+ "name": "groupId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
}
- },
- "CommandType": {
- "properties": {
- "type": {
- "type": "string"
- }
+ },
+ {
+ "name": "from",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
}
- },
- "Geofence": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "name": {
- "type": "string"
- },
- "description": {
- "type": "string"
- },
- "area": {
- "type": "string"
- },
- "calendarId": {
- "type": "integer"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
+ },
+ {
+ "name": "to",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ReportTrips"
+ }
+ }
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ReportTrips"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/reports/stops": {
+ "get": {
+ "summary": "Fetch a list of ReportStops within the time period for the Devices or Groups",
+ "description": "At least one _deviceId_ or one _groupId_ must be passed",
+ "tags": [
+ "Reports"
+ ],
+ "parameters": [
+ {
+ "name": "deviceId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
}
- },
- "Notification": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "type": {
- "type": "string"
- },
- "always": {
- "type": "boolean"
- },
- "web": {
- "type": "boolean"
- },
- "mail": {
- "type": "boolean"
- },
- "sms": {
- "type": "boolean"
- },
- "calendarId": {
- "type": "integer"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
+ },
+ {
+ "name": "groupId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
}
- },
- "NotificationType": {
- "properties": {
- "type": {
- "type": "string"
- }
+ },
+ {
+ "name": "from",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
}
- },
- "Event": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "type": {
- "type": "string"
- },
- "serverTime": {
- "type": "string",
- "format": "date-time",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`"
- },
- "deviceId": {
- "type": "integer"
- },
- "positionId": {
- "type": "integer"
- },
- "geofenceId": {
- "type": "integer"
- },
- "maintenanceId": {
- "type": "integer"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
+ },
+ {
+ "name": "to",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ReportStops"
+ }
+ }
+ },
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/ReportStops"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/statistics": {
+ "get": {
+ "summary": "Fetch server Statistics",
+ "tags": [
+ "Statistics"
+ ],
+ "parameters": [
+ {
+ "name": "from",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
}
- },
- "ReportSummary": {
- "properties": {
- "deviceId": {
- "type": "integer"
- },
- "deviceName": {
- "type": "string"
- },
- "maxSpeed": {
- "type": "number",
- "description": "in knots"
- },
- "averageSpeed": {
- "type": "number",
- "description": "in knots"
- },
- "distance": {
- "type": "number",
- "description": "in meters"
- },
- "spentFuel": {
- "type": "number",
- "description": "in liters"
- },
- "engineHours": {
- "type": "integer"
- }
+ },
+ {
+ "name": "to",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Statistics"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/calendars": {
+ "get": {
+ "summary": "Fetch a list of Calendars",
+ "description": "Without params, it returns a list of Calendars the user has access to",
+ "tags": [
+ "Calendars"
+ ],
+ "parameters": [
+ {
+ "name": "all",
+ "in": "query",
+ "description": "Can only be used by admins or managers to fetch all entities",
+ "schema": {
+ "type": "boolean"
}
- },
- "ReportTrips": {
- "properties": {
- "deviceId": {
- "type": "integer"
- },
- "deviceName": {
- "type": "string"
- },
- "maxSpeed": {
- "type": "number",
- "description": "in knots"
- },
- "averageSpeed": {
- "type": "number",
- "description": "in knots"
- },
- "distance": {
- "type": "number",
- "description": "in meters"
- },
- "spentFuel": {
- "type": "number",
- "description": "in liters"
- },
- "duration": {
- "type": "integer"
- },
- "startTime": {
- "type": "string",
- "format": "date-time",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`"
- },
- "startAddress": {
- "type": "string"
- },
- "startLat": {
- "type": "number"
- },
- "startLon": {
- "type": "number"
- },
- "endTime": {
- "type": "string",
- "format": "date-time",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`"
- },
- "endAddress": {
- "type": "string"
- },
- "endLat": {
- "type": "number"
- },
- "endLon": {
- "type": "number"
- },
- "driverUniqueId": {
- "type": "integer"
- },
- "driverName": {
- "type": "string"
- }
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Standard users can use this only with their own _userId_",
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Calendar"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a Calendar",
+ "tags": [
+ "Calendars"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Calendar"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Calendar"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/calendars/{id}": {
+ "put": {
+ "summary": "Update a Calendar",
+ "tags": [
+ "Calendars"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Calendar"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Calendar"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ },
+ "delete": {
+ "summary": "Delete a Calendar",
+ "tags": [
+ "Calendars"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/attributes/computed": {
+ "get": {
+ "summary": "Fetch a list of Attributes",
+ "description": "Without params, it returns a list of Attributes the user has access to",
+ "tags": [
+ "Attributes"
+ ],
+ "parameters": [
+ {
+ "name": "all",
+ "in": "query",
+ "description": "Can only be used by admins or managers to fetch all entities",
+ "schema": {
+ "type": "boolean"
}
- },
- "ReportStops": {
- "properties": {
- "deviceId": {
- "type": "integer"
- },
- "deviceName": {
- "type": "string"
- },
- "duration": {
- "type": "integer"
- },
- "startTime": {
- "type": "string",
- "format": "date-time",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`"
- },
- "address": {
- "type": "string"
- },
- "lat": {
- "type": "number"
- },
- "lon": {
- "type": "number"
- },
- "endTime": {
- "type": "string",
- "format": "date-time",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`"
- },
- "spentFuel": {
- "type": "number",
- "description": "in liters"
- },
- "engineHours": {
- "type": "integer"
- }
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Standard users can use this only with their own _userId_",
+ "schema": {
+ "type": "integer"
}
- },
- "Statistics": {
- "properties": {
- "captureTime": {
- "type": "string",
- "format": "date-time",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`"
- },
- "activeUsers": {
- "type": "integer"
- },
- "activeDevices": {
- "type": "integer"
- },
- "requests": {
- "type": "integer"
- },
- "messagesReceived": {
- "type": "integer"
- },
- "messagesStored": {
- "type": "integer"
- }
+ },
+ {
+ "name": "deviceId",
+ "in": "query",
+ "description": "Standard users can use this only with _deviceId_s, they have access to",
+ "schema": {
+ "type": "integer"
}
- },
- "DeviceAccumulators": {
- "properties": {
- "deviceId": {
- "type": "integer"
- },
- "totalDistance": {
- "type": "number",
- "description": "in meters"
- },
- "hours": {
- "type": "number"
- }
+ },
+ {
+ "name": "groupId",
+ "in": "query",
+ "description": "Standard users can use this only with _groupId_s, they have access to",
+ "schema": {
+ "type": "integer"
}
- },
- "Calendar": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "name": {
- "type": "string"
- },
- "data": {
- "type": "string",
- "description": "base64 encoded in iCalendar format"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
+ },
+ {
+ "name": "refresh",
+ "in": "query",
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Attribute"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create an Attribute",
+ "tags": [
+ "Attributes"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Attribute"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Attribute"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/attributes/computed/{id}": {
+ "put": {
+ "summary": "Update an Attribute",
+ "tags": [
+ "Attributes"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Attribute"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Attribute"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ },
+ "delete": {
+ "summary": "Delete an Attribute",
+ "tags": [
+ "Attributes"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/drivers": {
+ "get": {
+ "summary": "Fetch a list of Drivers",
+ "description": "Without params, it returns a list of Drivers the user has access to",
+ "tags": [
+ "Drivers"
+ ],
+ "parameters": [
+ {
+ "name": "all",
+ "in": "query",
+ "description": "Can only be used by admins or managers to fetch all entities",
+ "schema": {
+ "type": "boolean"
}
- },
- "Attribute": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "description": {
- "type": "string"
- },
- "attribute": {
- "type": "string"
- },
- "expression": {
- "type": "string"
- },
- "type": {
- "type": "string",
- "description": "String|Number|Boolean"
- }
+ },
+ {
+ "name": "userId",
+ "in": "query",
+ "description": "Standard users can use this only with their own _userId_",
+ "schema": {
+ "type": "integer"
}
- },
- "Driver": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "name": {
- "type": "string"
- },
- "uniqueId": {
- "type": "string"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
+ },
+ {
+ "name": "deviceId",
+ "in": "query",
+ "description": "Standard users can use this only with _deviceId_s, they have access to",
+ "schema": {
+ "type": "integer"
}
- },
- "Maintenance": {
- "properties": {
- "id": {
- "type": "integer"
- },
- "name": {
- "type": "string"
- },
- "type": {
- "type": "string"
- },
- "start": {
- "type": "number"
- },
- "period": {
- "type": "number"
- },
- "attributes": {
- "type": "object",
- "additionalProperties": true
- }
+ },
+ {
+ "name": "groupId",
+ "in": "query",
+ "description": "Standard users can use this only with _groupId_s, they have access to",
+ "schema": {
+ "type": "integer"
}
+ },
+ {
+ "name": "refresh",
+ "in": "query",
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Driver"
+ }
+ }
+ }
+ }
+ }
}
+ },
+ "post": {
+ "summary": "Create a Driver",
+ "tags": [
+ "Drivers"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Driver"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Driver"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
},
- "parameters": {
- "entityId": {
+ "/drivers/{id}": {
+ "put": {
+ "summary": "Update a Driver",
+ "tags": [
+ "Drivers"
+ ],
+ "parameters": [
+ {
"name": "id",
"in": "path",
"required": true,
- "type": "integer"
- },
- "all": {
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Driver"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Driver"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ },
+ "delete": {
+ "summary": "Delete a Driver",
+ "tags": [
+ "Drivers"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ }
+ }
+ },
+ "/maintenance": {
+ "get": {
+ "summary": "Fetch a list of Maintenance",
+ "description": "Without params, it returns a list of Maintenance the user has access to",
+ "tags": [
+ "Maintenance"
+ ],
+ "parameters": [
+ {
"name": "all",
"in": "query",
"description": "Can only be used by admins or managers to fetch all entities",
- "type": "boolean"
- },
- "refresh": {
- "name": "refresh",
- "in": "query",
- "required": false,
- "type": "boolean"
- },
- "userId": {
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
"name": "userId",
"in": "query",
"description": "Standard users can use this only with their own _userId_",
- "type": "integer"
- },
- "deviceId": {
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
"name": "deviceId",
"in": "query",
"description": "Standard users can use this only with _deviceId_s, they have access to",
- "type": "integer"
- },
- "groupId": {
+ "schema": {
+ "type": "integer"
+ }
+ },
+ {
"name": "groupId",
"in": "query",
"description": "Standard users can use this only with _groupId_s, they have access to",
- "type": "integer"
- },
- "Device": {
- "name": "body",
- "in": "body",
- "required": true,
"schema": {
- "$ref": "#/definitions/Device"
+ "type": "integer"
}
- },
- "Permission": {
- "name": "body",
- "in": "body",
+ },
+ {
+ "name": "refresh",
+ "in": "query",
+ "schema": {
+ "type": "boolean"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Maintenance"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "summary": "Create a Maintenance",
+ "tags": [
+ "Maintenance"
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Maintenance"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Maintenance"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ }
+ },
+ "/maintenance/{id}": {
+ "put": {
+ "summary": "Update a Maintenance",
+ "tags": [
+ "Maintenance"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
"required": true,
"schema": {
- "$ref": "#/definitions/Permission"
- }
- },
- "Group": {
- "name": "body",
- "in": "body",
+ "type": "integer"
+ }
+ }
+ ],
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Maintenance"
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "200": {
+ "description": "OK",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Maintenance"
+ }
+ }
+ }
+ }
+ },
+ "x-codegen-request-body-name": "body"
+ },
+ "delete": {
+ "summary": "Delete a Maintenance",
+ "tags": [
+ "Maintenance"
+ ],
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
"required": true,
"schema": {
- "$ref": "#/definitions/Group"
+ "type": "integer"
+ }
+ }
+ ],
+ "responses": {
+ "204": {
+ "description": "No Content",
+ "content": {}
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Position": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "deviceId": {
+ "type": "integer"
+ },
+ "protocol": {
+ "type": "string"
+ },
+ "deviceTime": {
+ "type": "string",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "format": "date-time"
+ },
+ "fixTime": {
+ "type": "string",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "format": "date-time"
+ },
+ "serverTime": {
+ "type": "string",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "format": "date-time"
+ },
+ "outdated": {
+ "type": "boolean"
+ },
+ "valid": {
+ "type": "boolean"
+ },
+ "latitude": {
+ "type": "number"
+ },
+ "longitude": {
+ "type": "number"
+ },
+ "altitude": {
+ "type": "number"
+ },
+ "speed": {
+ "type": "number",
+ "description": "in knots"
+ },
+ "course": {
+ "type": "number"
+ },
+ "address": {
+ "type": "string"
+ },
+ "accuracy": {
+ "type": "number"
+ },
+ "network": {
+ "type": "object",
+ "properties": {}
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ },
+ "User": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "email": {
+ "type": "string"
+ },
+ "phone": {
+ "type": "string"
+ },
+ "readonly": {
+ "type": "boolean"
+ },
+ "administrator": {
+ "type": "boolean"
+ },
+ "map": {
+ "type": "string"
+ },
+ "latitude": {
+ "type": "number"
+ },
+ "longitude": {
+ "type": "number"
+ },
+ "zoom": {
+ "type": "integer"
+ },
+ "password": {
+ "type": "string"
+ },
+ "twelveHourFormat": {
+ "type": "boolean"
+ },
+ "coordinateFormat": {
+ "type": "string"
+ },
+ "disabled": {
+ "type": "boolean"
+ },
+ "expirationTime": {
+ "type": "string",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "format": "date-time"
+ },
+ "deviceLimit": {
+ "type": "integer"
+ },
+ "userLimit": {
+ "type": "integer"
+ },
+ "deviceReadonly": {
+ "type": "boolean"
+ },
+ "limitCommands": {
+ "type": "boolean"
+ },
+ "poiLayer": {
+ "type": "string"
+ },
+ "token": {
+ "type": "string"
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ },
+ "Server": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "registration": {
+ "type": "boolean"
+ },
+ "readonly": {
+ "type": "boolean"
+ },
+ "deviceReadonly": {
+ "type": "boolean"
+ },
+ "limitCommands": {
+ "type": "boolean"
+ },
+ "map": {
+ "type": "string"
+ },
+ "bingKey": {
+ "type": "string"
+ },
+ "mapUrl": {
+ "type": "string"
+ },
+ "poiLayer": {
+ "type": "string"
+ },
+ "latitude": {
+ "type": "number"
+ },
+ "longitude": {
+ "type": "number"
+ },
+ "zoom": {
+ "type": "integer"
+ },
+ "twelveHourFormat": {
+ "type": "boolean"
+ },
+ "version": {
+ "type": "string"
+ },
+ "forceSettings": {
+ "type": "boolean"
+ },
+ "coordinateFormat": {
+ "type": "string"
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ },
+ "Command": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "deviceId": {
+ "type": "integer"
+ },
+ "description": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ },
+ "Device": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "uniqueId": {
+ "type": "string"
+ },
+ "status": {
+ "type": "string"
+ },
+ "disabled": {
+ "type": "boolean"
+ },
+ "lastUpdate": {
+ "type": "string",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "format": "date-time"
+ },
+ "positionId": {
+ "type": "integer"
+ },
+ "groupId": {
+ "type": "integer"
+ },
+ "phone": {
+ "type": "string"
+ },
+ "model": {
+ "type": "string"
+ },
+ "contact": {
+ "type": "string"
+ },
+ "category": {
+ "type": "string"
+ },
+ "geofenceIds": {
+ "type": "array",
+ "items": {
+ "type": "integer"
}
- },
- "User": {
- "name": "body",
- "in": "body",
- "required": true,
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ },
+ "Group": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "groupId": {
+ "type": "integer"
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ },
+ "Permission": {
+ "type": "object",
+ "properties": {
+ "userId": {
+ "type": "integer",
+ "description": "User Id, can be only first parameter"
+ },
+ "deviceId": {
+ "type": "integer",
+ "description": "Device Id, can be first parameter or second only in combination with userId"
+ },
+ "groupId": {
+ "type": "integer",
+ "description": "Group Id, can be first parameter or second only in combination with userId"
+ },
+ "geofenceId": {
+ "type": "integer",
+ "description": "Geofence Id, can be second parameter only"
+ },
+ "calendarId": {
+ "type": "integer",
+ "description": "Calendar Id, can be second parameter only and only in combination with userId"
+ },
+ "attributeId": {
+ "type": "integer",
+ "description": "Computed Attribute Id, can be second parameter only"
+ },
+ "driverId": {
+ "type": "integer",
+ "description": "Driver Id, can be second parameter only"
+ },
+ "managedUserId": {
+ "type": "integer",
+ "description": "User Id, can be second parameter only and only in combination with userId"
+ }
+ },
+ "description": "This is a permission map that contain two object indexes. It is used to link/unlink objects. Order is important. Example: { deviceId:8, geofenceId: 16 }"
+ },
+ "CommandType": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string"
+ }
+ }
+ },
+ "Geofence": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "area": {
+ "type": "string"
+ },
+ "calendarId": {
+ "type": "integer"
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ },
+ "Notification": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "type": {
+ "type": "string"
+ },
+ "always": {
+ "type": "boolean"
+ },
+ "web": {
+ "type": "boolean"
+ },
+ "mail": {
+ "type": "boolean"
+ },
+ "sms": {
+ "type": "boolean"
+ },
+ "calendarId": {
+ "type": "integer"
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ },
+ "NotificationType": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string"
+ }
+ }
+ },
+ "Event": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "type": {
+ "type": "string"
+ },
+ "serverTime": {
+ "type": "string",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "format": "date-time"
+ },
+ "deviceId": {
+ "type": "integer"
+ },
+ "positionId": {
+ "type": "integer"
+ },
+ "geofenceId": {
+ "type": "integer"
+ },
+ "maintenanceId": {
+ "type": "integer"
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ },
+ "ReportSummary": {
+ "type": "object",
+ "properties": {
+ "deviceId": {
+ "type": "integer"
+ },
+ "deviceName": {
+ "type": "string"
+ },
+ "maxSpeed": {
+ "type": "number",
+ "description": "in knots"
+ },
+ "averageSpeed": {
+ "type": "number",
+ "description": "in knots"
+ },
+ "distance": {
+ "type": "number",
+ "description": "in meters"
+ },
+ "spentFuel": {
+ "type": "number",
+ "description": "in liters"
+ },
+ "engineHours": {
+ "type": "integer"
+ }
+ }
+ },
+ "ReportTrips": {
+ "type": "object",
+ "properties": {
+ "deviceId": {
+ "type": "integer"
+ },
+ "deviceName": {
+ "type": "string"
+ },
+ "maxSpeed": {
+ "type": "number",
+ "description": "in knots"
+ },
+ "averageSpeed": {
+ "type": "number",
+ "description": "in knots"
+ },
+ "distance": {
+ "type": "number",
+ "description": "in meters"
+ },
+ "spentFuel": {
+ "type": "number",
+ "description": "in liters"
+ },
+ "duration": {
+ "type": "integer"
+ },
+ "startTime": {
+ "type": "string",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "format": "date-time"
+ },
+ "startAddress": {
+ "type": "string"
+ },
+ "startLat": {
+ "type": "number"
+ },
+ "startLon": {
+ "type": "number"
+ },
+ "endTime": {
+ "type": "string",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "format": "date-time"
+ },
+ "endAddress": {
+ "type": "string"
+ },
+ "endLat": {
+ "type": "number"
+ },
+ "endLon": {
+ "type": "number"
+ },
+ "driverUniqueId": {
+ "type": "integer"
+ },
+ "driverName": {
+ "type": "string"
+ }
+ }
+ },
+ "ReportStops": {
+ "type": "object",
+ "properties": {
+ "deviceId": {
+ "type": "integer"
+ },
+ "deviceName": {
+ "type": "string"
+ },
+ "duration": {
+ "type": "integer"
+ },
+ "startTime": {
+ "type": "string",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "format": "date-time"
+ },
+ "address": {
+ "type": "string"
+ },
+ "lat": {
+ "type": "number"
+ },
+ "lon": {
+ "type": "number"
+ },
+ "endTime": {
+ "type": "string",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "format": "date-time"
+ },
+ "spentFuel": {
+ "type": "number",
+ "description": "in liters"
+ },
+ "engineHours": {
+ "type": "integer"
+ }
+ }
+ },
+ "Statistics": {
+ "type": "object",
+ "properties": {
+ "captureTime": {
+ "type": "string",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "format": "date-time"
+ },
+ "activeUsers": {
+ "type": "integer"
+ },
+ "activeDevices": {
+ "type": "integer"
+ },
+ "requests": {
+ "type": "integer"
+ },
+ "messagesReceived": {
+ "type": "integer"
+ },
+ "messagesStored": {
+ "type": "integer"
+ }
+ }
+ },
+ "DeviceAccumulators": {
+ "type": "object",
+ "properties": {
+ "deviceId": {
+ "type": "integer"
+ },
+ "totalDistance": {
+ "type": "number",
+ "description": "in meters"
+ },
+ "hours": {
+ "type": "number"
+ }
+ }
+ },
+ "Calendar": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "data": {
+ "type": "string",
+ "description": "base64 encoded in iCalendar format"
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ },
+ "Attribute": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "description": {
+ "type": "string"
+ },
+ "attribute": {
+ "type": "string"
+ },
+ "expression": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string",
+ "description": "String|Number|Boolean"
+ }
+ }
+ },
+ "Driver": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "uniqueId": {
+ "type": "string"
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ },
+ "Maintenance": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "integer"
+ },
+ "name": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "start": {
+ "type": "number"
+ },
+ "period": {
+ "type": "number"
+ },
+ "attributes": {
+ "type": "object",
+ "properties": {}
+ }
+ }
+ }
+ },
+ "parameters": {
+ "entityId": {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "integer"
+ }
+ },
+ "all": {
+ "name": "all",
+ "in": "query",
+ "description": "Can only be used by admins or managers to fetch all entities",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ "refresh": {
+ "name": "refresh",
+ "in": "query",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ "userId": {
+ "name": "userId",
+ "in": "query",
+ "description": "Standard users can use this only with their own _userId_",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ "deviceId": {
+ "name": "deviceId",
+ "in": "query",
+ "description": "Standard users can use this only with _deviceId_s, they have access to",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ "groupId": {
+ "name": "groupId",
+ "in": "query",
+ "description": "Standard users can use this only with _groupId_s, they have access to",
+ "schema": {
+ "type": "integer"
+ }
+ },
+ "deviceIdArray": {
+ "name": "deviceId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ }
+ },
+ "groupIdArray": {
+ "name": "groupId",
+ "in": "query",
+ "style": "form",
+ "explode": true,
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ }
+ }
+ },
+ "fromTime": {
+ "name": "from",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ }
+ },
+ "toTime": {
+ "name": "to",
+ "in": "query",
+ "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
+ "required": true,
+ "schema": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "requestBodies": {
+ "Device": {
+ "content": {
+ "application/json": {
"schema": {
- "$ref": "#/definitions/User"
+ "$ref": "#/components/schemas/Device"
}
+ }
},
- "Geofence": {
- "name": "body",
- "in": "body",
- "required": true,
+ "required": true
+ },
+ "Permission": {
+ "content": {
+ "application/json": {
"schema": {
- "$ref": "#/definitions/Geofence"
+ "$ref": "#/components/schemas/Permission"
}
+ }
},
- "Calendar": {
- "name": "body",
- "in": "body",
- "required": true,
+ "required": true
+ },
+ "Group": {
+ "content": {
+ "application/json": {
"schema": {
- "$ref": "#/definitions/Calendar"
+ "$ref": "#/components/schemas/Group"
}
+ }
},
- "Attribute": {
- "name": "body",
- "in": "body",
- "required": true,
+ "required": true
+ },
+ "User": {
+ "content": {
+ "application/json": {
"schema": {
- "$ref": "#/definitions/Attribute"
+ "$ref": "#/components/schemas/User"
}
+ }
},
- "Driver": {
- "name": "body",
- "in": "body",
- "required": true,
+ "required": true
+ },
+ "Geofence": {
+ "content": {
+ "application/json": {
"schema": {
- "$ref": "#/definitions/Driver"
+ "$ref": "#/components/schemas/Geofence"
}
+ }
},
- "Command": {
- "name": "body",
- "in": "body",
- "required": true,
+ "required": true
+ },
+ "Calendar": {
+ "content": {
+ "application/json": {
"schema": {
- "$ref": "#/definitions/Command"
+ "$ref": "#/components/schemas/Calendar"
}
+ }
},
- "Notification": {
- "name": "body",
- "in": "body",
- "required": true,
+ "required": true
+ },
+ "Attribute": {
+ "content": {
+ "application/json": {
"schema": {
- "$ref": "#/definitions/Notification"
+ "$ref": "#/components/schemas/Attribute"
}
+ }
},
- "Maintenance": {
- "name": "body",
- "in": "body",
- "required": true,
+ "required": true
+ },
+ "Driver": {
+ "content": {
+ "application/json": {
"schema": {
- "$ref": "#/definitions/Maintenance"
+ "$ref": "#/components/schemas/Driver"
}
+ }
},
- "deviceIdArray": {
- "name": "deviceId",
- "in": "query",
- "type": "array",
- "items": {
- "type": "integer"
- },
- "collectionFormat": "multi"
+ "required": true
+ },
+ "Command": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Command"
+ }
+ }
},
- "groupIdArray": {
- "name": "groupId",
- "in": "query",
- "type": "array",
- "items": {
- "type": "integer"
- },
- "collectionFormat": "multi"
+ "required": true
+ },
+ "Notification": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Notification"
+ }
+ }
},
- "fromTime": {
- "name": "from",
- "in": "query",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
- "required": true,
- "type": "string",
- "format": "date-time"
+ "required": true
+ },
+ "Maintenance": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Maintenance"
+ }
+ }
},
- "toTime": {
- "name": "to",
- "in": "query",
- "description": "in IS0 8601 format. eg. `1963-11-22T18:30:00Z`",
- "required": true,
- "type": "string",
- "format": "date-time"
- }
+ "required": true
+ }
},
- "securityDefinitions": {
- "basicAuth": {
- "type": "basic",
- "description": "Basic HTTP authorization with _email_ and _password_"
- }
+ "securitySchemes": {
+ "basicAuth": {
+ "type": "http",
+ "description": "Basic HTTP authorization with _email_ and _password_",
+ "scheme": "basic"
+ }
}
-}
+ }
+} \ No newline at end of file
diff --git a/templates/export/summary.xlsx b/templates/export/summary.xlsx
index be16e8931..3fcf461ff 100644
--- a/templates/export/summary.xlsx
+++ b/templates/export/summary.xlsx
Binary files differ
diff --git a/templates/full/deviceInactive.vm b/templates/full/deviceInactive.vm
new file mode 100644
index 000000000..51aead653
--- /dev/null
+++ b/templates/full/deviceInactive.vm
@@ -0,0 +1,12 @@
+#set($subject = "$device.name: inactive")
+#set($lastUpdate = $dateTool.getDate())
+#set($ignore = $lastUpdate.setTime($event.getLong("lastUpdate")))
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Inactive<br>
+Last Update: $dateTool.format("YYYY-MM-dd HH:mm:ss", $lastUpdate, $locale, $timezone)<br>
+Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a>
+</body>
+</html>
diff --git a/templates/short/deviceInactive.vm b/templates/short/deviceInactive.vm
new file mode 100644
index 000000000..d7431124c
--- /dev/null
+++ b/templates/short/deviceInactive.vm
@@ -0,0 +1,3 @@
+#set($lastUpdate = $dateTool.getDate())
+#set($ignore = $lastUpdate.setTime($event.getLong("lastUpdate")))
+$device.name inactive from $dateTool.format("YYYY-MM-dd HH:mm:ss", $lastUpdate, $locale, $timezone)
diff --git a/tools/test-map.py b/tools/test-map.py
new file mode 100755
index 000000000..0568b283e
--- /dev/null
+++ b/tools/test-map.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+import urllib
+import urllib2
+import httplib
+import random
+import json
+
+server = 'localhost:5055'
+baseUrl = 'http://localhost:8082'
+user = { 'email' : 'admin', 'password' : 'admin' }
+devices = 500
+
+def login():
+ request = urllib2.Request(baseUrl + '/api/session')
+ response = urllib2.urlopen(request, urllib.urlencode(user))
+ return response.headers.get('Set-Cookie')
+
+def add_device(cookie, unique_id):
+ request = urllib2.Request(baseUrl + '/api/devices')
+ request.add_header('Cookie', cookie)
+ request.add_header('Content-Type', 'application/json')
+ device = { 'name' : unique_id, 'uniqueId' : unique_id }
+ response = urllib2.urlopen(request, json.dumps(device))
+ data = json.load(response)
+ return data['id']
+
+def send_message(conn, device_id):
+ params = (('id', device_id), ('lat', random.uniform(59, 61)), ('lon', random.uniform(29, 31)))
+ conn.request('GET', '?' + urllib.urlencode(params))
+ conn.getresponse().read()
+
+cookie = login()
+conn = httplib.HTTPConnection(server)
+
+for i in range(devices):
+ device_id = "{0:0>6}".format(i)
+ add_device(cookie, device_id)
+ send_message(conn, device_id)
diff --git a/tools/test-photo.sh b/tools/test-photo.sh
new file mode 100755
index 000000000..2333462fe
--- /dev/null
+++ b/tools/test-photo.sh
@@ -0,0 +1,103 @@
+#!/bin/bash
+
+echo "\
+imei:123456789012345,vt10,200729133914,,F,163910.000,A,1556.7517,S,04816.3609,W,0.00,0,,0,0,0.00%,,;\
+\
+imei:123456789012345,vr,0000fa01ffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a73\
+6250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c\
+7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110800f0014003012100021101031101ffdd0004000affc401\
+a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613\
+516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a\
+636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9ca\
+d2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100\
+020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718\
+191a262728292a35363738393a434445464748494a535455565758595a636465666768696a7374757677b800;\
+\
+imei:123456789012345,vr,0100fa0178797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6\
+c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00956d5f80cd1423a61064ff004159fa\
+c466dee1156591a374cf27a9efd29682bdca4b8c715a1a53fefdd3fbe9fa8a064dae2efb68e51fc2ff00a115cbcff2bb81c60e7eb4090d0369c9f5e2\
+af698fbe39e1f4f9c7f23fd28296e3255c3551957121f7e6921c86d771a34be7e996ec79fdded3f871fd29b24c7d394c5777307a0603f03ffd7a76ab\
+692dd3c5e426e3cfb003aff5a4808adf46ba5911d9e25dac0e324f4fc2ac368aaf2bbbcc7e6627013ffaf4c0963d2a18d1977390d8cf23b7e1532d9c\
+29d327f134800da5b93b8c48c7d5941fe74e10c43eec4a3e831480ffd076c03f871f8546d2449f79e35fab015170233796c3fe5bc5f830a8db50b51f\
+f2d81fa293fd28018753b61fc6c7e88698755b7ecb29ff00808ff1a0086e7538e6b592108ff38e09c71fad665030abda71f95fea29302f8a5a918545\
+2cc1320e455455c96c8bce53de992b0f971eb4dab05cb9b432f22ab358c6c49dcc09fc6a53b1427d94a0e1f77d4544c197b552607fffd1e7ae16f2d6\
+ee17b994b9dc18306dc3ad6ceabfbeb48a65e763633ec6908cf4e953d9c9e55d46dd006c1fa74a066bdf1300;\
+\
+imei:123456789012345,vr,0200fa01279963327521491f8735ca4c06fde7b8a0457639356b4d5916ed1846c54fca78ec681962e930c7eb4965a67f\
+6848c3cc09b067eb528b91a917876d53991ddff4ad5b4863b5458a25da80f4a641893e2dbc40c58855639c9e0722af0bcb4857e6b88c9031f29ddfca\
+9011beb168bf74c8ff00eea7f8e2a07d6e3fe081cffbcc07f8d016216d6a53f7608c7d589ff0a85b55ba6e8635ff00753fc7340ec42d7f76dd6761f4\
+c0fe55134f337de9a53f5734058fffd2e648cf5e7eb49803b0a8285a4a041450025140055dd3ba49f87f5a4f602f8a5a918b475e0d0046d6f13758d7\
+f0e3f9545f64404e198fa67b55733158b038141a918d34d650c3045007ffd3ce88c1716eb02c32b96c12c4e769f5000ad2b7b391f44b88e61fbc5538\
+f7c5240cc446cad293e94867450c826891fb3a827f91aa29e1f83acd2349ec3814c45c8b4cb487ee40b9f53cd4af35bdbf0f2c51fb640fd280306fc2\
+33978c8646e411dea0b1bb6b3959d1431c6307bff9c549a3d8b126b176ff0074c71ffba9fe39aad25ddccbf7ee253ec1b03f4a08b107d692800a4a06\
+2514084c8a72c6eff7119bfdd526819fffd4c15b2ba6e96f2fe2b8fe7522e9776dff002c82fd5c540c9068f707ef3c43f127fa53d7466fe29c0fa2e7\
+fad30b8f1a347fc5339fa2814f1a45b8ead29fc47f8501717fb32d4750c7eaf4e163663fe59afe2c7fc64000;\
+\
+imei:123456789012345,vr,0300fa01815c5169663fe5947fcea65b583cb3e4a229ff00645017212bb4e08c1a4c56650b450014845001da92801292\
+803fffd5a9a25bc97b68ca6f658a389b1e5c6003cf39cfe75bf676f1dac7e4a17287392edb8f3484ce4a58fc99e58bfb8e47eb4dcd228bf6baa0b6b7\
+58fca2eea4e0eec0c5365d66e9fee6c8c7fb2b93fad20b1524b99e6ff5b348c3d0b71f9543803a0a00b511f32d4af743fa1ff26a9b8c31a45f417b50\
+39381c9f6a6497ed34a6b88f7c8ed17cd8c18f9faf5a6269178e065153fde71fd29d857265d0e53f7e68d7e809ff000a997438c7df9dcffbaa07f8d1\
+60b922e93669f795dbfde7ff000c5482d2ca3e90c5f88ddfce815c786863fb8aabfeeae283723fda3401ffd60dc7a2feb4c33b7602a406999fdbf2a6\
+99643fc46801bbdcf563f9d34e7d680130693140062a48a428d91480b4e8b3a065fbdfe78aaa460e0d2921a0a2a461450014868010d36803ffd7c3d3\
+6fa6b277f2481e60c1c8cf4ff26ac4b7d7537fac9dc8f40703f4a91d8af494804a33400f8ede697fd5c523fd149ab51e9178fd63083fdb6029d80b49\
+a5496d148ef2ab7cbcaa83fceb2675c31a4cb4ee8d9d2e0b47b2479228da404862c33dff00c315a0b24518c46028f455c5519b2379b2e08ce31d29a6\
+76ec050218d2b9ef8a61673d58fe7486369314c04c5262901fffd08b1498a90131498a0031498a004c51f100;\
+\
+imei:123456789012345,vr,0400fa0140071450049148636e3a77153cb109577a7defe740157a1e68acca0a2800a4a004349401ffd1e601c10476ab\
+91ab48711ab37d066a4a2d47a5ddc9ff002cb60f5738ab71e84c7fd6cea3d95734ec2b965347b48fefef7ff79b03f4a9d22b583fd5c51a9f50b93f9d\
+02b8f3703b02698676ec00a6218d23b0209e0f06b06f23d9211ef52cb816749933be33f51fe7f2ad2c5084f713146298842293140094d3480292803f\
+ffd28e92a404c518a004c518a004c518a004c518a002a6865d8707ee9a009278770de9d7f9d55a990d051523128a004a43401fffd3e63bd74da35d86\
+d3d131f3464a1f7ee3f43fa54a1b2e99dbb002986473dea8919c9eb4628017145002566ea51f3b877a4cb8ee54d3e4f2aed33d09c7e75b99a4852dc4\
+a4a6210d262800c526280131462901ffd44c5262a404c518a004c518a004c518a004c518a006e28a00b104b8f958f1da92e21c12ea38ef49ec08af45\
+41421a4a004a2803ffd5e66b4b459765cb447a48bc7d47ff005b3528a66dd1544066933400b9a4a004c541791ee84fb52635b98adf23d6fc6de646ae\
+3f8803491531d8a5c5324691498a003146290098a4a00fffd628a9010d250025140094940094500252500156a09770dadd7f9d00453c3b3e65fba7f4\
+a82a1e834069290c4a4a00ffd7e645490c86195245ea841fad4947500860194e548c83ed455102e28c501f00;\
+\
+imei:123456789012345,vr,0500fa01018a2800a465dea57d462819cf5d82ae78e6b5f4b62f61193db23f5a94548b54532469346680128a40252628\
+03ffd05c5262a4031498a004c518a004c518a004c52500262928012941c1c8a00b71b895083d7bd559a2f2dbd41e9498223a4a8284349401ffd1e616\
+9d5251d0693379b64aa4fcd19da7e9dbfcfb55dc5510c28a0028cd002668a00864b58656cc8993f5c548aa1142a80aa3a014877b8b4500368c5200c5\
+18a004c518a00fffd27628a9013145002518a004c5250021a4a004a2900869b4c0723146c8ab5f2cb1fb1fd280293a1462a69b599421a4a00fffd3e6\
+3bd3aa4b343479bcbba319e928c7e2391fd6b7334d10c4a5a620a2818514005148028a006d2d200a4a003149401fffd47d15201486801292800a4a00\
+4a43400da4a004a4a002a4864d8dcf4a009a68c489c751d2a91c8383c54c868434d35233ffd5e629c3a5496391ca3aba9c329047d6ba88dc4b1ac8bd\
+1c0229a258ea2992145030a5a0028a40251da80129690094500149401fffd69292a40292801292800a4a004a4a004c5250021a6d00251480b1049fc2\
+7f0a4b98b237af5ef4dea80a94959947ffd7e60d28a92c756de8d36fb7688f58ce47d0ff00f5f342259a145512145030a2900533ccc9381d2800dcde\
+d4d2cdeb4809051400514005262803ffd093145480525002628a004a4a00434940094868010d34d002524300;\
+\
+imei:123456789012345,vr,0600fa0150000e2adc526f5e7af7a0456b88b6364743505432d1ffd1e69873495258eea2ad69d379176849c2b7cadf43\
+ff00d7c5023a1a4aa2028a062d14804638526a34e05003b04ff09a42ad9048c5003c52d200a2800a4a00ffd29a9315201494009450021a4a004a4a00\
+4a4a0069a43400d34940094e47dac08a00b471227b1aa32214620d292047ffd3e7e45c13ed50915058a29d4c0e8ace6f3ed9243f7b186fa8a9ea880a\
+5a0029690051400521a0029690052500145007ffd49a8a90128a004a4a000d3680128a006d21a0069a43400d3486801b4669012c326d383d0d493209\
+17dc5303ffd5c9bd87ca998763d2a830c1a82c4069e28034f479b0ef093c30dc3ea3aff9f6ad6aa4430a5a602d148028a005a4340051400514804a28\
+03ffd69a8a90128a004a4a004a4a004a28010d34d0021a69a0069a69a00434da4019ab3149b860f514c0ffd7835087cc8b7e395fe558b28c66a5ee52\
+d884d3d0e45032686430cc922ff09cfd6ba30430054e41e41a68963a8a62168a0028a402d277a0028a0028a40251401fffd09a8a90129280128a004a\
+28012928010d368010d34d0034d34d0034d34d201334aafb4e45007fffd179c1183c8358d7b6fe5b9c0e3b54b1c4cf618a1786a064a2b6f4c9bccb50\
+84fcd19dbf876ff3ed42065ca5aa2428a402d1400b486800a2800a4a40145007ffd29a92a404a2801292be00;\
+\
+imei:123456789012345,vr,0700fa01800a4a0029a6801292801a4d34d00349a693400d34841a40262908a00fffd35a82f2112c44f714982dcc5953\
+150f439a945b241d2ae69b379574a09f95fe53fd29899b94b544851400b45002d21a40252d00149480292803ffd49a92a404a4a0029280129280109a\
+4a004e68c1a006914981400d2290d0034d34d002534d007fffd55a2811937d079721c743d2a830f6a834043c5482981d05a4de7dbab9fbdd1beb5353\
+205a298052d0014948028a00293348033499a00fffd6949a4cd4809460d00260d18a00314981400521a004a4a00434d3400d34d3400d3484500260d2\
+6d3401ffd72968110dd442588fa8e958b22e09a97b96b6210706a5140cd0d2a6db2b447a38c8fa8ad5cd344b173466988334b9a4014940073460d200\
+c518a6018a31480fffd09a8a9012834009494005250025250021a4a004c1a4da68010ad34ad0026d1487028010914c6902f5e3eb401fffd14cd2d021\
+6b32fa0d8f91d0f349951339c60d0add052289918a3865e194e457411b09635917a30cd08963f6d18aa10b8a5a4014500252d0025140051480ffd29a\
+8a9012834009460d001b4d1b0d001b3de8d82800da3d2931400d3814d26802a35d824855ce0f5cf5a8dae1cf4c0fd6818b14a4921dbaf4a909a04319\
+d47522abcb26fe074fe7480fffd35a514085a8ae6212c44771d28608c399307a5573c1a93464e0e466b59d00;\
+\
+imei:123456789012345,vr,0800fa017499b2ad09edf32fd3bd084cd1a2a89168a00292900514005140060d2ed3480fffd4b1b0d2eca900d946d140\
+060518a0038a692280137534b50034b1a6b13eb400de7bd34d005436cca3e5607ebc546d1385dc474a0647494806d14c0fffd55a70a0414b40199a8d\
+bed6de0706b2dd706a0d3742c67b558b794c132483f84f23d477a6074406e00af208c834bb0d5102f967da97cbf7a401e5fbd2f962800d829768f4a0\
+0303d28a4014991401ffd6b7b8526ea90137526ea004cd26680133499a004cd349a004269a680109a69a0069a69e7ad0046d12375503e9c544d6e3f8\
+588fad0030dbbe382a6a368dd4f2a7f0e6819fffd7b3b052841e94085da3d29703d28022b8844d0b2639ed5cedc45b1cad4b2e3b158706a61cf340cd\
+dd22e3ccb631b1f9a33c7d2afee14d12c4dc28dc281016a4df4009b8d1b8d0026e3eb46680133499a00fffd09f346690099a334804cd266980949400\
+9494008690d0034d34d002536801b486900869a6803fffd19f71a371a042ee346ea00375676a56f9fde28ebd7eb49951dcc79170688dbb5228b9653f\
+d9ee55cfdd3c37d2b7f34d12c4cd19a62026933400668cd002668cd200a4a00fffd29a92900525001494009494009450036928010d34d0021a69a402\
+1a69a00434da00ffd3928a042e68a005a4750ea55ba1a00c3bb80c6c41154bee9a8342653919adcd3e7f1100;\
+\
+imei:123456789012345,vr,09005a013adc027e74f94ff434d0996a8aa244349400b4500251480292803fffd4969290051400945002525002525002\
+525002534d0034d250021a6d20129b401fffd57d2d020a5a00296802adf40248f701cafea2b0e54c1a97b96b61a8db4d5dd3e6315da0ed21da7f1a01\
+9b94b54488692800a2800a4a40145007ffd6928a402514005250025250021a4a004a434009486801b4940094d3480434d3401fffd7928a042d140052\
+d001591a85bf96f951f29e949951331860d58b23bae601dc48bfce90d9d1d15448869280168a40251400945007ffd092929005250025140094500368\
+a00434da004a4a004a43400da426900da43401ffd1968a041450014b4005473c4268ca1fc3da8030aead658db0636fa81906a6d32ce5fb524af1958d\
+39cb0c64f6a455cdca298869a2800a280128a00292901fffd27d266900525001494009494005250025266801b9a42680133499a00434d34804a4a00f\
+ffd92c00;\
+" | nc "localhost" "5001"