aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/traccar/BasePipelineFactory.java4
-rw-r--r--src/main/java/org/traccar/Context.java14
-rw-r--r--src/main/java/org/traccar/MainModule.java23
-rw-r--r--src/main/java/org/traccar/api/AsyncSocket.java9
-rw-r--r--src/main/java/org/traccar/api/resource/EventResource.java20
-rw-r--r--src/main/java/org/traccar/api/resource/OrderResource.java35
-rw-r--r--src/main/java/org/traccar/api/resource/PasswordResource.java7
-rw-r--r--src/main/java/org/traccar/api/resource/PermissionsResource.java68
-rw-r--r--src/main/java/org/traccar/api/resource/SessionResource.java33
-rw-r--r--src/main/java/org/traccar/config/Keys.java30
-rw-r--r--src/main/java/org/traccar/database/ConnectionManager.java9
-rw-r--r--src/main/java/org/traccar/database/DataManager.java7
-rw-r--r--src/main/java/org/traccar/database/OrderManager.java26
-rw-r--r--src/main/java/org/traccar/database/PermissionsManager.java9
-rw-r--r--src/main/java/org/traccar/geocoder/HereGeocoder.java4
-rw-r--r--src/main/java/org/traccar/geocoder/JsonGeocoder.java8
-rw-r--r--src/main/java/org/traccar/geocoder/MapTilerGeocoder.java73
-rw-r--r--src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java10
-rw-r--r--src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java2
-rw-r--r--src/main/java/org/traccar/handler/DistanceHandler.java8
-rw-r--r--src/main/java/org/traccar/handler/events/BehaviorEventHandler.java63
-rw-r--r--src/main/java/org/traccar/handler/events/GeofenceEventHandler.java11
-rw-r--r--src/main/java/org/traccar/helper/BufferUtil.java29
-rw-r--r--src/main/java/org/traccar/model/Order.java60
-rw-r--r--src/main/java/org/traccar/model/Position.java1
-rw-r--r--src/main/java/org/traccar/notification/NotificationFormatter.java9
-rw-r--r--src/main/java/org/traccar/notification/NotificationMessage.java (renamed from src/main/java/org/traccar/notification/FullMessage.java)4
-rw-r--r--src/main/java/org/traccar/notification/TextTemplateFormatter.java15
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorFirebase.java8
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorMail.java6
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorPushover.java8
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorSms.java7
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorTelegram.java9
-rw-r--r--src/main/java/org/traccar/protocol/AppletProtocolDecoder.java48
-rw-r--r--src/main/java/org/traccar/protocol/B2316Protocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java162
-rw-r--r--src/main/java/org/traccar/protocol/BceProtocolDecoder.java24
-rw-r--r--src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java92
-rw-r--r--src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/Dsf22FrameDecoder.java44
-rw-r--r--src/main/java/org/traccar/protocol/Dsf22Protocol.java40
-rw-r--r--src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java90
-rw-r--r--src/main/java/org/traccar/protocol/DualcamFrameDecoder.java49
-rw-r--r--src/main/java/org/traccar/protocol/DualcamProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java123
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/EsealProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java98
-rw-r--r--src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/FlexApiProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java101
-rw-r--r--src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java17
-rw-r--r--src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java56
-rw-r--r--src/main/java/org/traccar/protocol/H02ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/HoopoProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java72
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java7
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java159
-rw-r--r--src/main/java/org/traccar/protocol/JsonFrameDecoder.java55
-rw-r--r--src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java21
-rw-r--r--src/main/java/org/traccar/protocol/KhdProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/LacakProtocol.java (renamed from src/main/java/org/traccar/protocol/AppletProtocol.java)8
-rw-r--r--src/main/java/org/traccar/protocol/LacakProtocolDecoder.java95
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MegastekFrameDecoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java35
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java118
-rw-r--r--src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java17
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java96
-rw-r--r--src/main/java/org/traccar/protocol/MobilogixProtocol.java4
-rw-r--r--src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java75
-rw-r--r--src/main/java/org/traccar/protocol/MxtProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/NavtelecomFrameDecoder.java75
-rw-r--r--src/main/java/org/traccar/protocol/NavtelecomProtocol.java5
-rw-r--r--src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java222
-rw-r--r--src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java15
-rw-r--r--src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocol.java4
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java19
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java33
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocolDecoder.java61
-rw-r--r--src/main/java/org/traccar/protocol/StbProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/StbProtocolDecoder.java149
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java117
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocolDecoder.java149
-rw-r--r--src/main/java/org/traccar/protocol/TechtoCruzFrameDecoder.java44
-rw-r--r--src/main/java/org/traccar/protocol/TechtoCruzProtocol.java38
-rw-r--r--src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java108
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java18
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocol.java6
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocolDecoder.java50
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocolEncoder.java66
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocolDecoder.java33
-rw-r--r--src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java95
-rw-r--r--src/main/java/org/traccar/protocol/UuxProtocolDecoder.java5
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocolDecoder.java37
-rw-r--r--src/main/java/org/traccar/protocol/Xexun2FrameDecoder.java69
-rw-r--r--src/main/java/org/traccar/protocol/Xexun2Protocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java205
-rw-r--r--src/main/java/org/traccar/schedule/ScheduleManager.java3
-rw-r--r--src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java36
-rw-r--r--src/main/java/org/traccar/web/WebServer.java17
-rw-r--r--src/test/java/org/traccar/ProtocolTest.java4
-rw-r--r--src/test/java/org/traccar/geocoder/GeocoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java12
-rw-r--r--src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java23
-rw-r--r--src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java42
-rw-r--r--src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java13
-rw-r--r--src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java7
-rw-r--r--src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java47
-rw-r--r--src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java44
-rw-r--r--src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java12
-rw-r--r--src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java16
-rw-r--r--src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java25
-rw-r--r--src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java21
-rw-r--r--src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java10
-rw-r--r--src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java19
-rw-r--r--src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java27
-rw-r--r--src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java2
154 files changed, 4425 insertions, 530 deletions
diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java
index 642c75ea9..c9f3a2346 100644
--- a/src/main/java/org/traccar/BasePipelineFactory.java
+++ b/src/main/java/org/traccar/BasePipelineFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2021 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 @@ import org.traccar.handler.SpeedLimitHandler;
import org.traccar.handler.StandardLoggingHandler;
import org.traccar.handler.TimeHandler;
import org.traccar.handler.events.AlertEventHandler;
+import org.traccar.handler.events.BehaviorEventHandler;
import org.traccar.handler.events.CommandResultEventHandler;
import org.traccar.handler.events.DriverEventHandler;
import org.traccar.handler.events.FuelDropEventHandler;
@@ -132,6 +133,7 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
DefaultDataHandler.class,
CommandResultEventHandler.class,
OverspeedEventHandler.class,
+ BehaviorEventHandler.class,
FuelDropEventHandler.class,
MotionEventHandler.class,
GeofenceEventHandler.class,
diff --git a/src/main/java/org/traccar/Context.java b/src/main/java/org/traccar/Context.java
index fe494dabf..aeba9c4c9 100644
--- a/src/main/java/org/traccar/Context.java
+++ b/src/main/java/org/traccar/Context.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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,6 +38,7 @@ import org.traccar.database.MailManager;
import org.traccar.database.MaintenancesManager;
import org.traccar.database.MediaManager;
import org.traccar.database.NotificationManager;
+import org.traccar.database.OrderManager;
import org.traccar.database.PermissionsManager;
import org.traccar.database.UsersManager;
import org.traccar.geocoder.Geocoder;
@@ -53,6 +54,7 @@ import org.traccar.model.Geofence;
import org.traccar.model.Group;
import org.traccar.model.Maintenance;
import org.traccar.model.Notification;
+import org.traccar.model.Order;
import org.traccar.model.User;
import org.traccar.notification.EventForwarder;
import org.traccar.notification.NotificatorManager;
@@ -235,6 +237,12 @@ public final class Context {
return maintenancesManager;
}
+ private static OrderManager orderManager;
+
+ public static OrderManager getOrderManager() {
+ return orderManager;
+ }
+
private static SmsManager smsManager;
public static SmsManager getSmsManager() {
@@ -337,6 +345,8 @@ public final class Context {
commandsManager = new CommandsManager(dataManager, config.getBoolean(Keys.COMMANDS_QUEUEING));
+ orderManager = new OrderManager(dataManager);
+
}
private static void initEventsModule() {
@@ -397,6 +407,8 @@ public final class Context {
return (BaseObjectManager<T>) maintenancesManager;
} else if (clazz.equals(Notification.class)) {
return (BaseObjectManager<T>) notificationManager;
+ } else if (clazz.equals(Order.class)) {
+ return (BaseObjectManager<T>) orderManager;
}
return null;
}
diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java
index 350af6bd7..11100f66e 100644
--- a/src/main/java/org/traccar/MainModule.java
+++ b/src/main/java/org/traccar/MainModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2021 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.
@@ -23,6 +23,7 @@ import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.database.AttributesManager;
import org.traccar.database.CalendarManager;
+import org.traccar.database.ConnectionManager;
import org.traccar.database.DataManager;
import org.traccar.database.DeviceManager;
import org.traccar.database.GeofenceManager;
@@ -40,6 +41,7 @@ import org.traccar.geocoder.GisgraphyGeocoder;
import org.traccar.geocoder.GoogleGeocoder;
import org.traccar.geocoder.HereGeocoder;
import org.traccar.geocoder.MapQuestGeocoder;
+import org.traccar.geocoder.MapTilerGeocoder;
import org.traccar.geocoder.MapmyIndiaGeocoder;
import org.traccar.geocoder.NominatimGeocoder;
import org.traccar.geocoder.OpenCageGeocoder;
@@ -65,6 +67,7 @@ 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.BehaviorEventHandler;
import org.traccar.handler.events.CommandResultEventHandler;
import org.traccar.handler.events.DriverEventHandler;
import org.traccar.handler.events.FuelDropEventHandler;
@@ -104,6 +107,11 @@ public class MainModule extends AbstractModule {
}
@Provides
+ public static ConnectionManager provideConnectionManager() {
+ return Context.getConnectionManager();
+ }
+
+ @Provides
public static Client provideClient() {
return Context.getClient();
}
@@ -187,6 +195,8 @@ public class MainModule extends AbstractModule {
return new PositionStackGeocoder(key, cacheSize, addressFormat);
case "mapbox":
return new MapboxGeocoder(key, cacheSize, addressFormat);
+ case "maptiler":
+ return new MapTilerGeocoder(key, cacheSize, addressFormat);
default:
return new GoogleGeocoder(key, language, cacheSize, addressFormat);
}
@@ -369,6 +379,12 @@ public class MainModule extends AbstractModule {
@Singleton
@Provides
+ public static BehaviorEventHandler provideBehaviorEventHandler(Config config, IdentityManager identityManager) {
+ return new BehaviorEventHandler(config, identityManager);
+ }
+
+ @Singleton
+ @Provides
public static FuelDropEventHandler provideFuelDropEventHandler(IdentityManager identityManager) {
return new FuelDropEventHandler(identityManager);
}
@@ -383,8 +399,9 @@ public class MainModule extends AbstractModule {
@Singleton
@Provides
public static GeofenceEventHandler provideGeofenceEventHandler(
- IdentityManager identityManager, GeofenceManager geofenceManager, CalendarManager calendarManager) {
- return new GeofenceEventHandler(identityManager, geofenceManager, calendarManager);
+ IdentityManager identityManager, GeofenceManager geofenceManager, CalendarManager calendarManager,
+ ConnectionManager connectionManager) {
+ return new GeofenceEventHandler(identityManager, geofenceManager, calendarManager, connectionManager);
}
@Singleton
diff --git a/src/main/java/org/traccar/api/AsyncSocket.java b/src/main/java/org/traccar/api/AsyncSocket.java
index b2ff5031a..b1853822d 100644
--- a/src/main/java/org/traccar/api/AsyncSocket.java
+++ b/src/main/java/org/traccar/api/AsyncSocket.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -64,6 +64,11 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
}
@Override
+ public void onKeepalive() {
+ sendData(new HashMap<>());
+ }
+
+ @Override
public void onUpdateDevice(Device device) {
Map<String, Collection<?>> data = new HashMap<>();
data.put(KEY_DEVICES, Collections.singletonList(device));
@@ -85,7 +90,7 @@ public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.U
}
private void sendData(Map<String, Collection<?>> data) {
- if (!data.isEmpty() && isConnected()) {
+ if (isConnected()) {
try {
getRemote().sendString(Context.getObjectMapper().writeValueAsString(data), null);
} catch (JsonProcessingException e) {
diff --git a/src/main/java/org/traccar/api/resource/EventResource.java b/src/main/java/org/traccar/api/resource/EventResource.java
index e0ccf7020..34e4a94ce 100644
--- a/src/main/java/org/traccar/api/resource/EventResource.java
+++ b/src/main/java/org/traccar/api/resource/EventResource.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package org.traccar.api.resource;
import java.sql.SQLException;
@@ -7,7 +22,9 @@ import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
import org.traccar.Context;
import org.traccar.api.BaseResource;
@@ -25,6 +42,9 @@ public class EventResource extends BaseResource {
@GET
public Event get(@PathParam("id") long id) throws SQLException {
Event event = Context.getDataManager().getObject(Event.class, id);
+ if (event == null) {
+ throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
+ }
Context.getPermissionsManager().checkDevice(getUserId(), event.getDeviceId());
if (event.getGeofenceId() != 0) {
Context.getPermissionsManager().checkPermission(Geofence.class, getUserId(), event.getGeofenceId());
diff --git a/src/main/java/org/traccar/api/resource/OrderResource.java b/src/main/java/org/traccar/api/resource/OrderResource.java
new file mode 100644
index 000000000..77608a508
--- /dev/null
+++ b/src/main/java/org/traccar/api/resource/OrderResource.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api.resource;
+
+import org.traccar.api.SimpleObjectResource;
+import org.traccar.model.Order;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+@Path("orders")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class OrderResource extends SimpleObjectResource<Order> {
+
+ public OrderResource() {
+ super(Order.class);
+ }
+
+}
diff --git a/src/main/java/org/traccar/api/resource/PasswordResource.java b/src/main/java/org/traccar/api/resource/PasswordResource.java
index 20e8d768d..1868a6191 100644
--- a/src/main/java/org/traccar/api/resource/PasswordResource.java
+++ b/src/main/java/org/traccar/api/resource/PasswordResource.java
@@ -19,7 +19,7 @@ import org.apache.velocity.VelocityContext;
import org.traccar.Context;
import org.traccar.api.BaseResource;
import org.traccar.model.User;
-import org.traccar.notification.FullMessage;
+import org.traccar.notification.NotificationMessage;
import org.traccar.notification.TextTemplateFormatter;
import javax.annotation.security.PermitAll;
@@ -53,8 +53,9 @@ public class PasswordResource extends BaseResource {
Context.getUsersManager().updateItem(user);
VelocityContext velocityContext = TextTemplateFormatter.prepareContext(null);
velocityContext.put("token", token);
- FullMessage message = TextTemplateFormatter.formatFullMessage(velocityContext, "passwordReset");
- Context.getMailManager().sendMessage(userId, message.getSubject(), message.getBody());
+ NotificationMessage fullMessage =
+ TextTemplateFormatter.formatMessage(velocityContext, "passwordReset", "full");
+ Context.getMailManager().sendMessage(userId, fullMessage.getSubject(), fullMessage.getBody());
break;
}
}
diff --git a/src/main/java/org/traccar/api/resource/PermissionsResource.java b/src/main/java/org/traccar/api/resource/PermissionsResource.java
index b89d9d376..54d3964b6 100644
--- a/src/main/java/org/traccar/api/resource/PermissionsResource.java
+++ b/src/main/java/org/traccar/api/resource/PermissionsResource.java
@@ -17,13 +17,17 @@
package org.traccar.api.resource;
import java.sql.SQLException;
+import java.util.Collections;
import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Set;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
+import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -55,30 +59,62 @@ public class PermissionsResource extends BaseResource {
permission.getPropertyClass(), getUserId(), permission.getPropertyId());
}
+ private void checkPermissionTypes(List<LinkedHashMap<String, Long>> entities) {
+ Set<String> keys = null;
+ for (LinkedHashMap<String, Long> entity: entities) {
+ if (keys != null & !entity.keySet().equals(keys)) {
+ throw new WebApplicationException(Response.status(Response.Status.BAD_REQUEST).build());
+ }
+ keys = entity.keySet();
+ }
+ }
+
+ @Path("bulk")
@POST
- public Response add(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException {
+ public Response add(List<LinkedHashMap<String, Long>> entities) throws SQLException, ClassNotFoundException {
Context.getPermissionsManager().checkReadonly(getUserId());
- Permission permission = new Permission(entity);
- checkPermission(permission, true);
- Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
- permission.getPropertyClass(), permission.getPropertyId(), true);
- LogAction.link(getUserId(), permission.getOwnerClass(), permission.getOwnerId(),
- permission.getPropertyClass(), permission.getPropertyId());
- Context.getPermissionsManager().refreshPermissions(permission);
+ checkPermissionTypes(entities);
+ for (LinkedHashMap<String, Long> entity: entities) {
+ Permission permission = new Permission(entity);
+ checkPermission(permission, true);
+ Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
+ permission.getPropertyClass(), permission.getPropertyId(), true);
+ LogAction.link(getUserId(), permission.getOwnerClass(), permission.getOwnerId(),
+ permission.getPropertyClass(), permission.getPropertyId());
+ }
+ if (!entities.isEmpty()) {
+ Context.getPermissionsManager().refreshPermissions(new Permission(entities.get(0)));
+ }
return Response.noContent().build();
}
+ @POST
+ public Response add(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException {
+ return add(Collections.singletonList(entity));
+ }
+
@DELETE
- public Response remove(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException {
+ @Path("bulk")
+ public Response remove(List<LinkedHashMap<String, Long>> entities) throws SQLException, ClassNotFoundException {
Context.getPermissionsManager().checkReadonly(getUserId());
- Permission permission = new Permission(entity);
- checkPermission(permission, false);
- Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
- permission.getPropertyClass(), permission.getPropertyId(), false);
- LogAction.unlink(getUserId(), permission.getOwnerClass(), permission.getOwnerId(),
- permission.getPropertyClass(), permission.getPropertyId());
- Context.getPermissionsManager().refreshPermissions(permission);
+ checkPermissionTypes(entities);
+ for (LinkedHashMap<String, Long> entity: entities) {
+ Permission permission = new Permission(entity);
+ checkPermission(permission, false);
+ Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
+ permission.getPropertyClass(), permission.getPropertyId(), false);
+ LogAction.unlink(getUserId(), permission.getOwnerClass(), permission.getOwnerId(),
+ permission.getPropertyClass(), permission.getPropertyId());
+ }
+ if (!entities.isEmpty()) {
+ Context.getPermissionsManager().refreshPermissions(new Permission(entities.get(0)));
+ }
return Response.noContent().build();
}
+ @DELETE
+ public Response remove(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException {
+ return remove(Collections.singletonList(entity));
+ }
+
}
diff --git a/src/main/java/org/traccar/api/resource/SessionResource.java b/src/main/java/org/traccar/api/resource/SessionResource.java
index e3c5d457f..60ce5490a 100644
--- a/src/main/java/org/traccar/api/resource/SessionResource.java
+++ b/src/main/java/org/traccar/api/resource/SessionResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -57,8 +57,19 @@ public class SessionResource extends BaseResource {
@PermitAll
@GET
public User get(@QueryParam("token") String token) throws SQLException, UnsupportedEncodingException {
+
+ if (token != null) {
+ User user = Context.getUsersManager().getUserByToken(token);
+ if (user != null) {
+ Context.getPermissionsManager().checkUserEnabled(user.getId());
+ request.getSession().setAttribute(USER_ID_KEY, user.getId());
+ return user;
+ }
+ }
+
Long userId = (Long) request.getSession().getAttribute(USER_ID_KEY);
if (userId == null) {
+
Cookie[] cookies = request.getCookies();
String email = null, password = null;
if (cookies != null) {
@@ -77,24 +88,20 @@ public class SessionResource extends BaseResource {
if (email != null && password != null) {
User user = Context.getPermissionsManager().login(email, password);
if (user != null) {
- userId = user.getId();
- request.getSession().setAttribute(USER_ID_KEY, userId);
- }
- } else if (token != null) {
- User user = Context.getUsersManager().getUserByToken(token);
- if (user != null) {
- userId = user.getId();
- request.getSession().setAttribute(USER_ID_KEY, userId);
+ Context.getPermissionsManager().checkUserEnabled(user.getId());
+ request.getSession().setAttribute(USER_ID_KEY, user.getId());
+ return user;
}
}
- }
- if (userId != null) {
+ } else {
+
Context.getPermissionsManager().checkUserEnabled(userId);
return Context.getPermissionsManager().getUser(userId);
- } else {
- throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
+
}
+
+ throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
}
@PermitAll
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java
index a77204175..e8e0ff207 100644
--- a/src/main/java/org/traccar/config/Keys.java
+++ b/src/main/java/org/traccar/config/Keys.java
@@ -212,13 +212,27 @@ public final class Keys {
Collections.singletonList(KeyType.GLOBAL));
/**
- * Relevant only for geofence speed limits. Use lowest speed limits from all geofences.
+ * Relevant only for geofence speed limits. Use the lowest speed limit from all geofences.
*/
public static final ConfigKey<Boolean> EVENT_OVERSPEED_PREFER_LOWEST = new ConfigKey<>(
"event.overspeed.preferLowest",
Collections.singletonList(KeyType.GLOBAL));
/**
+ * Driver behavior acceleration threshold. Value is in meter per second squared.
+ */
+ public static final ConfigKey<Double> EVENT_BEHAVIOR_ACCELERATION_THRESHOLD = new ConfigKey<>(
+ "event.behavior.accelerationThreshold",
+ Collections.singletonList(KeyType.GLOBAL));
+
+ /**
+ * Driver behavior braking threshold. Value is in meter per second squared.
+ */
+ public static final ConfigKey<Double> EVENT_BEHAVIOR_BRAKING_THRESHOLD = new ConfigKey<>(
+ "event.behavior.brakingThreshold",
+ Collections.singletonList(KeyType.GLOBAL));
+
+ /**
* Do not generate alert event if same alert was present in last known location.
*/
public static final ConfigKey<Boolean> EVENT_IGNORE_DUPLICATE_ALERTS = new ConfigKey<>(
@@ -759,6 +773,13 @@ public final class Keys {
Collections.singletonList(KeyType.GLOBAL));
/**
+ * Telegram notification send location message.
+ */
+ public static final ConfigKey<Boolean> NOTIFICATOR_TELEGRAM_SEND_LOCATION = new ConfigKey<>(
+ "notificator.telegram.sendLocation",
+ Collections.singletonList(KeyType.GLOBAL));
+
+ /**
* Maximum time period for reports in seconds. Can be useful to prevent users to request unreasonably long reports.
* By default there is no limit.
*/
@@ -1208,6 +1229,13 @@ public final class Keys {
Collections.singletonList(KeyType.GLOBAL));
/**
+ * Enables persisting Jetty session to the database
+ */
+ public static final ConfigKey<Boolean> WEB_PERSIST_SESSION = new ConfigKey<>(
+ "web.persistSession",
+ Collections.singletonList(KeyType.GLOBAL));
+
+ /**
* Output logging to the standard terminal output instead of a log file.
*/
public static final ConfigKey<Boolean> LOGGER_CONSOLE = new ConfigKey<>(
diff --git a/src/main/java/org/traccar/database/ConnectionManager.java b/src/main/java/org/traccar/database/ConnectionManager.java
index 5ff27c187..e12d44612 100644
--- a/src/main/java/org/traccar/database/ConnectionManager.java
+++ b/src/main/java/org/traccar/database/ConnectionManager.java
@@ -154,6 +154,14 @@ public class ConnectionManager {
return result;
}
+ public synchronized void sendKeepalive() {
+ for (Set<UpdateListener> userListeners : listeners.values()) {
+ for (UpdateListener listener : userListeners) {
+ listener.onKeepalive();
+ }
+ }
+ }
+
public synchronized void updateDevice(Device device) {
for (long userId : Context.getPermissionsManager().getDeviceUsers(device.getId())) {
if (listeners.containsKey(userId)) {
@@ -185,6 +193,7 @@ public class ConnectionManager {
}
public interface UpdateListener {
+ void onKeepalive();
void onUpdateDevice(Device device);
void onUpdatePosition(Position position);
void onUpdateEvent(Event event);
diff --git a/src/main/java/org/traccar/database/DataManager.java b/src/main/java/org/traccar/database/DataManager.java
index 75ec70ea7..ebd0dcade 100644
--- a/src/main/java/org/traccar/database/DataManager.java
+++ b/src/main/java/org/traccar/database/DataManager.java
@@ -41,6 +41,7 @@ import org.traccar.model.Group;
import org.traccar.model.Maintenance;
import org.traccar.model.ManagedUser;
import org.traccar.model.Notification;
+import org.traccar.model.Order;
import org.traccar.model.Permission;
import org.traccar.model.Position;
import org.traccar.model.Server;
@@ -73,6 +74,10 @@ public class DataManager {
private DataSource dataSource;
+ public DataSource getDataSource() {
+ return dataSource;
+ }
+
private boolean generateQueries;
private final boolean forceLdap;
@@ -391,6 +396,8 @@ public class DataManager {
return Maintenance.class;
case "notification":
return Notification.class;
+ case "order":
+ return Order.class;
default:
throw new ClassNotFoundException();
}
diff --git a/src/main/java/org/traccar/database/OrderManager.java b/src/main/java/org/traccar/database/OrderManager.java
new file mode 100644
index 000000000..c3253e52f
--- /dev/null
+++ b/src/main/java/org/traccar/database/OrderManager.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2021 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.database;
+
+import org.traccar.model.Order;
+
+public class OrderManager extends ExtendedObjectManager<Order> {
+
+ public OrderManager(DataManager dataManager) {
+ super(dataManager, Order.class);
+ }
+
+}
diff --git a/src/main/java/org/traccar/database/PermissionsManager.java b/src/main/java/org/traccar/database/PermissionsManager.java
index a27eac069..32464cf90 100644
--- a/src/main/java/org/traccar/database/PermissionsManager.java
+++ b/src/main/java/org/traccar/database/PermissionsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -29,6 +29,7 @@ import org.traccar.model.Group;
import org.traccar.model.Maintenance;
import org.traccar.model.ManagedUser;
import org.traccar.model.Notification;
+import org.traccar.model.Order;
import org.traccar.model.Permission;
import org.traccar.model.Server;
import org.traccar.model.User;
@@ -395,6 +396,8 @@ public class PermissionsManager {
manager = Context.getMaintenancesManager();
} else if (object.equals(Notification.class)) {
manager = Context.getNotificationManager();
+ } else if (object.equals(Order.class)) {
+ manager = Context.getOrderManager();
} else {
throw new IllegalArgumentException("Unknown object type");
}
@@ -454,6 +457,8 @@ public class PermissionsManager {
Context.getCommandsManager().refreshUserItems();
} else if (permission.getPropertyClass().equals(Maintenance.class)) {
Context.getMaintenancesManager().refreshUserItems();
+ } else if (permission.getPropertyClass().equals(Order.class)) {
+ Context.getOrderManager().refreshUserItems();
} else if (permission.getPropertyClass().equals(Notification.class)
&& Context.getNotificationManager() != null) {
Context.getNotificationManager().refreshUserItems();
@@ -469,6 +474,8 @@ public class PermissionsManager {
Context.getCommandsManager().refreshExtendedPermissions();
} else if (permission.getPropertyClass().equals(Maintenance.class)) {
Context.getMaintenancesManager().refreshExtendedPermissions();
+ } else if (permission.getPropertyClass().equals(Order.class)) {
+ Context.getOrderManager().refreshExtendedPermissions();
} else if (permission.getPropertyClass().equals(Notification.class)
&& Context.getNotificationManager() != null) {
Context.getNotificationManager().refreshExtendedPermissions();
diff --git a/src/main/java/org/traccar/geocoder/HereGeocoder.java b/src/main/java/org/traccar/geocoder/HereGeocoder.java
index aaf11d74d..40390e65b 100644
--- a/src/main/java/org/traccar/geocoder/HereGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/HereGeocoder.java
@@ -53,8 +53,8 @@ public class HereGeocoder extends JsonGeocoder {
if (result != null) {
Address address = new Address();
- if (json.containsKey("Label")) {
- address.setFormattedAddress(json.getString("Label"));
+ if (result.containsKey("Label")) {
+ address.setFormattedAddress(result.getString("Label"));
}
if (result.containsKey("HouseNumber")) {
diff --git a/src/main/java/org/traccar/geocoder/JsonGeocoder.java b/src/main/java/org/traccar/geocoder/JsonGeocoder.java
index 4f34fb973..f20aa79d6 100644
--- a/src/main/java/org/traccar/geocoder/JsonGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/JsonGeocoder.java
@@ -22,7 +22,7 @@ import org.traccar.Main;
import org.traccar.database.StatisticsManager;
import javax.json.JsonObject;
-import javax.ws.rs.ClientErrorException;
+import javax.ws.rs.WebApplicationException;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.InvocationCallback;
import java.util.AbstractMap;
@@ -97,7 +97,9 @@ public abstract class JsonGeocoder implements Geocoder {
}
}
- Main.getInjector().getInstance(StatisticsManager.class).registerGeocoderRequest();
+ if (Main.getInjector() != null) {
+ Main.getInjector().getInstance(StatisticsManager.class).registerGeocoderRequest();
+ }
Invocation.Builder request = Context.getClient().target(String.format(url, latitude, longitude)).request();
@@ -116,7 +118,7 @@ public abstract class JsonGeocoder implements Geocoder {
} else {
try {
return handleResponse(latitude, longitude, request.get(JsonObject.class), null);
- } catch (ClientErrorException e) {
+ } catch (WebApplicationException e) {
LOGGER.warn("Geocoder network error", e);
}
}
diff --git a/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java b/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java
new file mode 100644
index 000000000..6b688a6e8
--- /dev/null
+++ b/src/main/java/org/traccar/geocoder/MapTilerGeocoder.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2021 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.geocoder;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+
+public class MapTilerGeocoder extends JsonGeocoder {
+
+ public MapTilerGeocoder(String key, int cacheSize, AddressFormat addressFormat) {
+ super("https://api.maptiler.com/geocoding/%2$f,%1$f.json?key=" + key, cacheSize, addressFormat);
+ }
+
+ @Override
+ public Address parseAddress(JsonObject json) {
+ JsonArray features = json.getJsonArray("features");
+
+ if (!features.isEmpty()) {
+ Address address = new Address();
+
+ for (int i = 0; i < features.size(); i++) {
+ JsonObject feature = features.getJsonObject(i);
+ String type = feature.getJsonArray("place_type").getString(0);
+ String value = feature.getString("text");
+ switch (type) {
+ case "street":
+ address.setStreet(value);
+ break;
+ case "city":
+ address.setSettlement(value);
+ break;
+ case "county":
+ address.setDistrict(value);
+ break;
+ case "state":
+ address.setState(value);
+ break;
+ case "country":
+ address.setCountry(value);
+ break;
+ default:
+ break;
+ }
+ if (address.getFormattedAddress() == null) {
+ address.setFormattedAddress(feature.getString("place_name"));
+ }
+ }
+
+ return address;
+ }
+
+ return null;
+ }
+
+ @Override
+ protected String parseError(JsonObject json) {
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
index cb3094e16..2535970d3 100644
--- a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
@@ -49,7 +49,15 @@ public class OpenCellIdGeolocationProvider implements GeolocationProvider {
json.getJsonNumber("lat").doubleValue(),
json.getJsonNumber("lon").doubleValue(), 0);
} else {
- callback.onFailure(new GeolocationException("Coordinates are missing"));
+ if (json.containsKey("error")) {
+ String errorMessage = json.getString("error");
+ if (json.containsKey("code")) {
+ errorMessage += " (" + json.getInt("code") + ")";
+ }
+ callback.onFailure(new GeolocationException(errorMessage));
+ } else {
+ callback.onFailure(new GeolocationException("Coordinates are missing"));
+ }
}
}
diff --git a/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java
index f71620d8a..33cd84a47 100644
--- a/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/UniversalGeolocationProvider.java
@@ -25,7 +25,7 @@ import javax.ws.rs.client.InvocationCallback;
public class UniversalGeolocationProvider implements GeolocationProvider {
- private String url;
+ private final String url;
public UniversalGeolocationProvider(String url, String key) {
this.url = url + "?key=" + key;
diff --git a/src/main/java/org/traccar/handler/DistanceHandler.java b/src/main/java/org/traccar/handler/DistanceHandler.java
index a336a884e..1e7e444f6 100644
--- a/src/main/java/org/traccar/handler/DistanceHandler.java
+++ b/src/main/java/org/traccar/handler/DistanceHandler.java
@@ -1,6 +1,6 @@
/*
* Copyright 2015 Amila Silva
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2021 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.
@@ -61,11 +61,11 @@ public class DistanceHandler extends BaseDataHandler {
last.getLatitude(), last.getLongitude());
distance = BigDecimal.valueOf(distance).setScale(2, RoundingMode.HALF_EVEN).doubleValue();
}
- if (filter && last.getValid() && last.getLatitude() != 0 && last.getLongitude() != 0) {
+ if (filter && last.getLatitude() != 0 && last.getLongitude() != 0) {
boolean satisfiesMin = coordinatesMinError == 0 || distance > coordinatesMinError;
- boolean satisfiesMax = coordinatesMaxError == 0
- || distance < coordinatesMaxError || position.getValid();
+ boolean satisfiesMax = coordinatesMaxError == 0 || distance < coordinatesMaxError;
if (!satisfiesMin || !satisfiesMax) {
+ position.setValid(last.getValid());
position.setLatitude(last.getLatitude());
position.setLongitude(last.getLongitude());
distance = 0;
diff --git a/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java
new file mode 100644
index 000000000..767cef3f6
--- /dev/null
+++ b/src/main/java/org/traccar/handler/events/BehaviorEventHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2021 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.events;
+
+import io.netty.channel.ChannelHandler;
+import org.traccar.config.Config;
+import org.traccar.config.Keys;
+import org.traccar.database.IdentityManager;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+import java.util.Collections;
+import java.util.Map;
+
+@ChannelHandler.Sharable
+public class BehaviorEventHandler extends BaseEventHandler {
+
+ private final double accelerationThreshold;
+ private final double brakingThreshold;
+
+ private final IdentityManager identityManager;
+
+ public BehaviorEventHandler(Config config, IdentityManager identityManager) {
+ accelerationThreshold = config.getDouble(Keys.EVENT_BEHAVIOR_ACCELERATION_THRESHOLD);
+ brakingThreshold = config.getDouble(Keys.EVENT_BEHAVIOR_BRAKING_THRESHOLD);
+ this.identityManager = identityManager;
+ }
+
+ @Override
+ protected Map<Event, Position> analyzePosition(Position position) {
+
+ Position lastPosition = identityManager.getLastPosition(position.getDeviceId());
+ if (lastPosition != null && position.getFixTime().equals(lastPosition.getFixTime())) {
+ double acceleration = UnitsConverter.mpsFromKnots(position.getSpeed() - lastPosition.getSpeed()) * 1000
+ / (position.getFixTime().getTime() - lastPosition.getFixTime().getTime());
+ if (accelerationThreshold != 0 && acceleration >= accelerationThreshold) {
+ Event event = new Event(Event.TYPE_ALARM, position);
+ event.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ return Collections.singletonMap(event, position);
+ } else if (brakingThreshold != 0 && acceleration <= -brakingThreshold) {
+ Event event = new Event(Event.TYPE_ALARM, position);
+ event.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ return Collections.singletonMap(event, position);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
index f4807e56b..dae0c891f 100644
--- a/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
+++ b/src/main/java/org/traccar/handler/events/GeofenceEventHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import java.util.Map;
import io.netty.channel.ChannelHandler;
import org.traccar.database.CalendarManager;
+import org.traccar.database.ConnectionManager;
import org.traccar.database.GeofenceManager;
import org.traccar.database.IdentityManager;
import org.traccar.model.Calendar;
@@ -35,12 +36,15 @@ public class GeofenceEventHandler extends BaseEventHandler {
private final IdentityManager identityManager;
private final GeofenceManager geofenceManager;
private final CalendarManager calendarManager;
+ private final ConnectionManager connectionManager;
public GeofenceEventHandler(
- IdentityManager identityManager, GeofenceManager geofenceManager, CalendarManager calendarManager) {
+ IdentityManager identityManager, GeofenceManager geofenceManager, CalendarManager calendarManager,
+ ConnectionManager connectionManager) {
this.identityManager = identityManager;
this.geofenceManager = geofenceManager;
this.calendarManager = calendarManager;
+ this.connectionManager = connectionManager;
}
@Override
@@ -63,6 +67,9 @@ public class GeofenceEventHandler extends BaseEventHandler {
oldGeofences.removeAll(currentGeofences);
device.setGeofenceIds(currentGeofences);
+ if (!oldGeofences.isEmpty() || !newGeofences.isEmpty()) {
+ connectionManager.updateDevice(device);
+ }
Map<Event, Position> events = new HashMap<>();
for (long geofenceId : oldGeofences) {
diff --git a/src/main/java/org/traccar/helper/BufferUtil.java b/src/main/java/org/traccar/helper/BufferUtil.java
index 15c619ec5..bbf12d738 100644
--- a/src/main/java/org/traccar/helper/BufferUtil.java
+++ b/src/main/java/org/traccar/helper/BufferUtil.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2021 Anton Tananaev (anton@traccar.org)
* Copyright 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,20 +28,29 @@ public final class BufferUtil {
}
public static int indexOf(String needle, ByteBuf haystack) {
- ByteBuf needleBuffer = Unpooled.wrappedBuffer(needle.getBytes(StandardCharsets.US_ASCII));
+ return indexOf(needle, haystack, haystack.readerIndex(), haystack.writerIndex());
+ }
+
+ public static int indexOf(String needle, ByteBuf haystack, int startIndex, int endIndex) {
+ ByteBuf wrappedNeedle = Unpooled.wrappedBuffer(needle.getBytes(StandardCharsets.US_ASCII));
try {
- return ByteBufUtil.indexOf(needleBuffer, haystack);
+ return indexOf(wrappedNeedle, haystack, startIndex, endIndex);
} finally {
- needleBuffer.release();
+ wrappedNeedle.release();
}
}
- public static int indexOf(String needle, ByteBuf haystack, int startIndex, int endIndex) {
- ByteBuf wrappedHaystack = Unpooled.wrappedBuffer(haystack);
- wrappedHaystack.readerIndex(startIndex - haystack.readerIndex());
- wrappedHaystack.writerIndex(endIndex - haystack.readerIndex());
- int result = indexOf(needle, wrappedHaystack);
- return result < 0 ? result : haystack.readerIndex() + result;
+ public static int indexOf(ByteBuf needle, ByteBuf haystack, int startIndex, int endIndex) {
+ ByteBuf wrappedHaystack;
+ if (startIndex == haystack.readerIndex() && endIndex == haystack.writerIndex()) {
+ wrappedHaystack = haystack;
+ } else {
+ wrappedHaystack = Unpooled.wrappedBuffer(haystack);
+ wrappedHaystack.readerIndex(startIndex - haystack.readerIndex());
+ wrappedHaystack.writerIndex(endIndex - haystack.readerIndex());
+ }
+ int result = ByteBufUtil.indexOf(needle, wrappedHaystack);
+ return result < 0 ? result : haystack.readerIndex() + startIndex + result;
}
}
diff --git a/src/main/java/org/traccar/model/Order.java b/src/main/java/org/traccar/model/Order.java
new file mode 100644
index 000000000..fe6d926b8
--- /dev/null
+++ b/src/main/java/org/traccar/model/Order.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.model;
+
+public class Order extends ExtendedModel {
+
+ private String uniqueId;
+
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ private String description;
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ private String fromAddress;
+
+ public String getFromAddress() {
+ return fromAddress;
+ }
+
+ public void setFromAddress(String fromAddress) {
+ this.fromAddress = fromAddress;
+ }
+
+ private String toAddress;
+
+ public String getToAddress() {
+ return toAddress;
+ }
+
+ public void setToAddress(String toAddress) {
+ this.toAddress = toAddress;
+ }
+
+}
diff --git a/src/main/java/org/traccar/model/Position.java b/src/main/java/org/traccar/model/Position.java
index 6f70c8e21..09d25e832 100644
--- a/src/main/java/org/traccar/model/Position.java
+++ b/src/main/java/org/traccar/model/Position.java
@@ -136,7 +136,6 @@ public class Position extends Message {
public static final String ALARM_JAMMING = "jamming";
public static final String ALARM_TEMPERATURE = "temperature";
public static final String ALARM_PARKING = "parking";
- public static final String ALARM_SHOCK = "shock";
public static final String ALARM_BONNET = "bonnet";
public static final String ALARM_FOOT_BRAKE = "footBrake";
public static final String ALARM_FUEL_LEAK = "fuelLeak";
diff --git a/src/main/java/org/traccar/notification/NotificationFormatter.java b/src/main/java/org/traccar/notification/NotificationFormatter.java
index dabc75b8b..9a6723a71 100644
--- a/src/main/java/org/traccar/notification/NotificationFormatter.java
+++ b/src/main/java/org/traccar/notification/NotificationFormatter.java
@@ -58,14 +58,9 @@ public final class NotificationFormatter {
return velocityContext;
}
- public static FullMessage formatFullMessage(long userId, Event event, Position position) {
+ public static NotificationMessage formatMessage(long userId, Event event, Position position, String templatePath) {
VelocityContext velocityContext = prepareContext(userId, event, position);
- return TextTemplateFormatter.formatFullMessage(velocityContext, event.getType());
- }
-
- public static String formatShortMessage(long userId, Event event, Position position) {
- VelocityContext velocityContext = prepareContext(userId, event, position);
- return TextTemplateFormatter.formatShortMessage(velocityContext, event.getType());
+ return TextTemplateFormatter.formatMessage(velocityContext, event.getType(), templatePath);
}
}
diff --git a/src/main/java/org/traccar/notification/FullMessage.java b/src/main/java/org/traccar/notification/NotificationMessage.java
index f66537c6e..0fb8d7654 100644
--- a/src/main/java/org/traccar/notification/FullMessage.java
+++ b/src/main/java/org/traccar/notification/NotificationMessage.java
@@ -16,12 +16,12 @@
*/
package org.traccar.notification;
-public class FullMessage {
+public class NotificationMessage {
private String subject;
private String body;
- public FullMessage(String subject, String body) {
+ public NotificationMessage(String subject, String body) {
this.subject = subject;
this.body = body;
}
diff --git a/src/main/java/org/traccar/notification/TextTemplateFormatter.java b/src/main/java/org/traccar/notification/TextTemplateFormatter.java
index c7cac2d4d..b7058c824 100644
--- a/src/main/java/org/traccar/notification/TextTemplateFormatter.java
+++ b/src/main/java/org/traccar/notification/TextTemplateFormatter.java
@@ -71,21 +71,10 @@ public final class TextTemplateFormatter {
return template;
}
- public static FullMessage formatFullMessage(VelocityContext velocityContext, String name) {
- String formattedMessage = formatMessage(velocityContext, name, "full");
- return new FullMessage((String) velocityContext.get("subject"), formattedMessage);
- }
-
- public static String formatShortMessage(VelocityContext velocityContext, String name) {
- return formatMessage(velocityContext, name, "short");
- }
-
- private static String formatMessage(
- VelocityContext velocityContext, String name, String templatePath) {
-
+ public static NotificationMessage formatMessage(VelocityContext velocityContext, String name, String templatePath) {
StringWriter writer = new StringWriter();
getTemplate(name, templatePath).merge(velocityContext, writer);
- return writer.toString();
+ return new NotificationMessage((String) velocityContext.get("subject"), writer.toString());
}
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorFirebase.java b/src/main/java/org/traccar/notificators/NotificatorFirebase.java
index 78d5da1e2..f91ec25a0 100644
--- a/src/main/java/org/traccar/notificators/NotificatorFirebase.java
+++ b/src/main/java/org/traccar/notificators/NotificatorFirebase.java
@@ -24,6 +24,7 @@ import org.traccar.config.Keys;
import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.model.User;
+import org.traccar.notification.NotificationMessage;
import org.traccar.notification.NotificationFormatter;
import javax.ws.rs.client.Entity;
@@ -37,6 +38,8 @@ public class NotificatorFirebase extends Notificator {
private final String key;
public static class Notification {
+ @JsonProperty("title")
+ private String title;
@JsonProperty("body")
private String body;
@JsonProperty("sound")
@@ -66,8 +69,11 @@ public class NotificatorFirebase extends Notificator {
final User user = Context.getPermissionsManager().getUser(userId);
if (user.getAttributes().containsKey("notificationTokens")) {
+ NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short");
+
Notification notification = new Notification();
- notification.body = NotificationFormatter.formatShortMessage(userId, event, position).trim();
+ notification.title = shortMessage.getSubject();
+ notification.body = shortMessage.getBody();
notification.sound = "default";
Message message = new Message();
diff --git a/src/main/java/org/traccar/notificators/NotificatorMail.java b/src/main/java/org/traccar/notificators/NotificatorMail.java
index 6b9774c58..9b5637ed8 100644
--- a/src/main/java/org/traccar/notificators/NotificatorMail.java
+++ b/src/main/java/org/traccar/notificators/NotificatorMail.java
@@ -19,7 +19,7 @@ package org.traccar.notificators;
import org.traccar.Context;
import org.traccar.model.Event;
import org.traccar.model.Position;
-import org.traccar.notification.FullMessage;
+import org.traccar.notification.NotificationMessage;
import org.traccar.notification.MessageException;
import org.traccar.notification.NotificationFormatter;
@@ -30,8 +30,8 @@ public final class NotificatorMail extends Notificator {
@Override
public void sendSync(long userId, Event event, Position position) throws MessageException {
try {
- FullMessage message = NotificationFormatter.formatFullMessage(userId, event, position);
- Context.getMailManager().sendMessage(userId, message.getSubject(), message.getBody());
+ NotificationMessage fullMessage = NotificationFormatter.formatMessage(userId, event, position, "full");
+ Context.getMailManager().sendMessage(userId, fullMessage.getSubject(), fullMessage.getBody());
} catch (MessagingException e) {
throw new MessageException(e);
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorPushover.java b/src/main/java/org/traccar/notificators/NotificatorPushover.java
index 189af7834..456c2fe4f 100644
--- a/src/main/java/org/traccar/notificators/NotificatorPushover.java
+++ b/src/main/java/org/traccar/notificators/NotificatorPushover.java
@@ -24,6 +24,7 @@ import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.model.User;
import org.traccar.notification.NotificationFormatter;
+import org.traccar.notification.NotificationMessage;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.InvocationCallback;
@@ -43,6 +44,8 @@ public class NotificatorPushover extends Notificator {
private String user;
@JsonProperty("device")
private String device;
+ @JsonProperty("title")
+ private String title;
@JsonProperty("message")
private String message;
}
@@ -74,11 +77,14 @@ public class NotificatorPushover extends Notificator {
return;
}
+ NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short");
+
Message message = new Message();
message.token = token;
message.user = this.user;
message.device = device;
- message.message = NotificationFormatter.formatShortMessage(userId, event, position);
+ message.title = shortMessage.getSubject();
+ message.message = shortMessage.getBody();
Context.getClient().target(url).request()
.async().post(Entity.json(message), new InvocationCallback<Object>() {
diff --git a/src/main/java/org/traccar/notificators/NotificatorSms.java b/src/main/java/org/traccar/notificators/NotificatorSms.java
index 8124e40b1..fb817b112 100644
--- a/src/main/java/org/traccar/notificators/NotificatorSms.java
+++ b/src/main/java/org/traccar/notificators/NotificatorSms.java
@@ -24,6 +24,7 @@ import org.traccar.model.Position;
import org.traccar.model.User;
import org.traccar.notification.MessageException;
import org.traccar.notification.NotificationFormatter;
+import org.traccar.notification.NotificationMessage;
public final class NotificatorSms extends Notificator {
@@ -31,9 +32,10 @@ public final class NotificatorSms extends Notificator {
public void sendAsync(long userId, Event event, Position position) {
final User user = Context.getPermissionsManager().getUser(userId);
if (user.getPhone() != null) {
+ NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short");
Main.getInjector().getInstance(StatisticsManager.class).registerSms();
Context.getSmsManager().sendMessageAsync(user.getPhone(),
- NotificationFormatter.formatShortMessage(userId, event, position), false);
+ shortMessage.getBody(), false);
}
}
@@ -41,9 +43,10 @@ public final class NotificatorSms extends Notificator {
public void sendSync(long userId, Event event, Position position) throws MessageException, InterruptedException {
final User user = Context.getPermissionsManager().getUser(userId);
if (user.getPhone() != null) {
+ NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short");
Main.getInjector().getInstance(StatisticsManager.class).registerSms();
Context.getSmsManager().sendMessageSync(user.getPhone(),
- NotificationFormatter.formatShortMessage(userId, event, position), false);
+ shortMessage.getBody(), false);
}
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorTelegram.java b/src/main/java/org/traccar/notificators/NotificatorTelegram.java
index 00baa2540..70148110c 100644
--- a/src/main/java/org/traccar/notificators/NotificatorTelegram.java
+++ b/src/main/java/org/traccar/notificators/NotificatorTelegram.java
@@ -25,6 +25,7 @@ import org.traccar.config.Keys;
import org.traccar.model.Event;
import org.traccar.model.Position;
import org.traccar.notification.NotificationFormatter;
+import org.traccar.notification.NotificationMessage;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.InvocationCallback;
@@ -36,6 +37,7 @@ public class NotificatorTelegram extends Notificator {
private final String urlSendText;
private final String urlSendLocation;
private final String chatId;
+ private final boolean sendLocation;
public static class TextMessage {
@JsonProperty("chat_id")
@@ -67,6 +69,7 @@ public class NotificatorTelegram extends Notificator {
"https://api.telegram.org/bot%s/sendLocation",
Context.getConfig().getString(Keys.NOTIFICATOR_TELEGRAM_KEY));
chatId = Context.getConfig().getString(Keys.NOTIFICATOR_TELEGRAM_CHAT_ID);
+ sendLocation = Context.getConfig().getBoolean(Keys.NOTIFICATOR_TELEGRAM_SEND_LOCATION);
}
private void executeRequest(String url, Object message) {
@@ -96,14 +99,16 @@ public class NotificatorTelegram extends Notificator {
@Override
public void sendSync(long userId, Event event, Position position) {
User user = Context.getPermissionsManager().getUser(userId);
+ NotificationMessage shortMessage = NotificationFormatter.formatMessage(userId, event, position, "short");
+
TextMessage message = new TextMessage();
message.chatId = user.getString("telegramChatId");
if (message.chatId == null) {
message.chatId = chatId;
}
- message.text = NotificationFormatter.formatShortMessage(userId, event, position);
+ message.text = shortMessage.getBody();
executeRequest(urlSendText, message);
- if (position != null) {
+ if (sendLocation && position != null) {
executeRequest(urlSendLocation, createLocationMessage(message.chatId, position));
}
}
diff --git a/src/main/java/org/traccar/protocol/AppletProtocolDecoder.java b/src/main/java/org/traccar/protocol/AppletProtocolDecoder.java
deleted file mode 100644
index 7a995cc24..000000000
--- a/src/main/java/org/traccar/protocol/AppletProtocolDecoder.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2018 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 java.net.SocketAddress;
-
-public class AppletProtocolDecoder extends BaseHttpProtocolDecoder {
-
- public AppletProtocolDecoder(Protocol protocol) {
- super(protocol);
- }
-
- @Override
- protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
-
- FullHttpRequest request = (FullHttpRequest) msg;
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, request.headers().get("From"));
- if (deviceSession != null) {
- sendResponse(channel, HttpResponseStatus.OK);
- } else {
- sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
- }
-
- return null;
- }
-
-}
diff --git a/src/main/java/org/traccar/protocol/B2316Protocol.java b/src/main/java/org/traccar/protocol/B2316Protocol.java
new file mode 100644
index 000000000..7f08870ce
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/B2316Protocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2021 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 B2316Protocol extends BaseProtocol {
+
+ public B2316Protocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new B2316ProtocolDecoder(B2316Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java b/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java
new file mode 100644
index 000000000..854107a20
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2021 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.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class B2316ProtocolDecoder extends BaseProtocolDecoder {
+
+ public B2316ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private String decodeAlarm(int value) {
+ switch (value) {
+ case 1:
+ return Position.ALARM_LOW_BATTERY;
+ case 2:
+ return Position.ALARM_SOS;
+ case 3:
+ return Position.ALARM_POWER_OFF;
+ case 4:
+ return Position.ALARM_REMOVING;
+ default:
+ return null;
+ }
+ }
+
+ private Integer decodeBattery(int value) {
+ switch (value) {
+ case 0:
+ return 10;
+ case 1:
+ return 30;
+ case 2:
+ return 60;
+ case 3:
+ return 80;
+ case 4:
+ return 100;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ JsonObject root = Json.createReader(new StringReader((String) msg)).readObject();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, root.getString("imei"));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+ JsonArray data = root.getJsonArray("data");
+ for (int i = 0; i < data.size(); i++) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ Network network = new Network();
+
+ JsonObject item = data.getJsonObject(i);
+ Date time = new Date(item.getJsonNumber("tm").longValue() * 1000);
+
+ if (item.containsKey("gp")) {
+ String[] coordinates = item.getString("gp").split(",");
+ position.setLongitude(Double.parseDouble(coordinates[0]));
+ position.setLatitude(Double.parseDouble(coordinates[1]));
+ position.setValid(true);
+ position.setTime(time);
+ } else {
+ getLastLocation(position, time);
+ }
+
+ if (item.containsKey("ci")) {
+ String[] cell = item.getString("ci").split(",");
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(cell[0]), Integer.parseInt(cell[1]),
+ Integer.parseInt(cell[2]), Integer.parseInt(cell[3]),
+ Integer.parseInt(cell[4])));
+ }
+
+ if (item.containsKey("wi")) {
+ String[] points = item.getString("wi").split(";");
+ for (String point : points) {
+ String[] values = point.split(",");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ values[0].replaceAll("(..)", "$1:"), Integer.parseInt(values[1])));
+ }
+ }
+
+ if (item.containsKey("wn")) {
+ position.set(Position.KEY_ALARM, decodeAlarm(item.getInt("wn")));
+ }
+ if (item.containsKey("ic")) {
+ position.set(Position.KEY_ICCID, item.getString("ic"));
+ }
+ if (item.containsKey("ve")) {
+ position.set(Position.KEY_VERSION_FW, item.getString("ve"));
+ }
+ if (item.containsKey("te")) {
+ String[] temperatures = item.getString("te").split(",");
+ for (int j = 0; j < temperatures.length; j++) {
+ position.set(Position.PREFIX_TEMP + (j + 1), Integer.parseInt(temperatures[j]) * 0.1);
+ }
+ }
+ if (item.containsKey("st")) {
+ position.set(Position.KEY_STEPS, item.getInt("st"));
+ }
+ if (item.containsKey("ba")) {
+ position.set(Position.KEY_BATTERY_LEVEL, decodeBattery(item.getInt("ba")));
+ }
+ if (item.containsKey("sn")) {
+ position.set(Position.KEY_RSSI, item.getInt("sn"));
+ }
+ if (item.containsKey("hr")) {
+ position.set(Position.KEY_HEART_RATE, item.getInt("hr"));
+ }
+
+ if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) {
+ position.setNetwork(network);
+ }
+
+ positions.add(position);
+ }
+
+ return positions.isEmpty() ? null : positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/BceProtocolDecoder.java b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
index a26b8e8e6..535827f3c 100644
--- a/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -90,11 +90,13 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
}
if (BitUtil.check(mask, 14)) {
- position.setNetwork(new Network(CellTower.from(
- buf.readUnsignedShortLE(), buf.readUnsignedByte(),
- buf.readUnsignedShortLE(), buf.readUnsignedShortLE(),
- buf.readUnsignedByte())));
- buf.readUnsignedByte();
+ int mcc = buf.readUnsignedShortLE();
+ int mnc = buf.readUnsignedByte();
+ int lac = buf.readUnsignedShortLE();
+ int cid = buf.readUnsignedShortLE();
+ buf.readUnsignedByte(); // time advance
+ int rssi = -buf.readUnsignedByte();
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, lac, cid, rssi)));
}
}
@@ -177,10 +179,16 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShortLE(); // dallas humidity
}
if (BitUtil.check(mask, 9)) {
- buf.skipBytes(6); // lls group 1
+ position.set("fuel1", buf.readUnsignedShortLE());
+ position.set("fuelTemp1", (int) buf.readByte());
+ position.set("fuel2", buf.readUnsignedShortLE());
+ position.set("fuelTemp2", (int) buf.readByte());
}
if (BitUtil.check(mask, 10)) {
- buf.skipBytes(6); // lls group 2
+ position.set("fuel3", buf.readUnsignedShortLE());
+ position.set("fuelTemp3", (int) buf.readByte());
+ position.set("fuel4", buf.readUnsignedShortLE());
+ position.set("fuelTemp4", (int) buf.readByte());
}
if (BitUtil.check(mask, 11)) {
buf.skipBytes(21); // j1979 group 1
diff --git a/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java b/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java
index 6a31cb2f4..83e62ff86 100644
--- a/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java
@@ -60,7 +60,7 @@ public class C2stekProtocolDecoder extends BaseProtocolDecoder {
private String decodeAlarm(int alarm) {
switch (alarm) {
case 0x2:
- return Position.ALARM_SHOCK;
+ return Position.ALARM_VIBRATION;
case 0x3:
return Position.ALARM_POWER_CUT;
case 0x4:
diff --git a/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java
index 987361baf..815cce987 100644
--- a/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2021 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.
@@ -32,7 +32,11 @@ import java.io.StringReader;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
+import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.time.OffsetDateTime;
+import java.util.Collection;
+import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;
@@ -51,12 +55,25 @@ public class DmtHttpProtocolDecoder extends BaseHttpProtocolDecoder {
JsonObject root = Json.createReader(
new StringReader(request.content().toString(StandardCharsets.US_ASCII))).readObject();
+ Object result;
+ if (root.containsKey("device")) {
+ result = decodeEdge(channel, remoteAddress, root);
+ } else {
+ result = decodeTraditional(channel, remoteAddress, root);
+ }
+
+ sendResponse(channel, result != null ? HttpResponseStatus.OK : HttpResponseStatus.BAD_REQUEST);
+ return result;
+ }
+
+ private Collection<Position> decodeTraditional(
+ Channel channel, SocketAddress remoteAddress, JsonObject root) throws ParseException {
+
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, root.getString("IMEI"));
if (deviceSession == null) {
- sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
return null;
}
@@ -126,8 +143,77 @@ public class DmtHttpProtocolDecoder extends BaseHttpProtocolDecoder {
positions.add(position);
}
- sendResponse(channel, HttpResponseStatus.OK);
return positions;
}
+ private Position decodeEdge(
+ Channel channel, SocketAddress remoteAddress, JsonObject root) {
+
+ JsonObject device = root.getJsonObject("device");
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, device.getString("imei"));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ Date time = new Date(OffsetDateTime.parse(root.getString("date")).toInstant().toEpochMilli());
+
+ if (root.containsKey("lat") && root.containsKey("lng")) {
+ position.setValid(true);
+ position.setTime(time);
+ position.setLatitude(root.getJsonNumber("lat").doubleValue());
+ position.setLongitude(root.getJsonNumber("lng").doubleValue());
+ position.setAccuracy(root.getJsonNumber("posAcc").doubleValue());
+ } else {
+ getLastLocation(position, time);
+ }
+
+ position.set(Position.KEY_INDEX, root.getInt("sqn"));
+ position.set(Position.KEY_EVENT, root.getInt("reason"));
+
+ if (root.containsKey("analogues")) {
+ JsonArray analogues = root.getJsonArray("analogues");
+ for (int i = 0; i < analogues.size(); i++) {
+ JsonObject adc = analogues.getJsonObject(i);
+ position.set(Position.PREFIX_ADC + adc.getInt("id"), adc.getInt("val"));
+ }
+ }
+
+ if (root.containsKey("inputs")) {
+ int input = root.getInt("inputs");
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 0));
+ position.set(Position.KEY_INPUT, input);
+ }
+ if (root.containsKey("outputs")) {
+ position.set(Position.KEY_OUTPUT, root.getInt("outputs"));
+ }
+ if (root.containsKey("status")) {
+ position.set(Position.KEY_STATUS, root.getInt("status"));
+ }
+
+ if (root.containsKey("counters")) {
+ JsonArray counters = root.getJsonArray("counters");
+ for (int i = 0; i < counters.size(); i++) {
+ JsonObject counter = counters.getJsonObject(i);
+ switch (counter.getInt("id")) {
+ case 0:
+ position.set(Position.KEY_BATTERY, counter.getInt("val") * 0.001);
+ break;
+ case 1:
+ position.set(Position.KEY_BATTERY_LEVEL, counter.getInt("val") * 0.01);
+ break;
+ default:
+ position.set("counter" + counter.getInt("id"), counter.getInt("val"));
+ break;
+ }
+
+ }
+ }
+
+ return position;
+ }
+
}
diff --git a/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java b/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java
index e882c2378..d509b3ec0 100644
--- a/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DolphinProtocolDecoder.java
@@ -101,6 +101,10 @@ public class DolphinProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_SATELLITES, point.getSatellites());
position.set(Position.KEY_HDOP, point.getHDOP());
+ for (int j = 0; j < point.getIOListIDCount(); j++) {
+ position.set(Position.PREFIX_IO + point.getIOListIDValue(j), point.getIOListValue(j));
+ }
+
positions.add(position);
}
diff --git a/src/main/java/org/traccar/protocol/Dsf22FrameDecoder.java b/src/main/java/org/traccar/protocol/Dsf22FrameDecoder.java
new file mode 100644
index 000000000..388c97f85
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Dsf22FrameDecoder.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 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.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class Dsf22FrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 21) {
+ return null;
+ }
+
+ int count = buf.getUnsignedByte(buf.readerIndex() + 4);
+
+ int length = 2 + 2 + 1 + count * (4 + 4 + 4 + 1 + 2 + 1);
+
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Dsf22Protocol.java b/src/main/java/org/traccar/protocol/Dsf22Protocol.java
new file mode 100644
index 000000000..bffc3e419
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Dsf22Protocol.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class Dsf22Protocol extends BaseProtocol {
+
+ public Dsf22Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new Dsf22FrameDecoder());
+ pipeline.addLast(new Dsf22ProtocolDecoder(Dsf22Protocol.this));
+ }
+ });
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new Dsf22ProtocolDecoder(Dsf22Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java
new file mode 100644
index 000000000..d5a9df7bc
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2021 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.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Dsf22ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Dsf22ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+
+ String id = ByteBufUtil.hexDump(buf.readSlice(2));
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+ int count = buf.readUnsignedByte();
+
+ for (int i = 0; i < count; i++) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setLatitude(buf.readInt());
+ position.setLongitude(buf.readInt());
+ position.setTime(new Date(946684800000L + buf.readUnsignedInt()));
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.001);
+
+ int status = buf.readUnsignedByte();
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 0));
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(status, 1));
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(status, 4));
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 6) ? Position.ALARM_JAMMING : null);
+ position.set(Position.KEY_STATUS, status);
+
+ positions.add(position);
+
+ }
+
+ if (channel != null) {
+ byte[] response = {0x01};
+ channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(response), remoteAddress));
+ }
+
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/DualcamFrameDecoder.java b/src/main/java/org/traccar/protocol/DualcamFrameDecoder.java
new file mode 100644
index 000000000..312d43f19
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/DualcamFrameDecoder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 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.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class DualcamFrameDecoder extends BaseFrameDecoder {
+
+ private static final int MESSAGE_MINIMUM_LENGTH = 4;
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < MESSAGE_MINIMUM_LENGTH) {
+ return null;
+ }
+
+ int length;
+ if (buf.getUnsignedShort(buf.readerIndex()) == 0) {
+ length = 16;
+ } else {
+ length = 4 + buf.getUnsignedShort(buf.readerIndex() + 2);
+ }
+
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/DualcamProtocol.java b/src/main/java/org/traccar/protocol/DualcamProtocol.java
new file mode 100644
index 000000000..04c4f2bd1
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/DualcamProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class DualcamProtocol extends BaseProtocol {
+
+ public DualcamProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new DualcamFrameDecoder());
+ pipeline.addLast(new DualcamProtocolDecoder(DualcamProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java b/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java
new file mode 100644
index 000000000..c64b8171f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2021 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.Context;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+
+public class DualcamProtocolDecoder extends BaseProtocolDecoder {
+
+ public DualcamProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_INIT = 0;
+ public static final int MSG_START = 1;
+ public static final int MSG_RESUME = 2;
+ public static final int MSG_SYNC = 3;
+ public static final int MSG_DATA = 4;
+ public static final int MSG_COMPLETE = 5;
+ public static final int MSG_FILE_REQUEST = 8;
+ public static final int MSG_INIT_REQUEST = 9;
+
+ private String uniqueId;
+ private int packetCount;
+ private int currentPacket;
+ private ByteBuf photo;
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ int type = buf.readUnsignedShort();
+
+ switch (type) {
+ case MSG_INIT:
+ buf.readUnsignedShort(); // protocol id
+ uniqueId = String.valueOf(buf.readLong());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, uniqueId);
+ long settings = buf.readUnsignedInt();
+ if (channel != null && deviceSession != null) {
+ ByteBuf response = Unpooled.buffer();
+ if (BitUtil.between(settings, 26, 28) > 0) {
+ response.writeShort(MSG_FILE_REQUEST);
+ String file = BitUtil.check(settings, 26) ? "%photof" : "%photor";
+ response.writeShort(file.length());
+ response.writeCharSequence(file, StandardCharsets.US_ASCII);
+ } else {
+ response.writeShort(MSG_COMPLETE);
+ }
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ break;
+ case MSG_START:
+ buf.readUnsignedShort(); // length
+ packetCount = buf.readInt();
+ currentPacket = 1;
+ photo = Unpooled.buffer();
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(MSG_RESUME);
+ response.writeShort(4);
+ response.writeInt(currentPacket);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ break;
+ case MSG_DATA:
+ buf.readUnsignedShort(); // length
+ photo.writeBytes(buf, buf.readableBytes() - 2);
+ if (currentPacket == packetCount) {
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, null);
+ try {
+ position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg"));
+ } finally {
+ photo.release();
+ photo = null;
+ }
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(MSG_INIT_REQUEST);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ return position;
+ } else {
+ currentPacket += 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
index 613710587..8fe12fe69 100644
--- a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
@@ -421,7 +421,7 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
ByteBuf content = Unpooled.buffer();
if (type == MSG_LOGIN) {
content.writeInt((int) (System.currentTimeMillis() / 1000));
- content.writeByte(1); // protocol version
+ content.writeShort(1); // protocol version
content.writeByte(0); // action mask
}
ByteBuf response = EelinkProtocolEncoder.encodeContent(
diff --git a/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java b/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java
index cc7f6e935..0a12f781d 100644
--- a/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EsealProtocolDecoder.java
@@ -75,7 +75,7 @@ public class EsealProtocolDecoder extends BaseProtocolDecoder {
case "Event-Door":
return Position.ALARM_DOOR;
case "Event-Shock":
- return Position.ALARM_SHOCK;
+ return Position.ALARM_VIBRATION;
case "Event-Drop":
return Position.ALARM_FALL_DOWN;
case "Event-Lock":
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
index 70972f847..5f9326a61 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -31,6 +31,7 @@ import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
@@ -74,6 +75,37 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_NEW = new PatternBuilder()
+ .text("$$")
+ .number("d+,") // length
+ .number("(d+),") // imei
+ .number("x+,") // index
+ .text("A03,") // type
+ .number("(d+)?,") // alarm
+ .number("(dd)(dd)(dd)") // date (yymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(d+)|") // mcc
+ .number("(d+)|") // mnc
+ .number("(x+)|") // lac
+ .number("(x+),") // cid
+ .number("(d+.d+),") // battery
+ .number("(d+),") // battery level
+ .number("(x+),") // status
+ .groupBegin()
+ .text("0,") // gps location
+ .number("([AV]),") // validity
+ .number("(d+),") // speed
+ .number("(d+),") // satellites
+ .number("(-?d+.d+),") // latitude
+ .number("(-?d+.d+)") // longitude
+ .or()
+ .text("1,") // wifi location
+ .expression("([^*]+)") // wifi
+ .groupEnd()
+ .text("*")
+ .number("xx") // checksum
+ .compile();
+
private static final Pattern PATTERN_PHOTO = new PatternBuilder()
.text("$$")
.number("d+,") // length
@@ -160,6 +192,61 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+
+ private Object decodeLocationNew(
+ Channel channel, SocketAddress remoteAddress, String sentence) {
+
+ Parser parser = new Parser(PATTERN_NEW, 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_ALARM, decodeAlarm(parser.nextInt()));
+
+ position.setDeviceTime(parser.nextDateTime());
+
+ Network network = new Network();
+ network.addCellTower(CellTower.from(
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt()));
+
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ position.set(Position.KEY_STATUS, parser.nextHexInt());
+
+ if (parser.hasNext(5)) {
+
+ position.setValid(parser.next().equals("A"));
+ position.setFixTime(position.getDeviceTime());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+
+ } else {
+
+ String[] points = parser.next().split("\\|");
+ for (String point : points) {
+ String[] wifi = point.split(":");
+ String mac = wifi[0].replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), Integer.parseInt(wifi[1])));
+ }
+
+ }
+
+ position.setNetwork(network);
+
+ return position;
+ }
+
private Object decodeLocation(
Channel channel, SocketAddress remoteAddress, String sentence) {
@@ -206,7 +293,12 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
}
if (parser.hasNext()) {
- position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(parser.nextHexInt()));
+ String rfid = parser.next();
+ if (rfid.matches("\\p{XDigit}+")) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(Integer.parseInt(rfid, 16)));
+ } else {
+ position.set("driverLicense", rfid);
+ }
}
if (parser.hasNext()) {
@@ -296,6 +388,10 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
}
}
+ } else if (type.equals("A03")) {
+
+ return decodeLocationNew(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII));
+
} else {
return decodeLocation(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII));
diff --git a/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java
index 0a0d04db0..83ca74ce5 100644
--- a/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FlespiProtocolDecoder.java
@@ -190,7 +190,7 @@ public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder {
return true;
case "shock.event.trigger":
if (value == JsonValue.TRUE) {
- position.set(Position.KEY_ALARM, Position.ALARM_SHOCK);
+ position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
}
return true;
case "overspeeding.event.trigger":
diff --git a/src/main/java/org/traccar/protocol/FlexApiProtocol.java b/src/main/java/org/traccar/protocol/FlexApiProtocol.java
new file mode 100644
index 000000000..bc6a49907
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FlexApiProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2021 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.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class FlexApiProtocol extends BaseProtocol {
+
+ public FlexApiProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(5120));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new FlexApiProtocolDecoder(FlexApiProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java
new file mode 100644
index 000000000..d4d539a9e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2021 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.model.Position;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.util.Date;
+
+public class FlexApiProtocolDecoder extends BaseProtocolDecoder {
+
+ public FlexApiProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String message = (String) msg;
+ JsonObject root = Json.createReader(new StringReader(message.substring(1, message.length() - 2))).readObject();
+
+ String topic = root.getString("topic");
+ String clientId = topic.substring(3, topic.indexOf('/', 3));
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, clientId);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ JsonObject payload = root.getJsonObject("payload");
+
+ if (topic.contains("gnss")) {
+
+ position.setValid(true);
+
+ if (payload.containsKey("time")) {
+ position.setTime(new Date(payload.getInt("time") * 1000L));
+ position.setLatitude(payload.getJsonNumber("lat").doubleValue());
+ position.setLongitude(payload.getJsonNumber("log").doubleValue());
+ } else {
+ position.setTime(new Date(payload.getInt("gnss.ts") * 1000L));
+ position.setLatitude(payload.getJsonNumber("gnss.latitude").doubleValue());
+ position.setLongitude(payload.getJsonNumber("gnss.longitude").doubleValue());
+ }
+
+ position.setAltitude(payload.getJsonNumber("gnss.altitude").doubleValue());
+ position.setSpeed(payload.getJsonNumber("gnss.speed").doubleValue());
+ position.setCourse(payload.getJsonNumber("gnss.heading").doubleValue());
+
+ position.set(Position.KEY_SATELLITES, payload.getInt("gnss.num_sv"));
+
+ } else if (topic.contains("obd")) {
+
+ getLastLocation(position, new Date(payload.getInt("obd.ts") * 1000L));
+
+ if (payload.containsKey("obd.speed")) {
+ position.set(Position.KEY_OBD_SPEED, payload.getJsonNumber("obd.speed").doubleValue());
+ }
+ if (payload.containsKey("obd.odo")) {
+ position.set(Position.KEY_OBD_ODOMETER, payload.getInt("obd.odo"));
+ }
+ if (payload.containsKey("obd.rpm")) {
+ position.set(Position.KEY_RPM, payload.getInt("obd.rpm"));
+ }
+ if (payload.containsKey("obd.vin")) {
+ position.set(Position.KEY_VIN, payload.getString("obd.vin"));
+ }
+
+ } else {
+
+ return null;
+
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
index dc558147a..f29fb9850 100644
--- a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
@@ -309,18 +309,17 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
Position position = null;
- if (length > 1) {
+ if (photo == null) {
+ photo = Unpooled.buffer();
+ }
- if (photo == null) {
- photo = Unpooled.buffer();
- }
+ buf.readUnsignedByte(); // part number
- buf.readUnsignedByte(); // part number
- photo.writeBytes(buf, length - 1);
+ if (length > 1) {
- sendResponse(channel, 0x07, buf.readUnsignedShortLE());
+ photo.writeBytes(buf, length - 1);
- } else if (photo != null) {
+ } else {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
String uniqueId = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId();
@@ -336,6 +335,8 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
}
+ sendResponse(channel, 0x07, buf.readUnsignedShortLE());
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java b/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java
index 76278070e..a86249224 100644
--- a/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GoSafeProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -187,7 +187,12 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder {
int index = 0;
String[] fragments = sentence.split(",");
- position.setTime(new SimpleDateFormat("HHmmssddMMyy").parse(fragments[index++]));
+ if (fragments[index].matches("[0-9]{12}")) {
+ position.setTime(new SimpleDateFormat("HHmmssddMMyy").parse(fragments[index++]));
+ } else {
+ getLastLocation(position, null);
+ position.set(Position.KEY_RESULT, fragments[index++]);
+ }
for (; index < fragments.length; index += 1) {
if (!fragments[index].isEmpty()) {
diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
index 2ca71a1ae..d74f19179 100644
--- a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -140,8 +140,6 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_FUEL_LEAK;
}
switch (value) {
- case "tracker":
- return null;
case "help me":
return Position.ALARM_SOS;
case "low battery":
@@ -152,10 +150,6 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_MOVEMENT;
case "speed":
return Position.ALARM_OVERSPEED;
- case "acc on":
- return Position.ALARM_POWER_ON;
- case "acc off":
- return Position.ALARM_POWER_OFF;
case "door alarm":
return Position.ALARM_DOOR;
case "ac alarm":
@@ -163,13 +157,14 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
case "accident alarm":
return Position.ALARM_ACCIDENT;
case "sensor alarm":
- return Position.ALARM_SHOCK;
+ return Position.ALARM_VIBRATION;
case "bonnet alarm":
return Position.ALARM_BONNET;
case "footbrake alarm":
return Position.ALARM_FOOT_BRAKE;
case "DTC":
return Position.ALARM_FAULT;
+ case "tracker":
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
index 3f3a96109..0dcdab892 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -56,7 +56,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_LOGIN = 0x01;
public static final int MSG_GPS = 0x10;
- public static final int MSG_LBS = 0x11;
+ public static final int MSG_GPS_LBS_6 = 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;
@@ -78,8 +78,11 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_HEARTBEAT = 0x23;
public static final int MSG_ADDRESS_REQUEST = 0x2A;
public static final int MSG_ADDRESS_RESPONSE = 0x97;
- public static final int MSG_AZ735_GPS = 0x32;
- public static final int MSG_AZ735_ALARM = 0x33;
+ public static final int MSG_GPS_LBS_5 = 0x31;
+ public static final int MSG_GPS_LBS_STATUS_4 = 0x32;
+ public static final int MSG_WIFI_5 = 0x33;
+ public static final int MSG_AZ735_GPS = 0x32; // only extended
+ public static final int MSG_AZ735_ALARM = 0x33; // only extended
public static final int MSG_X1_GPS = 0x34;
public static final int MSG_X1_PHOTO_INFO = 0x35;
public static final int MSG_X1_PHOTO_DATA = 0x36;
@@ -120,9 +123,12 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
case MSG_GPS_LBS_2:
case MSG_GPS_LBS_3:
case MSG_GPS_LBS_4:
+ case MSG_GPS_LBS_5:
+ case MSG_GPS_LBS_6:
case MSG_GPS_LBS_STATUS_1:
case MSG_GPS_LBS_STATUS_2:
case MSG_GPS_LBS_STATUS_3:
+ case MSG_GPS_LBS_STATUS_4:
case MSG_GPS_PHONE:
case MSG_GPS_LBS_EXTEND:
case MSG_GPS_2:
@@ -136,15 +142,17 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
private static boolean hasLbs(int type) {
switch (type) {
- case MSG_LBS:
case MSG_LBS_STATUS:
case MSG_GPS_LBS_1:
case MSG_GPS_LBS_2:
case MSG_GPS_LBS_3:
case MSG_GPS_LBS_4:
+ case MSG_GPS_LBS_5:
+ case MSG_GPS_LBS_6:
case MSG_GPS_LBS_STATUS_1:
case MSG_GPS_LBS_STATUS_2:
case MSG_GPS_LBS_STATUS_3:
+ case MSG_GPS_LBS_STATUS_4:
case MSG_GPS_2:
case MSG_FENCE_SINGLE:
case MSG_FENCE_MULTI:
@@ -163,6 +171,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
case MSG_GPS_LBS_STATUS_1:
case MSG_GPS_LBS_STATUS_2:
case MSG_GPS_LBS_STATUS_3:
+ case MSG_GPS_LBS_STATUS_4:
return true;
default:
return false;
@@ -267,7 +276,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return true;
}
- private boolean decodeLbs(Position position, ByteBuf buf, boolean hasLength) {
+ private boolean decodeLbs(Position position, ByteBuf buf, int type, boolean hasLength) {
int length = 0;
if (hasLength) {
@@ -288,10 +297,11 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
}
int mcc = buf.readUnsignedShort();
- int mnc = BitUtil.check(mcc, 15) ? buf.readUnsignedShort() : buf.readUnsignedByte();
+ int mnc = BitUtil.check(mcc, 15) || type == MSG_GPS_LBS_6 ? buf.readUnsignedShort() : buf.readUnsignedByte();
+ int lac = buf.readUnsignedShort();
+ long cid = type == MSG_GPS_LBS_6 ? buf.readUnsignedInt() : buf.readUnsignedMedium();
- position.setNetwork(new Network(CellTower.from(
- BitUtil.to(mcc, 15), mnc, buf.readUnsignedShort(), buf.readUnsignedMedium())));
+ position.setNetwork(new Network(CellTower.from(BitUtil.to(mcc, 15), mnc, lac, cid)));
if (length > 9) {
buf.skipBytes(length - 9);
@@ -311,7 +321,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
switch (BitUtil.between(status, 3, 6)) {
case 1:
- position.set(Position.KEY_ALARM, Position.ALARM_SHOCK);
+ position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
break;
case 2:
position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
@@ -685,10 +695,14 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- if (type == MSG_LBS_MULTIPLE_1 || type == MSG_LBS_MULTIPLE_2 || type == MSG_LBS_EXTEND
- || type == MSG_LBS_WIFI || type == MSG_LBS_2 || type == MSG_WIFI_3) {
+ if (type == MSG_LBS_STATUS && dataLength >= 18) {
- boolean longFormat = type == MSG_LBS_2 || type == MSG_WIFI_3;
+ return null; // space10x multi-lbs message
+
+ } else if (type == MSG_LBS_MULTIPLE_1 || type == MSG_LBS_MULTIPLE_2 || type == MSG_LBS_EXTEND
+ || type == MSG_LBS_WIFI || type == MSG_LBS_2 || type == MSG_WIFI_3 || type == MSG_WIFI_5) {
+
+ boolean longFormat = type == MSG_LBS_2 || type == MSG_WIFI_3 || type == MSG_WIFI_5;
DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone())
.setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
@@ -699,7 +713,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
int mcc = buf.readUnsignedShort();
int mnc = BitUtil.check(mcc, 15) ? buf.readUnsignedShort() : buf.readUnsignedByte();
Network network = new Network();
- for (int i = 0; i < 7; i++) {
+
+ int cellCount = type == MSG_WIFI_5 ? 6 : 7;
+ for (int i = 0; i < cellCount; i++) {
int lac = longFormat ? buf.readInt() : buf.readUnsignedShort();
int cid = longFormat ? (int) buf.readLong() : buf.readUnsignedMedium();
int rssi = -buf.readUnsignedByte();
@@ -871,7 +887,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
}
if (hasLbs(type)) {
- decodeLbs(position, buf, hasStatus(type));
+ decodeLbs(position, buf, type, hasStatus(type));
}
if (hasStatus(type)) {
@@ -1039,7 +1055,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, position.getDeviceTime());
}
- if (decodeLbs(position, buf, true)) {
+ if (decodeLbs(position, buf, type, true)) {
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
}
@@ -1127,7 +1143,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else if (type == MSG_GPS_MODULAR) {
- return decodeExtendedModular(buf, deviceSession);
+ return decodeExtendedModular(channel, buf, deviceSession);
} else {
@@ -1138,7 +1154,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private Object decodeExtendedModular(ByteBuf buf, DeviceSession deviceSession) {
+ private Object decodeExtendedModular(Channel channel, ByteBuf buf, DeviceSession deviceSession) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -1239,6 +1255,12 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
}
}
+ if (position.getFixTime() == null) {
+ getLastLocation(position, null);
+ }
+
+ sendResponse(channel, false, MSG_GPS_MODULAR, buf.readUnsignedShort(), null);
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
index 3f8611b85..dd7141a2c 100644
--- a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
@@ -387,7 +387,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.setNetwork(new Network(CellTower.fromLacCid(parser.nextHexInt(0), parser.nextHexInt(0))));
}
- if (parser.hasNext(4)) {
+ if (parser.hasNext()) {
String[] values = parser.next().split(",");
for (int i = 0; i < values.length; i++) {
position.set(Position.PREFIX_IO + (i + 1), values[i].trim());
diff --git a/src/main/java/org/traccar/protocol/HoopoProtocol.java b/src/main/java/org/traccar/protocol/HoopoProtocol.java
new file mode 100644
index 000000000..387b967d3
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/HoopoProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 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 HoopoProtocol extends BaseProtocol {
+
+ public HoopoProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new JsonFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new HoopoProtocolDecoder(HoopoProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java
new file mode 100644
index 000000000..65333ba6e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/HoopoProtocolDecoder.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2021 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.model.Position;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.time.OffsetDateTime;
+import java.util.Date;
+
+public class HoopoProtocolDecoder extends BaseProtocolDecoder {
+
+ public HoopoProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ JsonObject json = Json.createReader(new StringReader((String) msg)).readObject();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, json.getString("deviceId"));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ if (json.containsKey("eventData")) {
+
+ JsonObject eventData = json.getJsonObject("eventData");
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ Date time = new Date(OffsetDateTime.parse(json.getString("eventTime")).toInstant().toEpochMilli());
+ position.setTime(time);
+
+ position.setValid(true);
+ position.setLatitude(eventData.getJsonNumber("latitude").doubleValue());
+ position.setLongitude(eventData.getJsonNumber("longitude").doubleValue());
+
+ position.set(Position.KEY_EVENT, eventData.getString("eventType"));
+ position.set(Position.KEY_BATTERY_LEVEL, eventData.getInt("batteryLevel"));
+
+ 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 2e1ddf5f2..891046213 100644
--- a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
@@ -262,6 +262,13 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
case 0x0011:
position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 0.05);
break;
+ case 0x0014:
+ position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte() / 255.0);
+ position.set("timingAdvance", buf.readUnsignedByte() * 0.5);
+ position.set("airTemp", buf.readUnsignedByte() - 40);
+ position.set("airFlow", buf.readUnsignedShort() * 0.01);
+ position.set(Position.KEY_THROTTLE, buf.readUnsignedByte() / 255.0);
+ 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 675a08aef..aa85ea061 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -34,8 +34,10 @@ import org.traccar.model.Position;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
+import java.util.TimeZone;
public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
@@ -51,10 +53,14 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_TERMINAL_CONTROL = 0x8105;
public static final int MSG_TERMINAL_AUTH = 0x0102;
public static final int MSG_LOCATION_REPORT = 0x0200;
+ public static final int MSG_ACCELERATION = 0x2070;
public static final int MSG_LOCATION_REPORT_2 = 0x5501;
public static final int MSG_LOCATION_REPORT_BLIND = 0x5502;
public static final int MSG_LOCATION_BATCH = 0x0704;
public static final int MSG_OIL_CONTROL = 0XA006;
+ public static final int MSG_TIME_SYNC_REQUEST = 0x0109;
+ public static final int MSG_TIME_SYNC_RESPONSE = 0x8109;
+ public static final int MSG_PHOTO = 0x8888;
public static final int RESULT_SUCCESS = 0;
@@ -67,7 +73,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
if (shortIndex) {
buf.writeByte(1);
} else {
- buf.writeShort(1);
+ buf.writeShort(0);
}
buf.writeBytes(data);
data.release();
@@ -131,6 +137,11 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ private int readSignedWord(ByteBuf buf) {
+ int value = buf.readUnsignedShort();
+ return BitUtil.check(value, 15) ? -BitUtil.to(value, 15) : BitUtil.to(value, 15);
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -172,7 +183,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress));
}
- } else if (type == MSG_TERMINAL_AUTH || type == MSG_HEARTBEAT) {
+ } else if (type == MSG_TERMINAL_AUTH || type == MSG_HEARTBEAT || type == MSG_PHOTO) {
sendGeneralResponse(channel, remoteAddress, id, type, index);
@@ -192,8 +203,52 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
} else if (type == MSG_LOCATION_BATCH) {
+ sendGeneralResponse(channel, remoteAddress, id, type, index);
+
return decodeLocationBatch(deviceSession, buf);
+ } else if (type == MSG_TIME_SYNC_REQUEST) {
+
+ if (channel != null) {
+ Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(calendar.get(Calendar.YEAR));
+ response.writeByte(calendar.get(Calendar.MONTH) + 1);
+ response.writeByte(calendar.get(Calendar.DAY_OF_MONTH));
+ response.writeByte(calendar.get(Calendar.HOUR_OF_DAY));
+ response.writeByte(calendar.get(Calendar.MINUTE));
+ response.writeByte(calendar.get(Calendar.SECOND));
+ channel.writeAndFlush(new NetworkMessage(
+ formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, false, response), remoteAddress));
+ }
+
+ } else if (type == MSG_ACCELERATION) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ StringBuilder data = new StringBuilder("[");
+ while (buf.readableBytes() > 2) {
+ buf.skipBytes(6); // time
+ if (data.length() > 1) {
+ data.append(",");
+ }
+ data.append("[");
+ data.append(readSignedWord(buf));
+ data.append(",");
+ data.append(readSignedWord(buf));
+ data.append(",");
+ data.append(readSignedWord(buf));
+ data.append("]");
+ }
+ data.append("]");
+
+ position.set(Position.KEY_G_SENSOR, data.toString());
+
+ return position;
+
}
return null;
@@ -211,6 +266,76 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ private void decodeExtension(Position position, ByteBuf buf, int endIndex) {
+ while (buf.readerIndex() < endIndex) {
+ int type = buf.readUnsignedByte();
+ int length = buf.readUnsignedByte();
+ switch (type) {
+ case 0x01:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100L);
+ break;
+ case 0x02:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x03:
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x80:
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte());
+ break;
+ case 0x81:
+ position.set(Position.KEY_RPM, buf.readUnsignedShort());
+ break;
+ case 0x82:
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x83:
+ position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte());
+ break;
+ case 0x84:
+ position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte() - 40);
+ break;
+ case 0x85:
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort());
+ break;
+ case 0x86:
+ position.set("intakeTemp", buf.readUnsignedByte() - 40);
+ break;
+ case 0x87:
+ position.set("intakeFlow", buf.readUnsignedShort());
+ break;
+ case 0x88:
+ position.set("intakePressure", buf.readUnsignedByte());
+ break;
+ case 0x89:
+ position.set(Position.KEY_THROTTLE, buf.readUnsignedByte());
+ break;
+ case 0x8B:
+ position.set(Position.KEY_VIN, buf.readCharSequence(17, StandardCharsets.US_ASCII).toString());
+ break;
+ case 0x8C:
+ position.set(Position.KEY_OBD_ODOMETER, buf.readUnsignedInt() * 100L);
+ break;
+ case 0x8D:
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedShort() * 1000L);
+ break;
+ case 0x8E:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte());
+ break;
+ case 0xA0:
+ String codes = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString();
+ position.set(Position.KEY_DTCS, codes.replace(',', ' '));
+ break;
+ case 0xCC:
+ position.set(Position.KEY_ICCID, buf.readCharSequence(20, StandardCharsets.US_ASCII).toString());
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
+ }
+ }
+
private Position decodeLocation(DeviceSession deviceSession, ByteBuf buf) {
Position position = new Position(getProtocolName());
@@ -291,6 +416,11 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, Integer.parseInt(lockStatus.substring(2, 5)) * 0.01);
}
break;
+ case 0x80:
+ buf.readUnsignedByte(); // content
+ endIndex = buf.writerIndex() - 2;
+ decodeExtension(position, buf, endIndex);
+ break;
case 0x91:
position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.1);
position.set(Position.KEY_RPM, buf.readUnsignedShort());
@@ -311,6 +441,13 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString());
}
break;
+ case 0xA7:
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+ break;
+ case 0xAC:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ break;
case 0xD0:
long userStatus = buf.readUnsignedInt();
if (BitUtil.check(userStatus, 3)) {
@@ -368,6 +505,16 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
}
}
break;
+ case 0xED:
+ String license = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString().trim();
+ position.set("driverLicense", license);
+ break;
+ case 0xEE:
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ break;
default:
break;
}
@@ -429,11 +576,15 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
List<Position> positions = new LinkedList<>();
int count = buf.readUnsignedShort();
- buf.readUnsignedByte(); // location type
+ int locationType = buf.readUnsignedByte();
for (int i = 0; i < count; i++) {
int endIndex = buf.readUnsignedShort() + buf.readerIndex();
- positions.add(decodeLocation(deviceSession, buf));
+ Position position = decodeLocation(deviceSession, buf);
+ if (locationType > 0) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+ positions.add(position);
buf.readerIndex(endIndex);
}
diff --git a/src/main/java/org/traccar/protocol/JsonFrameDecoder.java b/src/main/java/org/traccar/protocol/JsonFrameDecoder.java
new file mode 100644
index 000000000..b2d7fbd53
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/JsonFrameDecoder.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2021 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.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class JsonFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ int startIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '{');
+ if (startIndex >= 0) {
+
+ buf.readerIndex(startIndex);
+
+ int currentIndex = startIndex + 1;
+ int nesting = 1;
+ while (currentIndex < buf.writerIndex() && nesting > 0) {
+ byte currentByte = buf.getByte(currentIndex);
+ if (currentByte == '{') {
+ nesting += 1;
+ } else if (currentByte == '}') {
+ nesting -= 1;
+ }
+ currentIndex += 1;
+ }
+
+ if (nesting == 0) {
+ return buf.readRetainedSlice(currentIndex - startIndex);
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
index d745153a4..dc4bd3486 100644
--- a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2021 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.
@@ -87,7 +87,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
}
static boolean isLongFormat(ByteBuf buf, int flagIndex) {
- return buf.getUnsignedByte(flagIndex) >> 4 == 0x7;
+ return buf.getUnsignedByte(flagIndex) >> 4 >= 7;
}
static void decodeBinaryLocation(ByteBuf buf, Position position) {
@@ -141,6 +141,8 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
int version = BitUtil.from(buf.readUnsignedByte(), 4);
buf.readUnsignedShort(); // length
+ boolean responseRequired = false;
+
while (buf.readableBytes() > 1) {
Position position = new Position(getProtocolName());
@@ -160,6 +162,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ALARM, BitUtil.check(status, 2) ? Position.ALARM_GEOFENCE_EXIT : null);
position.set(Position.KEY_ALARM, BitUtil.check(status, 3) ? Position.ALARM_POWER_CUT : null);
position.set(Position.KEY_ALARM, BitUtil.check(status, 4) ? Position.ALARM_VIBRATION : null);
+ if (BitUtil.check(status, 5)) {
+ responseRequired = true;
+ }
position.set(Position.KEY_BLOCKED, BitUtil.check(status, 7));
position.set(Position.KEY_ALARM, BitUtil.check(status, 8 + 3) ? Position.ALARM_LOW_BATTERY : null);
position.set(Position.KEY_ALARM, BitUtil.check(status, 8 + 6) ? Position.ALARM_FAULT : null);
@@ -176,7 +181,7 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
cellTower.setSignalStrength((int) buf.readUnsignedByte());
position.setNetwork(new Network(cellTower));
- if (protocolVersion == 0x17) {
+ if (protocolVersion == 0x17 || protocolVersion == 0x19) {
buf.readUnsignedByte(); // geofence id
buf.skipBytes(3); // reserved
buf.skipBytes(buf.readableBytes() - 1);
@@ -232,7 +237,15 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
}
- buf.readUnsignedByte(); // index
+ int index = buf.readUnsignedByte();
+
+ if (channel != null && responseRequired) {
+ if (protocolVersion < 0x19) {
+ channel.writeAndFlush(new NetworkMessage("(P35)", remoteAddress));
+ } else {
+ channel.writeAndFlush(new NetworkMessage("(P69,0," + index + ")", remoteAddress));
+ }
+ }
return positions;
}
diff --git a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
index 251351a74..a14f9b8a4 100644
--- a/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/KhdProtocolDecoder.java
@@ -162,7 +162,13 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
if (type != MSG_ALARM) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium());
+ int odometer = buf.readUnsignedMedium();
+ if (BitUtil.to(odometer, 16) > 0) {
+ position.set(Position.KEY_ODOMETER, odometer);
+ } else if (odometer > 0) {
+ position.set(Position.KEY_FUEL_LEVEL, BitUtil.from(odometer, 16));
+ }
+
position.set(Position.KEY_STATUS, buf.readUnsignedInt());
buf.readUnsignedShort();
@@ -172,7 +178,7 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte();
buf.readUnsignedByte();
- position.set(Position.KEY_RESULT, buf.readUnsignedByte());
+ position.set(Position.KEY_RESULT, String.valueOf(buf.readUnsignedByte()));
if (type == MSG_PERIPHERAL) {
diff --git a/src/main/java/org/traccar/protocol/AppletProtocol.java b/src/main/java/org/traccar/protocol/LacakProtocol.java
index 2297dca03..0a0499ad7 100644
--- a/src/main/java/org/traccar/protocol/AppletProtocol.java
+++ b/src/main/java/org/traccar/protocol/LacakProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,16 +22,16 @@ import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-public class AppletProtocol extends BaseProtocol {
+public class LacakProtocol extends BaseProtocol {
- public AppletProtocol() {
+ public LacakProtocol() {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(16384));
- pipeline.addLast(new AppletProtocolDecoder(AppletProtocol.this));
+ pipeline.addLast(new LacakProtocolDecoder(LacakProtocol.this));
}
});
}
diff --git a/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java b/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java
new file mode 100644
index 000000000..132087c8f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/LacakProtocolDecoder.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2021 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.helper.DateUtil;
+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;
+
+public class LacakProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public LacakProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+ JsonObject root = Json.createReader(
+ new StringReader(request.content().toString(StandardCharsets.US_ASCII))).readObject();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, root.getString("device_id"));
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ JsonObject location = root.getJsonObject("location");
+
+ position.setTime(DateUtil.parseDate(location.getString("timestamp")));
+
+ if (location.containsKey("coords")) {
+ JsonObject coordinates = location.getJsonObject("coords");
+ position.setLatitude(coordinates.getJsonNumber("latitude").doubleValue());
+ position.setLongitude(coordinates.getJsonNumber("longitude").doubleValue());
+ position.setAccuracy(coordinates.getJsonNumber("accuracy").doubleValue());
+ position.setSpeed(coordinates.getJsonNumber("speed").doubleValue());
+ position.setCourse(coordinates.getJsonNumber("heading").doubleValue());
+ position.setAltitude(coordinates.getJsonNumber("altitude").doubleValue());
+ }
+
+ if (location.containsKey("event")) {
+ position.set(Position.KEY_EVENT, location.getString("event"));
+ }
+ if (location.containsKey("is_moving")) {
+ position.set(Position.KEY_MOTION, location.getBoolean("is_moving"));
+ }
+ if (location.containsKey("odometer")) {
+ position.set(Position.KEY_ODOMETER, location.getInt("odometer"));
+ }
+ if (location.containsKey("mock")) {
+ position.set("mock", location.getBoolean("mock"));
+ }
+ if (location.containsKey("activity")) {
+ position.set("activity", location.getJsonObject("activity").getString("type"));
+ }
+ if (location.containsKey("battery")) {
+ JsonObject battery = location.getJsonObject("battery");
+ position.set(Position.KEY_BATTERY_LEVEL, (int) (battery.getJsonNumber("level").doubleValue() * 100));
+ position.set(Position.KEY_CHARGE, battery.getBoolean("is_charging"));
+ }
+
+ sendResponse(channel, HttpResponseStatus.OK);
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
index 4abb75025..45890e9a2 100644
--- a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
@@ -93,7 +93,7 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
case "H":
return Position.ALARM_POWER_OFF;
case "8":
- return Position.ALARM_SHOCK;
+ return Position.ALARM_VIBRATION;
case "7":
case "4":
return Position.ALARM_GEOFENCE_EXIT;
diff --git a/src/main/java/org/traccar/protocol/MegastekFrameDecoder.java b/src/main/java/org/traccar/protocol/MegastekFrameDecoder.java
index 347fa24b1..a4091436c 100644
--- a/src/main/java/org/traccar/protocol/MegastekFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/MegastekFrameDecoder.java
@@ -46,6 +46,9 @@ public class MegastekFrameDecoder extends BaseFrameDecoder {
if (delimiter == -1) {
delimiter = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '!');
}
+ if (delimiter == -1) {
+ delimiter = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\n');
+ }
if (delimiter != -1) {
ByteBuf result = buf.readRetainedSlice(delimiter - buf.readerIndex());
buf.skipBytes(1);
diff --git a/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
index fae73d931..7233280c2 100644
--- a/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
@@ -141,6 +141,9 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
beginIndex = endIndex + 2;
endIndex = sentence.indexOf('*', beginIndex) + 3;
+ if (endIndex < 0) {
+ return null;
+ }
location = sentence.substring(beginIndex, endIndex);
beginIndex = endIndex + 1;
@@ -243,13 +246,13 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
.number("dd,")
.number("(dd),") // satellites
.number("dd,")
- .number("(d+.d+),") // hdop
+ .number("(d+.d+)?,") // hdop
.number("(d+.d+)?,") // speed
.number("(d+.d+)?,") // course
- .number("(-?d+.d+),") // altitude
+ .number("(-?d+.d+)?,") // altitude
.number("(d+.d+)?,") // odometer
- .number("(d+),") // mcc
- .number("(d+),") // mnc
+ .number("(d+)?,") // mcc
+ .number("(d+)?,") // mnc
.number("(xxxx)?,") // lac
.number("(x+)?,") // cid
.number("(d+)?,") // gsm
@@ -318,17 +321,19 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, parser.nextDouble(0) * 1000);
}
- int mcc = parser.nextInt();
- int mnc = parser.nextInt();
- Integer lac = parser.nextHexInt();
- Integer cid = parser.nextHexInt();
- Integer rssi = parser.nextInt();
- if (lac != null && cid != null) {
- CellTower tower = CellTower.from(mcc, mnc, lac, cid);
- if (rssi != null) {
- tower.setSignalStrength(rssi);
+ if (parser.hasNext(5)) {
+ int mcc = parser.nextInt();
+ int mnc = parser.nextInt();
+ Integer lac = parser.nextHexInt();
+ Integer cid = parser.nextHexInt();
+ Integer rssi = parser.nextInt();
+ if (lac != null && cid != null) {
+ CellTower tower = CellTower.from(mcc, mnc, lac, cid);
+ if (rssi != null) {
+ tower.setSignalStrength(rssi);
+ }
+ position.setNetwork(new Network(tower));
}
- position.setNetwork(new Network(tower));
}
if (parser.hasNext(5)) {
@@ -418,7 +423,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
case "psr":
return Position.ALARM_POWER_RESTORED;
case "hit":
- return Position.ALARM_SHOCK;
+ return Position.ALARM_VIBRATION;
case "belt on":
case "belton":
return Position.ALARM_LOCK;
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
index 9b12f1c15..0eb6f8776 100644
--- a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2021 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.
@@ -23,6 +23,7 @@ import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -68,15 +69,23 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // runtime
.number("(d+)|") // mcc
.number("(d+)|") // mnc
- .number("(x+)|") // lac
- .number("(x+),") // cid
+ .number("(x+)?|") // lac
+ .number("(x+)?,") // cid
.number("(xx)") // input
.number("(xx),") // output
+ .groupBegin()
+ .number("(d+.d+)|") // battery
+ .number("(d+.d+)|") // power
+ .number("d+.d+|") // rtc voltage
+ .number("d+.d+|") // mcu voltage
+ .number("d+.d+,") // gps voltage
+ .or()
.number("(x+)?|") // adc1
.number("(x+)?|") // adc2
.number("(x+)?|") // adc3
.number("(x+)|") // battery
.number("(x+)?,") // power
+ .groupEnd()
.groupBegin()
.expression("([^,]+)?,").optional() // event specific
.expression("[^,]*,") // reserved
@@ -174,51 +183,70 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, parser.nextInt());
position.set("runtime", parser.next());
- position.setNetwork(new Network(CellTower.from(
- parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), rssi)));
+ int mcc = parser.nextInt();
+ int mnc = parser.nextInt();
+ int lac = parser.nextHexInt(0);
+ int cid = parser.nextHexInt(0);
+ if (mcc != 0 && mnc != 0) {
+ position.setNetwork(new Network(CellTower.from(mcc, mnc, lac, cid, rssi)));
+ }
- position.set(Position.KEY_INPUT, parser.nextHexInt());
- position.set(Position.KEY_OUTPUT, parser.nextHexInt());
+ int input = parser.nextHexInt();
+ int output = parser.nextHexInt();
- for (int i = 1; i <= 3; i++) {
- position.set(Position.PREFIX_ADC + i, parser.nextHexInt());
- }
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 2));
+
+ position.set(Position.KEY_INPUT, input);
+ position.set(Position.KEY_OUTPUT, output);
+
+ if (parser.hasNext(2)) {
+
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+
+ } else {
+
+ for (int i = 1; i <= 3; i++) {
+ position.set(Position.PREFIX_ADC + i, parser.nextHexInt());
+ }
+
+ String deviceModel = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getModel();
+ if (deviceModel == null) {
+ deviceModel = "";
+ }
+ switch (deviceModel.toUpperCase()) {
+ case "MVT340":
+ case "MVT380":
+ position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.0 * 2.0 / 1024.0);
+ position.set(Position.KEY_POWER, parser.nextHexInt(0) * 3.0 * 16.0 / 1024.0);
+ break;
+ case "MT90":
+ position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.3 * 2.0 / 4096.0);
+ position.set(Position.KEY_POWER, parser.nextHexInt(0));
+ break;
+ case "T1":
+ case "T3":
+ case "MVT100":
+ case "MVT600":
+ case "MVT800":
+ case "TC68":
+ case "TC68S":
+ position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.3 * 2.0 / 4096.0);
+ position.set(Position.KEY_POWER, parser.nextHexInt(0) * 3.3 * 16.0 / 4096.0);
+ break;
+ case "T311":
+ case "T322X":
+ case "T333":
+ case "T355":
+ position.set(Position.KEY_BATTERY, parser.nextHexInt(0) / 100.0);
+ position.set(Position.KEY_POWER, parser.nextHexInt(0) / 100.0);
+ break;
+ default:
+ position.set(Position.KEY_BATTERY, parser.nextHexInt(0));
+ position.set(Position.KEY_POWER, parser.nextHexInt(0));
+ break;
+ }
- String deviceModel = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getModel();
- if (deviceModel == null) {
- deviceModel = "";
- }
- switch (deviceModel.toUpperCase()) {
- case "MVT340":
- case "MVT380":
- position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.0 * 2.0 / 1024.0);
- position.set(Position.KEY_POWER, parser.nextHexInt(0) * 3.0 * 16.0 / 1024.0);
- break;
- case "MT90":
- position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.3 * 2.0 / 4096.0);
- position.set(Position.KEY_POWER, parser.nextHexInt(0));
- break;
- case "T1":
- case "T3":
- case "MVT100":
- case "MVT600":
- case "MVT800":
- case "TC68":
- case "TC68S":
- position.set(Position.KEY_BATTERY, parser.nextHexInt(0) * 3.3 * 2.0 / 4096.0);
- position.set(Position.KEY_POWER, parser.nextHexInt(0) * 3.3 * 16.0 / 4096.0);
- break;
- case "T311":
- case "T322X":
- case "T333":
- case "T355":
- position.set(Position.KEY_BATTERY, parser.nextHexInt(0) / 100.0);
- position.set(Position.KEY_POWER, parser.nextHexInt(0) / 100.0);
- break;
- default:
- position.set(Position.KEY_BATTERY, parser.nextHexInt(0));
- position.set(Position.KEY_POWER, parser.nextHexInt(0));
- break;
}
String eventData = parser.next();
diff --git a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
index 992b5c43a..652ba3f6a 100644
--- a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
@@ -54,7 +54,7 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder {
.expression("([EW]),")
.number("(d+.?d*)?,") // speed
.number("(d+.?d*)?,") // course
- .number("(d+.?d*)?,") // altitude
+ .number("(-?d+.?d*)?,") // altitude
.number("(dd)(dd)(dd)") // date (ddmmyy)
.compile();
diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java
index 2b7a960c4..d5be31cec 100644
--- a/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MiniFinderProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2021 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.
@@ -143,7 +143,7 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder {
}
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
- if (deviceSession == null || !sentence.matches("![3A-D],.*")) {
+ if (deviceSession == null || !sentence.matches("![35A-D],.*")) {
return null;
}
@@ -161,6 +161,19 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type.equals("5")) {
+
+ String[] values = sentence.split(",");
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_RSSI, Integer.parseInt(values[1]));
+ if (values.length >= 4) {
+ position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(values[3]));
+ }
+
+ return position;
+
} else if (type.equals("B") || type.equals("D")) {
Parser parser = new Parser(PATTERN_BD, sentence);
diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
index 68e9e8dd5..641a45864 100644
--- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
@@ -46,6 +46,8 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
}
public static final int MSG_DATA = 0x01;
+ public static final int MSG_CONFIGURATION = 0x02;
+ public static final int MSG_SERVICES = 0x03;
public static final int MSG_RESPONSE = 0x7F;
private String decodeAlarm(int code) {
@@ -70,25 +72,44 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, int index, int type, ByteBuf buf) {
- ByteBuf buf = (ByteBuf) msg;
+ if (channel != null) {
- buf.readUnsignedByte(); // header
- int flags = buf.readUnsignedByte();
- buf.readUnsignedShortLE(); // length
- buf.readUnsignedShortLE(); // checksum
- int index = buf.readUnsignedShortLE();
- int type = buf.readUnsignedByte();
-
- if (BitUtil.check(flags, 4) && channel != null) {
+ ByteBuf body = Unpooled.buffer();
+ if (type == MSG_SERVICES) {
+ while (buf.isReadable()) {
+ int endIndex = buf.readUnsignedByte() + buf.readerIndex();
+ int key = buf.readUnsignedByte();
+ switch (key) {
+ case 0x11:
+ case 0x21:
+ case 0x22:
+ body.writeByte(9 + 1); // length
+ body.writeByte(key);
+ body.writeIntLE(0); // latitude
+ body.writeIntLE(0); // longitude
+ body.writeByte(0); // address
+ break;
+ case 0x12:
+ body.writeByte(5); // length
+ body.writeByte(key);
+ body.writeIntLE((int) (System.currentTimeMillis() / 1000));
+ break;
+ default:
+ break;
+ }
+ buf.readerIndex(endIndex);
+ }
+ } else {
+ body.writeByte(1); // key length
+ body.writeByte(0); // success
+ }
ByteBuf content = Unpooled.buffer();
- content.writeByte(MSG_RESPONSE);
- content.writeByte(1); // key length
- content.writeByte(0); // success
+ content.writeByte(type == MSG_SERVICES ? type : MSG_RESPONSE);
+ content.writeBytes(body);
+ body.release();
ByteBuf response = Unpooled.buffer();
response.writeByte(0xAB); // header
@@ -101,6 +122,32 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
+ }
+
+ private String readTagId(ByteBuf buf) {
+ StringBuilder tagId = new StringBuilder();
+ for (int i = 0; i < 6; i++) {
+ tagId.insert(0, ByteBufUtil.hexDump(buf.readSlice(1)));
+ }
+ return tagId.toString();
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // header
+ int flags = buf.readUnsignedByte();
+ buf.readUnsignedShortLE(); // length
+ buf.readUnsignedShortLE(); // checksum
+ int index = buf.readUnsignedShortLE();
+ int type = buf.readUnsignedByte();
+
+ if (BitUtil.check(flags, 4)) {
+ sendResponse(channel, remoteAddress, index, type, buf);
+ }
if (type == MSG_DATA) {
@@ -175,9 +222,10 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
}
break;
case 0x23:
- position.set("tagId", ByteBufUtil.hexDump(buf.readSlice(6)));
+ position.set("tagId", readTagId(buf));
position.setLatitude(buf.readIntLE() * 0.0000001);
position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setValid(true);
hasLocation = true;
break;
case 0x24:
@@ -188,12 +236,13 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
break;
case 0x28:
int beaconFlags = buf.readUnsignedByte();
- position.set("tagId", ByteBufUtil.hexDump(buf.readSlice(6)));
- position.set("tagRssi", buf.readUnsignedByte());
- buf.readUnsignedByte(); // 1m rssi
+ position.set("tagId", readTagId(buf));
+ position.set("tagRssi", (int) buf.readByte());
+ position.set("tag1mRssi", (int) buf.readByte());
if (BitUtil.check(beaconFlags, 7)) {
position.setLatitude(buf.readIntLE() * 0.0000001);
position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setValid(true);
hasLocation = true;
}
if (BitUtil.check(beaconFlags, 6)) {
@@ -201,6 +250,15 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
endIndex - buf.readerIndex(), StandardCharsets.US_ASCII).toString());
}
break;
+ case 0x2A:
+ buf.readUnsignedByte(); // flags
+ buf.skipBytes(6); // mac
+ buf.readUnsignedByte(); // rssi
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setValid(true);
+ hasLocation = true;
+ break;
case 0x30:
buf.readUnsignedInt(); // timestamp
position.set(Position.KEY_STEPS, buf.readUnsignedInt());
diff --git a/src/main/java/org/traccar/protocol/MobilogixProtocol.java b/src/main/java/org/traccar/protocol/MobilogixProtocol.java
index ebf2c9c7f..28380a2af 100644
--- a/src/main/java/org/traccar/protocol/MobilogixProtocol.java
+++ b/src/main/java/org/traccar/protocol/MobilogixProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2021 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.
@@ -28,7 +28,7 @@ public class MobilogixProtocol extends BaseProtocol {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, "]\r\n", "]"));
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ']'));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new MobilogixProtocolDecoder(MobilogixProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java b/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java
index 8677ba9ec..86c89e336 100644
--- a/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2021 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,28 +39,52 @@ public class MobilogixProtocolDecoder extends BaseProtocolDecoder {
.text("[")
.number("(dddd)-(dd)-(dd) ") // date (yyyymmdd)
.number("(dd):(dd):(dd),") // time (hhmmss)
- .number("Td,") // type
- .number("d+,") // device type
+ .number("Td+,") // type
+ .number("(d),") // archive
.expression("[^,]+,") // protocol version
.expression("([^,]+),") // serial number
.number("(xx),") // status
- .number("(d+.d+),") // battery
- .number("(d)") // valid
+ .number("(d+.d+)") // battery
+ .groupBegin()
+ .text(",")
+ .number("(d)") // satellites
.number("(d)") // rssi
- .number("(d),") // satellites
+ .number("(d),") // valid
.number("(-?d+.d+),") // latitude
.number("(-?d+.d+),") // longitude
.number("(d+.?d*),") // speed
.number("(d+.?d*)") // course
+ .groupEnd("?")
.any()
.compile();
+ private String decodeAlarm(String type) {
+ switch (type) {
+ case "T8":
+ return Position.ALARM_LOW_BATTERY;
+ case "T9":
+ return Position.ALARM_VIBRATION;
+ case "T10":
+ return Position.ALARM_POWER_CUT;
+ case "T11":
+ return Position.ALARM_LOW_POWER;
+ case "T12":
+ return Position.ALARM_GEOFENCE_EXIT;
+ case "T13":
+ return Position.ALARM_OVERSPEED;
+ case "T15":
+ return Position.ALARM_TOW;
+ default:
+ return null;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- String sentence = (String) msg;
- String type = sentence.substring(21, 21 + 2);
+ String sentence = ((String) msg).trim();
+ String type = sentence.substring(21, sentence.indexOf(',', 21));
if (channel != null) {
String time = sentence.substring(1, 20);
@@ -68,19 +92,22 @@ public class MobilogixProtocolDecoder extends BaseProtocolDecoder {
if (type.equals("T1")) {
response = String.format("[%s,S1,1]", time);
} else {
- response = String.format("[%s,S%c]", time, type.charAt(1));
+ response = String.format("[%s,S%s]", time, type.substring(1));
}
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
- Parser parser = new Parser(PATTERN, (String) msg);
+ Parser parser = new Parser(PATTERN, sentence);
if (!parser.matches()) {
return null;
}
Position position = new Position(getProtocolName());
- position.setTime(parser.nextDateTime());
+ position.setDeviceTime(parser.nextDateTime());
+ if (parser.nextInt() == 0) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
@@ -88,6 +115,9 @@ public class MobilogixProtocolDecoder extends BaseProtocolDecoder {
}
position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_TYPE, type);
+ position.set(Position.KEY_ALARM, decodeAlarm(type));
+
int status = parser.nextHexInt();
position.set(Position.KEY_IGNITION, BitUtil.check(status, 2));
position.set(Position.KEY_MOTION, BitUtil.check(status, 3));
@@ -95,15 +125,24 @@ public class MobilogixProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, parser.nextDouble());
- position.setValid(parser.nextInt() > 0);
+ if (parser.hasNext(7)) {
- position.set(Position.KEY_RSSI, parser.nextInt());
- position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_RSSI, 6 * parser.nextInt() - 111);
- position.setLatitude(parser.nextDouble());
- position.setLongitude(parser.nextDouble());
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
- position.setCourse(parser.nextDouble());
+ position.setValid(parser.nextInt() > 0);
+ position.setFixTime(position.getDeviceTime());
+
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
+
+ } else {
+
+ getLastLocation(position, position.getDeviceTime());
+
+ }
return position;
}
diff --git a/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java b/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java
index 7bde85f87..379b610e1 100644
--- a/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MxtProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -97,6 +97,10 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder {
long date = buf.readUnsignedIntLE();
long days = BitUtil.from(date, 6 + 6 + 5);
+ if (days < 7 * 780) {
+ days += 7 * 1024;
+ }
+
long hours = BitUtil.between(date, 6 + 6, 6 + 6 + 5);
long minutes = BitUtil.between(date, 6, 6 + 6);
long seconds = BitUtil.to(date, 6);
diff --git a/src/main/java/org/traccar/protocol/NavtelecomFrameDecoder.java b/src/main/java/org/traccar/protocol/NavtelecomFrameDecoder.java
new file mode 100644
index 000000000..0fb82528b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NavtelecomFrameDecoder.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021 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.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.BasePipelineFactory;
+
+import java.nio.charset.StandardCharsets;
+import java.util.BitSet;
+
+public class NavtelecomFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.getByte(buf.readerIndex()) == '@') {
+
+ int length = buf.getUnsignedShortLE(12) + 12 + 2 + 2;
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ } else {
+
+ NavtelecomProtocolDecoder protocolDecoder =
+ BasePipelineFactory.getHandler(ctx.pipeline(), NavtelecomProtocolDecoder.class);
+ if (protocolDecoder == null) {
+ throw new RuntimeException("Decoder not found");
+ }
+
+ String type = buf.getCharSequence(buf.readerIndex(), 2, StandardCharsets.US_ASCII).toString();
+ BitSet bits = protocolDecoder.getBits();
+
+ if (type.equals("~A")) {
+ int count = buf.getUnsignedByte(buf.readerIndex() + 2);
+ int length = 2 + 1 + 1;
+
+ for (int i = 0; i < count; i++) {
+ for (int j = 0; j < bits.length(); j++) {
+ if (bits.get(j)) {
+ length += NavtelecomProtocolDecoder.getItemLength(j + 1);
+ }
+ }
+ }
+
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+ } else {
+ throw new UnsupportedOperationException("Unsupported message type: " + type);
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
index c76b42768..29ce8c41e 100644
--- a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
+++ b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
@@ -15,7 +15,6 @@
*/
package org.traccar.protocol;
-import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
@@ -26,8 +25,8 @@ public class NavtelecomProtocol extends BaseProtocol {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new LengthFieldBasedFrameDecoder(256, 2, 1, 2, 0));
- pipeline.addLast(new Gt02ProtocolDecoder(NavtelecomProtocol.this));
+ pipeline.addLast(new NavtelecomFrameDecoder());
+ pipeline.addLast(new NavtelecomProtocolDecoder(NavtelecomProtocol.this));
}
});
}
diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
index 2362b1870..bdcc12c4c 100644
--- a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java
@@ -19,12 +19,22 @@ 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.NetworkMessage;
import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.util.BitSet;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
@@ -32,36 +42,210 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+ private static final Map<Integer, Integer> ITEM_LENGTH_MAP = new HashMap<>();
- ByteBuf buf = (ByteBuf) msg;
-
- buf.skipBytes(4); // preamble
- int receiver = buf.readIntLE();
- int sender = buf.readIntLE();
- int length = buf.readUnsignedShortLE();
- buf.readUnsignedByte(); // data checksum
- buf.readUnsignedByte(); // header checksum
+ static {
+ int[] l1 = {
+ 4, 5, 6, 7, 8, 29, 30, 31, 32, 45, 46, 47, 48, 49, 50, 51, 52, 56, 63, 64, 65, 69, 72, 78, 79, 80, 81,
+ 82, 83, 98, 99, 101, 104, 118, 122, 123, 124, 125, 126, 139, 140, 144, 145, 167, 168, 169, 170, 199,
+ 202, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222
+ };
+ int[] l2 = {
+ 2, 14, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 35, 36, 38, 39, 40, 41, 42, 43, 44, 53, 55, 58,
+ 59, 60, 61, 62, 66, 68, 71, 75, 100, 106, 108, 110, 111, 112, 113, 114, 115, 116, 117, 119, 120, 121,
+ 133, 134, 135, 136, 137, 138, 141, 143, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 171, 175, 177, 178, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 200, 201, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237
+ };
+ int[] l3 = {
+ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 142, 146, 198
+ };
+ int[] l4 = {
+ 1, 3, 9, 10, 11, 12, 13, 15, 16, 33, 34, 37, 54, 57, 67, 74, 76, 102, 103, 105, 127, 128, 129, 130, 131,
+ 132, 172, 173, 174, 176, 179, 193, 194, 195, 196, 203, 205, 206, 238, 239, 240, 241, 242, 243, 244, 245,
+ 246, 247, 248, 249, 250, 251, 252
+ };
+ for (int i : l1) {
+ ITEM_LENGTH_MAP.put(i, 1);
+ }
+ for (int i : l2) {
+ ITEM_LENGTH_MAP.put(i, 2);
+ }
+ for (int i : l3) {
+ ITEM_LENGTH_MAP.put(i, 3);
+ }
+ for (int i : l4) {
+ ITEM_LENGTH_MAP.put(i, 4);
+ }
+ ITEM_LENGTH_MAP.put(70, 8);
+ ITEM_LENGTH_MAP.put(73, 16);
+ ITEM_LENGTH_MAP.put(77, 37);
+ ITEM_LENGTH_MAP.put(94, 6);
+ ITEM_LENGTH_MAP.put(95, 12);
+ ITEM_LENGTH_MAP.put(96, 24);
+ ITEM_LENGTH_MAP.put(97, 48);
+ ITEM_LENGTH_MAP.put(107, 6);
+ ITEM_LENGTH_MAP.put(109, 6);
+ ITEM_LENGTH_MAP.put(197, 6);
+ ITEM_LENGTH_MAP.put(204, 5);
+ ITEM_LENGTH_MAP.put(253, 8);
+ ITEM_LENGTH_MAP.put(254, 8);
+ ITEM_LENGTH_MAP.put(255, 8);
+ }
- String sentence = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString();
+ private BitSet bits;
- if (sentence.startsWith("*>S")) {
+ public static int getItemLength(int id) {
+ Integer length = ITEM_LENGTH_MAP.get(id);
+ if (length == null) {
+ throw new IllegalArgumentException(String.format("Unknown item: %d", id));
+ }
+ return length;
+ }
- String data = "*<S";
+ public BitSet getBits() {
+ return bits;
+ }
+ private void sendResponse(
+ Channel channel, SocketAddress remoteAddress, int receiver, int sender, ByteBuf content) {
+ if (channel != null) {
ByteBuf response = Unpooled.buffer();
response.writeCharSequence("@NTC", StandardCharsets.US_ASCII);
response.writeIntLE(sender);
response.writeIntLE(receiver);
- response.writeShortLE(data.length());
- response.writeByte(Checksum.xor(data));
+ response.writeShortLE(content.readableBytes());
+ response.writeByte(Checksum.xor(content.nioBuffer()));
response.writeByte(Checksum.xor(response.nioBuffer()));
- response.writeCharSequence(data, StandardCharsets.US_ASCII);
+ response.writeBytes(content);
+ content.release();
+
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ if (buf.getByte(buf.readerIndex()) == '@') {
+
+ buf.skipBytes(4); // preamble
+ int receiver = buf.readIntLE();
+ int sender = buf.readIntLE();
+ int length = buf.readUnsignedShortLE();
+ buf.readUnsignedByte(); // data checksum
+ buf.readUnsignedByte(); // header checksum
+
+ String type = buf.toString(buf.readerIndex(), 6, StandardCharsets.US_ASCII);
+
+ if (type.startsWith("*>S")) {
+
+ String sentence = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString();
+ getDeviceSession(channel, remoteAddress, sentence.substring(4));
+
+ ByteBuf payload = Unpooled.copiedBuffer("*<S", StandardCharsets.US_ASCII);
+
+ sendResponse(channel, remoteAddress, receiver, sender, payload);
+
+ } else if (type.startsWith("*>FLEX")) {
+
+ buf.skipBytes(6);
+
+ ByteBuf payload = Unpooled.buffer();
+ payload.writeCharSequence("*<FLEX", StandardCharsets.US_ASCII);
+ payload.writeByte(buf.readUnsignedByte()); // protocol
+ payload.writeByte(buf.readUnsignedByte()); // protocol version
+ payload.writeByte(buf.readUnsignedByte()); // struct version
+
+ int bitCount = buf.readUnsignedByte();
+ bits = new BitSet((bitCount + 7) / 8);
+
+ int currentByte = 0;
+ for (int i = 0; i < bitCount; i++) {
+ if (i % 8 == 0) {
+ currentByte = buf.readUnsignedByte();
+ }
+ bits.set(i, BitUtil.check(currentByte, 7 - i % 8));
+ }
+
+ sendResponse(channel, remoteAddress, receiver, sender, payload);
+
+ }
+
+ } else {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ String type = buf.readCharSequence(2, StandardCharsets.US_ASCII).toString();
+
+ if (type.equals("~A")) {
+
+ int count = buf.readUnsignedByte();
+ List<Position> positions = new LinkedList<>();
+
+ for (int i = 0; i < count; i++) {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ for (int j = 0; j < bits.length(); j++) {
+ if (bits.get(j)) {
+ switch (j + 1) {
+ case 1:
+ position.set(Position.KEY_INDEX, buf.readUnsignedIntLE());
+ break;
+ case 2:
+ position.set(Position.KEY_EVENT, buf.readUnsignedShortLE());
+ break;
+ case 3:
+ position.setDeviceTime(new Date(buf.readUnsignedIntLE() * 1000));
+ break;
+ case 9:
+ position.setValid(true);
+ position.setFixTime(new Date(buf.readUnsignedIntLE() * 1000));
+ break;
+ case 10:
+ position.setLatitude(buf.readIntLE() * 0.0001 / 60);
+ break;
+ case 11:
+ position.setLongitude(buf.readIntLE() * 0.0001 / 60);
+ break;
+ case 12:
+ position.setAltitude(buf.readIntLE() * 0.1);
+ break;
+ case 13:
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readFloatLE()));
+ break;
+ default:
+ buf.skipBytes(getItemLength(j + 1));
+ break;
+ }
+ }
+ }
+
+ if (position.getFixTime() == null) {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ positions.add(position);
+ }
+
+ int checksum = buf.readUnsignedByte();
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeCharSequence(type, StandardCharsets.US_ASCII);
+ response.writeByte(count);
+ response.writeByte(checksum);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ return positions;
- if (channel != null) {
- channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
diff --git a/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
index c9044fa2b..b5d34a029 100644
--- a/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
@@ -138,6 +138,21 @@ public class PacificTrackProtocolDecoder extends BaseProtocolDecoder {
case 0b01011:
position.set("barometer", buf.readUnsignedByte() * 0.5);
break;
+ case 0b01100:
+ position.set("intakeManifoldTemp", buf.readUnsignedByte() - 40);
+ break;
+ case 0b01101:
+ position.set("fuelTankTemp", buf.readUnsignedByte() - 40);
+ break;
+ case 0b01110:
+ position.set("intercoolerTemp", buf.readUnsignedByte() - 40);
+ break;
+ case 0b01111:
+ position.set("turboOilTemp", buf.readUnsignedByte() - 40);
+ break;
+ case 0b10000:
+ position.set("transOilTemp", buf.readUnsignedByte() - 40);
+ break;
default:
buf.readUnsignedByte();
break;
diff --git a/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java b/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java
index ebb93abd6..e1847a2b2 100644
--- a/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PortmanProtocolDecoder.java
@@ -80,7 +80,15 @@ public class PortmanProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_TEMP + 1, parser.next());
position.set(Position.KEY_STATUS, parser.nextHexLong());
position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
- position.set(Position.KEY_EVENT, parser.nextInt());
+
+ int event = parser.nextInt();
+ position.set(Position.KEY_EVENT, event);
+ if (event == 253) {
+ position.set(Position.KEY_IGNITION, true);
+ } else if (event == 254) {
+ position.set(Position.KEY_IGNITION, false);
+ }
+
position.set(Position.KEY_SATELLITES, parser.nextInt());
position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
position.set(Position.KEY_RSSI, parser.nextInt());
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocol.java b/src/main/java/org/traccar/protocol/RuptelaProtocol.java
index b8f72336b..5d1f86553 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocol.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@ public class RuptelaProtocol extends BaseProtocol {
public RuptelaProtocol() {
setSupportedDataCommands(
Command.TYPE_CUSTOM,
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_ENGINE_RESUME,
Command.TYPE_REQUEST_PHOTO,
Command.TYPE_CONFIGURATION,
Command.TYPE_GET_VERSION,
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
index 5c2885a8b..2812d22ff 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
@@ -102,6 +102,12 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
case 5:
position.set(Position.KEY_IGNITION, readValue(buf, length, false) == 1);
break;
+ case 29:
+ position.set(Position.KEY_POWER, readValue(buf, length, false));
+ break;
+ case 30:
+ position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001);
+ break;
case 74:
position.set(Position.PREFIX_TEMP + 3, readValue(buf, length, true) * 0.1);
break;
@@ -110,22 +116,19 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
case 80:
position.set(Position.PREFIX_TEMP + (id - 78), readValue(buf, length, true) * 0.1);
break;
- case 198:
- if (readValue(buf, length, false) > 0) {
- position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
- }
- break;
- case 199:
- case 200:
+ case 134:
if (readValue(buf, length, false) > 0) {
position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
}
break;
- case 201:
+ case 136:
if (readValue(buf, length, false) > 0) {
position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
}
break;
+ case 197:
+ position.set(Position.KEY_RPM, readValue(buf, length, false) * 0.125);
+ break;
default:
position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
break;
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
index fb0dcf690..442961b19 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2021 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.
@@ -58,6 +58,12 @@ public class RuptelaProtocolEncoder extends BaseProtocolEncoder {
content.writeBytes(data.getBytes(StandardCharsets.US_ASCII));
return encodeContent(RuptelaProtocolDecoder.MSG_SMS_VIA_GPRS, content);
}
+ case Command.TYPE_ENGINE_STOP:
+ content.writeBytes("pass immobilizer 10".getBytes(StandardCharsets.US_ASCII));
+ return encodeContent(RuptelaProtocolDecoder.MSG_SMS_VIA_GPRS, content);
+ case Command.TYPE_ENGINE_RESUME:
+ content.writeBytes("pass resetimmob".getBytes(StandardCharsets.US_ASCII));
+ return encodeContent(RuptelaProtocolDecoder.MSG_SMS_VIA_GPRS, content);
case Command.TYPE_REQUEST_PHOTO:
content.writeByte(1); // sub-command
content.writeByte(0); // source
diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
index 82f0e4061..7a6b6f4fe 100644
--- a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
@@ -150,9 +150,21 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
continue;
}
switch (dataTags[i]) {
+ case "#ALT#":
+ case "#ALTD#":
+ position.setAltitude(Double.parseDouble(data[i]));
+ break;
+ case "#DAL#":
+ case "#DID#":
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, data[i]);
+ break;
case "#EDT#":
position.setDeviceTime(dateFormat.parse(data[i]));
break;
+ case "#EDV1#":
+ case "#EDV2#":
+ position.set("external" + dataTags[i].charAt(4), data[i]);
+ break;
case "#EID#":
event = Integer.parseInt(data[i]);
position.set(Position.KEY_ALARM, decodeAlarm(event));
@@ -166,6 +178,9 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
case "#EDSC#":
position.set("reason", data[i]);
break;
+ case "#IARM#":
+ position.set(Position.KEY_ARMED, Integer.parseInt(data[i]) > 0);
+ break;
case "#PDT#":
position.setFixTime(dateFormat.parse(data[i]));
break;
@@ -185,11 +200,15 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(Integer.parseInt(data[i]));
break;
case "#ODO#":
+ case "#ODOD#":
position.set(Position.KEY_ODOMETER, (long) (Double.parseDouble(data[i]) * 1000));
break;
case "#BATC#":
position.set(Position.KEY_BATTERY_LEVEL, Integer.parseInt(data[i]));
break;
+ case "#BATH#":
+ position.set("batteryHealth", Integer.parseInt(data[i]));
+ break;
case "#TVI#":
position.set(Position.KEY_DEVICE_TEMP, Double.parseDouble(data[i]));
break;
@@ -217,6 +236,9 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
case "#OUTD#":
position.set(Position.PREFIX_OUT + (dataTags[i].charAt(4) - 'A' + 1), Integer.parseInt(data[i]));
break;
+ case "#PDOP#":
+ position.set(Position.KEY_PDOP, Double.parseDouble(data[i]));
+ break;
case "#LAC#":
if (!data[i].isEmpty()) {
lac = Integer.parseInt(data[i]);
@@ -241,18 +263,23 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
break;
case "#IGN#":
case "#IGNL#":
- position.set(Position.KEY_IGNITION, data[i].equals("1"));
- break;
case "#ENG#":
- position.set("engine", data[i].equals("1"));
+ position.set(Position.KEY_IGNITION, Integer.parseInt(data[i]) > 0);
break;
case "#DUR#":
case "#TDUR#":
position.set(Position.KEY_HOURS, Integer.parseInt(data[i]));
break;
+ case "#SAT#":
+ case "#SATN#":
+ position.set(Position.KEY_SATELLITES_VISIBLE, Integer.parseInt(data[i]));
+ break;
case "#SATU#":
position.set(Position.KEY_SATELLITES, Integer.parseInt(data[i]));
break;
+ case "#STRT#":
+ position.set("starter", Double.parseDouble(data[i]));
+ break;
case "#TS1#":
position.set("sensor1State", Integer.parseInt(data[i]));
break;
diff --git a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
index 1cc69c6e6..042518cb2 100644
--- a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
@@ -41,6 +41,11 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
.expression(".") // index
.number("d+,") // length
.number("(d+),") // imei
+ .expression("(.+)") // content
+ .number("xx") // checksum
+ .compile();
+
+ private static final Pattern PATTERN_POSITION = new PatternBuilder()
.number("xxx,") // command
.number("(d+),") // event
.expression("([^,]+)?,") // event data
@@ -64,12 +69,18 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
.number("(x+),") // inputs
.number("(x+),") // outputs
.number("(x+)|") // power
- .number("(x+)|") // battery
- .expression("([^,]+),") // adc
+ .number("(x+)") // battery
+ .groupBegin()
+ .text("|")
+ .expression("([^,]+)").optional() // adc
+ .groupBegin()
+ .text(",")
.number("d,") // extended
.expression("([^,]+)?,") // fuel
- .expression("([^,]+)?") // temperature
- .number("xx") // checksum
+ .expression("([^,]+)?,?") // temperature
+ .groupEnd("?")
+ .groupEnd("?")
+ .any()
.compile();
private String decodeAlarm(int value) {
@@ -102,6 +113,32 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ String content = parser.next();
+ if (content.length() < 100) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_RESULT, content);
+
+ return position;
+
+ } else {
+
+ return decodePosition(deviceSession, content);
+
+ }
+ }
+
+ protected Object decodePosition(DeviceSession deviceSession, String content) throws Exception {
+
+ Parser parser = new Parser(PATTERN_POSITION, content);
+ if (!parser.matches()) {
+ return null;
+ }
+
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -132,15 +169,21 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), parser.nextInt())));
position.set(Position.KEY_STATUS, parser.nextHexInt());
- 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_IGNITION, BitUtil.check(input, 1));
+ position.set(Position.KEY_INPUT, input);
+ position.set(Position.KEY_OUTPUT, output);
position.set(Position.KEY_POWER, parser.nextHexInt() * 0.01);
position.set(Position.KEY_BATTERY, parser.nextHexInt() * 0.01);
- String[] adc = parser.next().split("\\|");
- for (int i = 0; i < adc.length; i++) {
- position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(adc[i], 16) * 0.01);
+ if (parser.hasNext()) {
+ String[] adc = parser.next().split("\\|");
+ for (int i = 0; i < adc.length; i++) {
+ position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(adc[i], 16) * 0.01);
+ }
}
if (parser.hasNext()) {
diff --git a/src/main/java/org/traccar/protocol/StbProtocol.java b/src/main/java/org/traccar/protocol/StbProtocol.java
new file mode 100644
index 000000000..002ed86c7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/StbProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 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 StbProtocol extends BaseProtocol {
+
+ public StbProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new JsonFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new StbProtocolDecoder(StbProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/StbProtocolDecoder.java b/src/main/java/org/traccar/protocol/StbProtocolDecoder.java
new file mode 100644
index 000000000..cc985d605
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/StbProtocolDecoder.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2021 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.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.model.Position;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.util.Date;
+
+public class StbProtocolDecoder extends BaseProtocolDecoder {
+
+ public StbProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN = 110;
+ public static final int MSG_PROPERTY = 310;
+ public static final int MSG_ALARM = 410;
+
+ public static class Response {
+ @JsonProperty("msgType")
+ private int type;
+ @JsonProperty("devId")
+ private String deviceId;
+ @JsonProperty("result")
+ private int result;
+ @JsonProperty("txnNo")
+ private String transaction;
+ }
+
+ private void sendResponse(
+ Channel channel, SocketAddress remoteAddress, int type, String deviceId, JsonObject root)
+ throws JsonProcessingException {
+
+ Response response = new Response();
+ response.type = type + 1;
+ response.deviceId = deviceId;
+ response.result = 1;
+ response.transaction = root.getString("txnNo");
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(
+ Context.getObjectMapper().writeValueAsString(response), remoteAddress));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ JsonObject root = Json.createReader(new StringReader((String) msg)).readObject();
+ int type = root.getInt("msgType");
+ String deviceId = root.getString("devId");
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ sendResponse(channel, remoteAddress, type, deviceId, root);
+
+ if (type == MSG_PROPERTY || type == MSG_ALARM) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (type == MSG_PROPERTY) {
+ int locationType = 0;
+ for (JsonValue property : root.getJsonArray("attrList")) {
+ JsonObject propertyObject = property.asJsonObject();
+ String id = propertyObject.getString("id");
+ switch (id) {
+ case "01101001":
+ locationType = Integer.parseInt(propertyObject.getString("value"));
+ break;
+ case "01102001":
+ position.setLongitude(
+ Double.parseDouble(propertyObject.getString("value")));
+ break;
+ case "01103001":
+ position.setLatitude(
+ Double.parseDouble(propertyObject.getString("value")));
+ break;
+ case "01118001":
+ position.set(
+ Position.KEY_DEVICE_TEMP, Double.parseDouble(propertyObject.getString("value")));
+ break;
+ case "01122001":
+ position.set(
+ "batteryControl", Integer.parseInt(propertyObject.getString("value")));
+ break;
+ case "02301001":
+ position.set(
+ "switchCabinetCommand", Integer.parseInt(propertyObject.getString("value")));
+ break;
+ default:
+ String key = "id" + id;
+ if (propertyObject.containsKey("doorId")) {
+ key += "Door" + propertyObject.getString("doorId");
+ }
+ position.set(key, propertyObject.getString("value"));
+ break;
+ }
+ }
+ if (locationType > 0) {
+ position.setTime(new Date());
+ position.setValid(locationType != 5);
+ if (locationType == 2 || locationType == 4) {
+ position.setLongitude(-position.getLongitude());
+ }
+ if (locationType == 3 || locationType == 4) {
+ position.setLatitude(-position.getLatitude());
+ }
+ } else {
+ getLastLocation(position, null);
+ }
+ }
+
+ return position;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
index d8710a899..2d00ea81e 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2021 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.
@@ -161,7 +161,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
case 7:
return Position.ALARM_MOVEMENT;
case 8:
- return Position.ALARM_SHOCK;
+ return Position.ALARM_VIBRATION;
default:
return null;
}
@@ -178,7 +178,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
case 14:
return Position.ALARM_LOW_BATTERY;
case 15:
- return Position.ALARM_SHOCK;
+ return Position.ALARM_VIBRATION;
case 16:
return Position.ALARM_ACCIDENT;
case 40:
@@ -448,7 +448,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
String type = values[index++];
- if (!type.equals("STT") && !type.equals("ALT")) {
+ if (!type.equals("STT") && !type.equals("ALT") && !type.equals("BLE")) {
return null;
}
@@ -461,7 +461,12 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_TYPE, type);
- int mask = Integer.parseInt(values[index++], 16);
+ int mask;
+ if (type.equals("BLE")) {
+ mask = 0b1100000110110;
+ } else {
+ mask = Integer.parseInt(values[index++], 16);
+ }
if (BitUtil.check(mask, 1)) {
index += 1; // model
@@ -510,63 +515,83 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.setLongitude(Double.parseDouble(values[index++]));
}
- if (BitUtil.check(mask, 13)) {
- position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
- }
+ if (type.equals("BLE")) {
- if (BitUtil.check(mask, 14)) {
- position.setCourse(Double.parseDouble(values[index++]));
- }
+ position.setValid(true);
- if (BitUtil.check(mask, 15)) {
- position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
- }
+ int count = Integer.parseInt(values[index++]);
- if (BitUtil.check(mask, 16)) {
- position.setValid(values[index++].equals("1"));
- }
+ for (int i = 1; i <= count; i++) {
+ position.set("tag" + i + "Rssi", Integer.parseInt(values[index++]));
+ index += 1; // rssi min
+ index += 1; // rssi max
+ position.set("tag" + i + "Id", values[index++]);
+ position.set("tag" + i + "Samples", Integer.parseInt(values[index++]));
+ position.set("tag" + i + "Major", Integer.parseInt(values[index++]));
+ position.set("tag" + i + "Minor", Integer.parseInt(values[index++]));
+ }
- if (BitUtil.check(mask, 17)) {
- position.set(Position.KEY_INPUT, Integer.parseInt(values[index++]));
- }
+ } else {
- if (BitUtil.check(mask, 18)) {
- position.set(Position.KEY_OUTPUT, Integer.parseInt(values[index++]));
- }
+ if (BitUtil.check(mask, 13)) {
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ }
- if (type.equals("ALT")) {
- if (BitUtil.check(mask, 19)) {
- position.set("alertId", values[index++]);
+ if (BitUtil.check(mask, 14)) {
+ position.setCourse(Double.parseDouble(values[index++]));
}
- if (BitUtil.check(mask, 20)) {
- position.set("alertModifier", values[index++]);
+
+ if (BitUtil.check(mask, 15)) {
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
}
- if (BitUtil.check(mask, 21)) {
- position.set("alertData", values[index++]);
+
+ if (BitUtil.check(mask, 16)) {
+ position.setValid(values[index++].equals("1"));
}
- } else {
- if (BitUtil.check(mask, 19)) {
- position.set("mode", Integer.parseInt(values[index++]));
+
+ if (BitUtil.check(mask, 17)) {
+ position.set(Position.KEY_INPUT, Integer.parseInt(values[index++]));
}
- if (BitUtil.check(mask, 20)) {
- position.set("reason", Integer.parseInt(values[index++]));
+
+ if (BitUtil.check(mask, 18)) {
+ position.set(Position.KEY_OUTPUT, Integer.parseInt(values[index++]));
}
- if (BitUtil.check(mask, 21)) {
- position.set(Position.KEY_INDEX, Integer.parseInt(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)) {
- index += 1; // reserved
- }
+ if (BitUtil.check(mask, 22)) {
+ index += 1; // reserved
+ }
- if (BitUtil.check(mask, 23)) {
- int assignMask = Integer.parseInt(values[index++], 16);
- for (int i = 0; i <= 30; i++) {
- if (BitUtil.check(assignMask, i)) {
- position.set(Position.PREFIX_IO + (i + 1), values[index++]);
+ if (BitUtil.check(mask, 23)) {
+ int assignMask = Integer.parseInt(values[index++], 16);
+ for (int i = 0; i <= 30; i++) {
+ if (BitUtil.check(assignMask, i)) {
+ position.set(Position.PREFIX_IO + (i + 1), values[index++]);
+ }
}
}
+
}
return position;
diff --git a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
index b23e3fa70..b15688df0 100644
--- a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
@@ -54,8 +54,12 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_GPS = 0x02;
public static final int MSG_HEARTBEAT = 0x03;
public static final int MSG_ALARM = 0x04;
- public static final int MSG_NETWORK = 0x05;
+ public static final int MSG_NETWORK = 0x05; // 0x2727
+ public static final int MSG_DRIVER_BEHAVIOR_1 = 0x05; // 0x2626
+ public static final int MSG_DRIVER_BEHAVIOR_2 = 0x06; // 0x2626
public static final int MSG_BLE = 0x10;
+ public static final int MSG_GPS_2 = 0x13;
+ public static final int MSG_ALARM_2 = 0x14;
public static final int MSG_COMMAND = 0x81;
private void sendResponse(Channel channel, short header, int type, int index, ByteBuf imei, int alarm) {
@@ -73,7 +77,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
}
}
- private String decodeAlarm(int value) {
+ private String decodeAlarm1(int value) {
switch (value) {
case 1:
return Position.ALARM_POWER_CUT;
@@ -103,6 +107,28 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private String decodeAlarm2(int value) {
+ switch (value) {
+ case 1:
+ case 4:
+ return Position.ALARM_REMOVING;
+ case 2:
+ return Position.ALARM_TAMPERING;
+ case 3:
+ return Position.ALARM_SOS;
+ case 5:
+ return Position.ALARM_FALL_DOWN;
+ case 6:
+ return Position.ALARM_LOW_BATTERY;
+ case 14:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 15:
+ return Position.ALARM_GEOFENCE_EXIT;
+ default:
+ return null;
+ }
+ }
+
private Date readDate(ByteBuf buf) {
return new DateBuilder()
.setYear(BcdUtil.readInteger(buf, 2))
@@ -132,15 +158,16 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- if (type != MSG_GPS && type != MSG_ALARM) {
+ boolean positionType = type == MSG_GPS || type == MSG_GPS_2 || type == MSG_ALARM || type == MSG_ALARM_2;
+ if (!positionType) {
sendResponse(channel, header, type, index, imei, 0);
}
- if (type == MSG_GPS || type == MSG_ALARM) {
+ if (positionType) {
return decodePosition(channel, deviceSession, buf, type, index, imei);
- } else if (type == MSG_NETWORK) {
+ } else if (type == MSG_NETWORK && header == 0x2727) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -159,6 +186,52 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if ((type == MSG_DRIVER_BEHAVIOR_1 || type == MSG_DRIVER_BEHAVIOR_2) && header == 0x2626) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ switch (buf.readUnsignedByte()) {
+ case 0:
+ case 4:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 1:
+ case 3:
+ case 5:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 2:
+ if (type == MSG_DRIVER_BEHAVIOR_1) {
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ } else {
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ }
+ break;
+ default:
+ break;
+ }
+
+ position.setTime(readDate(buf));
+
+ if (type == MSG_DRIVER_BEHAVIOR_2) {
+ int status = buf.readUnsignedByte();
+ position.setValid(!BitUtil.check(status, 7));
+ buf.skipBytes(5); // acceleration
+ } else {
+ position.setValid(true);
+ }
+
+ position.setAltitude(buf.readFloatLE());
+ position.setLongitude(buf.readFloatLE());
+ position.setLatitude(buf.readFloatLE());
+ position.setSpeed(UnitsConverter.knotsFromKph(BcdUtil.readInteger(buf, 4) * 0.1));
+ position.setCourse(buf.readUnsignedShort());
+
+ position.set(Position.KEY_RPM, buf.readUnsignedShort());
+
+ return position;
+
} else if (type == MSG_BLE) {
return decodeBle(channel, deviceSession, buf, type, index, imei);
@@ -181,6 +254,11 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ private double decodeBleTemp(ByteBuf buf) {
+ int value = buf.readUnsignedShort();
+ return (BitUtil.check(value, 15) ? -BitUtil.to(value, 15) : BitUtil.to(value, 15)) * 0.01;
+ }
+
private Position decodeBle(
Channel channel, DeviceSession deviceSession, ByteBuf buf, int type, int index, ByteBuf imei) {
@@ -230,7 +308,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
position.set("tag" + i + "Id", ByteBufUtil.hexDump(buf.readSlice(6)));
position.set("tag" + i + "Battery", buf.readUnsignedByte() * 0.01 + 2);
buf.readUnsignedByte(); // battery level
- position.set("tag" + i + "Temp", buf.readUnsignedShort() * 0.01);
+ position.set("tag" + i + "Temp", decodeBleTemp(buf));
position.set("tag" + i + "Humidity", buf.readUnsignedShort() * 0.01);
position.set("tag" + i + "LightSensor", buf.readUnsignedShort());
position.set("tag" + i + "Rssi", buf.readUnsignedByte() - 128);
@@ -239,7 +317,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
position.set("tag" + i + "Id", ByteBufUtil.hexDump(buf.readSlice(6)));
position.set("tag" + i + "Battery", buf.readUnsignedByte() * 0.01 + 2);
buf.readUnsignedByte(); // battery level
- position.set("tag" + i + "Temp", buf.readUnsignedShort() * 0.01);
+ position.set("tag" + i + "Temp", decodeBleTemp(buf));
position.set("tag" + i + "Door", buf.readUnsignedByte() > 0);
position.set("tag" + i + "Rssi", buf.readUnsignedByte() - 128);
break;
@@ -295,12 +373,19 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
position.set("ac", BitUtil.check(io, 13));
position.set(Position.PREFIX_IN + 3, BitUtil.check(io, 12));
position.set(Position.PREFIX_IN + 4, BitUtil.check(io, 11));
- position.set(Position.PREFIX_OUT + 1, BitUtil.check(io, 7));
- position.set(Position.PREFIX_OUT + 2, BitUtil.check(io, 8));
- position.set(Position.PREFIX_OUT + 3, BitUtil.check(io, 9));
+
+ if (type == MSG_GPS_2 || type == MSG_ALARM_2) {
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
+ buf.readUnsignedByte(); // reserved
+ } else {
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(io, 7));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(io, 8));
+ position.set(Position.PREFIX_OUT + 3, BitUtil.check(io, 9));
+ }
if (header != 0x2626) {
- for (int i = 1; i <= 2; i++) {
+ int adcCount = type == MSG_GPS_2 || type == MSG_ALARM_2 ? 5 : 2;
+ for (int i = 1; i <= adcCount; i++) {
String value = ByteBufUtil.hexDump(buf.readSlice(2));
if (!value.equals("ffff")) {
position.set(Position.PREFIX_ADC + i, Integer.parseInt(value) * 0.01);
@@ -311,7 +396,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
}
int alarm = buf.readUnsignedByte();
- position.set(Position.KEY_ALARM, decodeAlarm(alarm));
+ position.set(Position.KEY_ALARM, header != 0x2727 ? decodeAlarm1(alarm) : decodeAlarm2(alarm));
if (header != 0x2727) {
@@ -389,13 +474,45 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShort(); // distance upload interval
buf.readUnsignedByte(); // heartbeat
- } else if (buf.readableBytes() >= 2) {
-
- position.set(Position.KEY_POWER, BcdUtil.readInteger(buf, 4) * 0.01);
+ } else {
+ if (buf.readableBytes() >= 2) {
+ position.set(Position.KEY_POWER, BcdUtil.readInteger(buf, 4) * 0.01);
+ }
+ if (buf.readableBytes() >= 19) {
+ position.set(Position.KEY_OBD_SPEED, BcdUtil.readInteger(buf, 4) * 0.01);
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedInt() * 0.001);
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedInt() * 0.001);
+ position.set(Position.KEY_RPM, buf.readUnsignedShort());
+ int value;
+ value = buf.readUnsignedByte();
+ if (value != 0xff) {
+ position.set("airInput", value);
+ }
+ if (value != 0xff) {
+ position.set("airPressure", value);
+ }
+ if (value != 0xff) {
+ position.set(Position.KEY_COOLANT_TEMP, value - 40);
+ }
+ if (value != 0xff) {
+ position.set("airTemp", value - 40);
+ }
+ if (value != 0xff) {
+ position.set(Position.KEY_ENGINE_LOAD, value);
+ }
+ if (value != 0xff) {
+ position.set(Position.KEY_THROTTLE, value);
+ }
+ if (value != 0xff) {
+ position.set(Position.KEY_FUEL_LEVEL, value);
+ }
+ }
}
- sendResponse(channel, header, type, index, imei, alarm);
+ if (type == MSG_ALARM || type == MSG_ALARM_2) {
+ sendResponse(channel, header, type, index, imei, alarm);
+ }
return position;
}
diff --git a/src/main/java/org/traccar/protocol/TechtoCruzFrameDecoder.java b/src/main/java/org/traccar/protocol/TechtoCruzFrameDecoder.java
new file mode 100644
index 000000000..8b9152e8b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TechtoCruzFrameDecoder.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 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.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+import java.nio.charset.StandardCharsets;
+
+public class TechtoCruzFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ int lengthStart = buf.readerIndex() + 3;
+ int lengthEnd = buf.indexOf(lengthStart, buf.writerIndex(), (byte) ',');
+ if (lengthEnd > 0) {
+ int length = lengthStart
+ + Integer.parseInt(buf.toString(lengthStart, lengthEnd - lengthStart, StandardCharsets.US_ASCII));
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java b/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java
new file mode 100644
index 000000000..a217ea738
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TechtoCruzProtocol.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021 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 TechtoCruzProtocol extends BaseProtocol {
+
+ public TechtoCruzProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new TechtoCruzFrameDecoder());
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new TechtoCruzProtocolDecoder(TechtoCruzProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java b/src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java
new file mode 100644
index 000000000..6b9f0edb6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TechtoCruzProtocolDecoder.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2021 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.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class TechtoCruzProtocolDecoder extends BaseProtocolDecoder {
+
+ public TechtoCruzProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("$$A")
+ .number("d+,") // length
+ .number("(d+),") // imei
+ .number("(dd)(dd)(dd)") // date (yymmdd)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .expression("([AV]),") // valid
+ .expression("[^,]+,") // manufacturer
+ .expression("([^,]+),") // license plate
+ .number("(d+.d+),") // speed
+ .number("(d+),") // odometer
+ .number("(-?d+.d+),[NS],") // latitude
+ .number("(-?d+.d+),[WE],") // longitude
+ .number("(-?d+.d+),") // altitude
+ .number("(d+.d+),") // course
+ .number("(d+),") // satellites
+ .number("(d+),") // rssi
+ .number("(d+.d+),") // power
+ .number("(d+.d+),") // battery
+ .number("([01]),") // charge
+ .number("[01],") // speed sensor
+ .number("[01],") // gps status
+ .number("([01]),") // ignition
+ .number("([01]),") // overspeed
+ .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());
+ position.setValid(parser.next().equals("A"));
+
+ position.set("registration", parser.next());
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setAltitude(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_CHARGE, parser.nextInt() > 0);
+ position.set(Position.KEY_IGNITION, parser.nextInt() > 0);
+
+ if (parser.nextInt() > 0) {
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 6ba183f9b..89ae48b3a 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -235,9 +235,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
case 67:
position.set(Position.KEY_BATTERY, readValue(buf, length, false) * 0.001);
break;
- case 69:
- position.set("gpsStatus", readValue(buf, length, false));
- break;
case 72:
case 73:
case 74:
@@ -258,16 +255,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
case 115:
position.set(Position.KEY_COOLANT_TEMP, readValue(buf, length, true) * 0.1);
break;
- case 129:
- case 130:
- case 131:
- case 132:
- case 133:
- case 134:
- String driver = id == 129 || id == 132 ? "" : position.getString("driver1");
- position.set("driver" + (id >= 132 ? 2 : 1),
- driver + buf.readSlice(length).toString(StandardCharsets.US_ASCII).trim());
- break;
case 179:
position.set(Position.PREFIX_OUT + 1, readValue(buf, length, false) == 1);
break;
@@ -312,11 +299,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
break;
}
break;
- case 389:
- if (BitUtil.between(readValue(buf, length, false), 4, 8) == 1) {
- position.set(Position.KEY_ALARM, Position.ALARM_SOS);
- }
- break;
default:
position.set(Position.PREFIX_IO + id, readValue(buf, length, false));
break;
diff --git a/src/main/java/org/traccar/protocol/TopinProtocol.java b/src/main/java/org/traccar/protocol/TopinProtocol.java
index 844dd7518..d28afbf94 100644
--- a/src/main/java/org/traccar/protocol/TopinProtocol.java
+++ b/src/main/java/org/traccar/protocol/TopinProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2021 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,13 +18,17 @@ package org.traccar.protocol;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
+import org.traccar.model.Command;
public class TopinProtocol extends BaseProtocol {
public TopinProtocol() {
+ setSupportedDataCommands(
+ Command.TYPE_SOS_NUMBER);
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new TopinProtocolEncoder(TopinProtocol.this));
pipeline.addLast(new TopinProtocolDecoder(TopinProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
index 87db95946..4fe261aa4 100644
--- a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
@@ -50,7 +50,11 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_STATUS = 0x13;
public static final int MSG_WIFI_OFFLINE = 0x17;
public static final int MSG_TIME_UPDATE = 0x30;
+ public static final int MSG_SOS_NUMBER = 0x41;
public static final int MSG_WIFI = 0x69;
+ public static final int MSG_VIBRATION_ON = 0x92;
+ public static final int MSG_VIBRATION_OFF = 0x93;
+ public static final int MSG_VIBRATION = 0x94;
private void sendResponse(Channel channel, int length, int type, ByteBuf content) {
if (channel != null) {
@@ -89,6 +93,19 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
return negative ? -result : result;
}
+ private String decodeAlarm(int alarms) {
+ if (BitUtil.check(alarms, 0)) {
+ return Position.ALARM_VIBRATION;
+ }
+ if (BitUtil.check(alarms, 1)) {
+ return Position.ALARM_OVERSPEED;
+ }
+ if (BitUtil.check(alarms, 4)) {
+ return Position.ALARM_LOW_POWER;
+ }
+ return null;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -154,17 +171,7 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
if (buf.readableBytes() >= 5) {
position.setAltitude(buf.readShort());
-
- int alarms = buf.readUnsignedByte();
- if (BitUtil.check(alarms, 0)) {
- position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
- }
- if (BitUtil.check(alarms, 1)) {
- position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
- }
- if (BitUtil.check(alarms, 4)) {
- position.set(Position.KEY_ALARM, Position.ALARM_LOW_POWER);
- }
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
}
ByteBuf content = Unpooled.buffer();
@@ -186,10 +193,12 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
+ ByteBuf content = buf.retainedSlice(buf.readerIndex(), buf.readableBytes() - 2);
+
position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
buf.readUnsignedByte(); // timezone
- int interval = buf.readUnsignedByte();
+ buf.readUnsignedByte(); // interval
if (buf.readableBytes() >= 1 + 2) {
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
}
@@ -203,8 +212,6 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_HEART_RATE, buf.readUnsignedByte());
}
- ByteBuf content = Unpooled.buffer();
- content.writeByte(interval);
sendResponse(channel, length, type, content);
return position;
@@ -242,6 +249,10 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedShort(), buf.readUnsignedByte()));
}
+ if (buf.readableBytes() > 2) {
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+ }
+
position.setNetwork(network);
ByteBuf content = Unpooled.buffer();
@@ -250,6 +261,17 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
return position;
+ } else if (type == MSG_VIBRATION) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
+
+ return position;
+
}
return null;
diff --git a/src/main/java/org/traccar/protocol/TopinProtocolEncoder.java b/src/main/java/org/traccar/protocol/TopinProtocolEncoder.java
new file mode 100644
index 000000000..77f80b9d4
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TopinProtocolEncoder.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2021 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.model.Command;
+
+import java.nio.charset.StandardCharsets;
+
+public class TopinProtocolEncoder extends BaseProtocolEncoder {
+
+ public TopinProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private ByteBuf encodeContent(int type, ByteBuf content) {
+
+ ByteBuf buf = Unpooled.buffer();
+
+ buf.writeByte(0x78);
+ buf.writeByte(0x78);
+
+ buf.writeByte(1 + content.readableBytes()); // message length
+
+ buf.writeByte(type);
+
+ buf.writeBytes(content);
+ content.release();
+
+ buf.writeByte('\r');
+ buf.writeByte('\n');
+
+ return buf;
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ ByteBuf content = Unpooled.buffer();
+
+ switch (command.getType()) {
+ case Command.TYPE_SOS_NUMBER:
+ content.writeCharSequence(command.getString(Command.KEY_PHONE), StandardCharsets.US_ASCII);
+ return encodeContent(TopinProtocolDecoder.MSG_SOS_NUMBER, content);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java b/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java
index b5398116d..58c66031e 100644
--- a/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TotemProtocolDecoder.java
@@ -227,8 +227,22 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_GEOFENCE_EXIT;
case 0x05:
return Position.ALARM_GEOFENCE_ENTER;
+ case 0x06:
+ return Position.ALARM_TOW;
+ case 0x07:
+ return Position.ALARM_GPS_ANTENNA_CUT;
+ case 0x10:
+ return Position.ALARM_POWER_CUT;
+ case 0x11:
+ return Position.ALARM_POWER_RESTORED;
+ case 0x12:
+ return Position.ALARM_LOW_POWER;
+ case 0x13:
+ return Position.ALARM_LOW_BATTERY;
case 0x40:
- return Position.ALARM_SHOCK;
+ return Position.ALARM_VIBRATION;
+ case 0x41:
+ return Position.ALARM_IDLE;
case 0x42:
return Position.ALARM_ACCELERATION;
case 0x43:
@@ -357,16 +371,11 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_CHARGE, BitUtil.check(status, 32 - 4));
position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 5) ? Position.ALARM_GEOFENCE_EXIT : null);
position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 6) ? Position.ALARM_GEOFENCE_ENTER : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 7) ? Position.ALARM_GPS_ANTENNA_CUT : null);
position.set(Position.PREFIX_OUT + 1, BitUtil.check(status, 32 - 9));
position.set(Position.PREFIX_OUT + 2, BitUtil.check(status, 32 - 10));
position.set(Position.PREFIX_OUT + 3, BitUtil.check(status, 32 - 11));
- position.set(Position.PREFIX_OUT + 4, BitUtil.check(status, 32 - 12));
- position.set(Position.PREFIX_IN + 2, BitUtil.check(status, 32 - 13));
- position.set(Position.PREFIX_IN + 3, BitUtil.check(status, 32 - 14));
- position.set(Position.PREFIX_IN + 4, BitUtil.check(status, 32 - 15));
- position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 16) ? Position.ALARM_SHOCK : null);
- position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 18) ? Position.ALARM_LOW_BATTERY : null);
- position.set(Position.KEY_ALARM, BitUtil.check(status, 32 - 22) ? Position.ALARM_JAMMING : null);
+ position.set(Position.KEY_STATUS, status); // see https://github.com/traccar/traccar/pull/4762
position.setTime(parser.nextDateTime());
@@ -461,10 +470,8 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
- String type = null;
if (pattern == PATTERN4) {
- type = parser.next();
- position.set(Position.KEY_ALARM, decodeAlarm4(Integer.parseInt(type, 16)));
+ position.set(Position.KEY_ALARM, decodeAlarm4(parser.nextHexInt()));
}
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
@@ -485,8 +492,8 @@ public class TotemProtocolDecoder extends BaseProtocolDecoder {
}
if (channel != null) {
- if (type != null) {
- String response = "$$0014" + type + sentence.substring(sentence.length() - 6, sentence.length() - 2);
+ if (pattern == PATTERN4) {
+ String response = "$$0014AA" + sentence.substring(sentence.length() - 6, sentence.length() - 2);
response += String.format("%02X", Checksum.xor(response)).toUpperCase();
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
} else {
diff --git a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
index 4f6854098..819c42471 100644
--- a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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,10 +17,15 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
import org.traccar.Protocol;
+import org.traccar.config.Keys;
+import org.traccar.helper.BcdUtil;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
@@ -29,6 +34,11 @@ import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
public class TzoneProtocolDecoder extends BaseProtocolDecoder {
@@ -36,6 +46,20 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, int index) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ String ack = String.format("@ACK,%d#", index);
+ String time = String.format("@UTC time:%s", dateFormat.format(new Date()));
+
+ ByteBuf response = Unpooled.copiedBuffer(ack + time, StandardCharsets.US_ASCII);
+
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
private String decodeAlarm(Short value) {
switch (value) {
case 0x01:
@@ -256,9 +280,28 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
int blockLength = buf.readUnsignedShort();
int blockEnd = buf.readerIndex() + blockLength;
- if (blockLength > 0 && (hardware == 0x10A || hardware == 0x10B || hardware == 0x406)) {
- position.setNetwork(new Network(
- CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort())));
+ if (blockLength > 0) {
+ if (hardware == 0x10A || hardware == 0x10B || hardware == 0x406) {
+
+ position.setNetwork(new Network(
+ CellTower.fromLacCid(buf.readUnsignedShort(), buf.readUnsignedShort())));
+
+ } else if (hardware == 0x407) {
+
+ Network network = new Network();
+ int count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+ buf.readUnsignedByte(); // signal information
+
+ int mcc = BcdUtil.readInteger(buf, 4);
+ int mnc = BcdUtil.readInteger(buf, 4) % 1000;
+
+ network.addCellTower(CellTower.from(
+ mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedInt()));
+ }
+ position.setNetwork(network);
+
+ }
}
buf.readerIndex(blockEnd);
@@ -268,25 +311,41 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
blockLength = buf.readUnsignedShort();
blockEnd = buf.readerIndex() + blockLength;
- if (blockLength >= 13) {
+ if (hardware == 0x407 || blockLength >= 13) {
position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
position.set("terminalInfo", buf.readUnsignedByte());
- int status = buf.readUnsignedByte();
- position.set(Position.PREFIX_OUT + 1, BitUtil.check(status, 0));
- position.set(Position.PREFIX_OUT + 2, BitUtil.check(status, 1));
- status = buf.readUnsignedByte();
- position.set(Position.PREFIX_IN + 1, BitUtil.check(status, 4));
- if (BitUtil.check(status, 0)) {
- position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ if (hardware != 0x407) {
+ int status = buf.readUnsignedByte();
+ position.set(Position.PREFIX_OUT + 1, BitUtil.check(status, 0));
+ position.set(Position.PREFIX_OUT + 2, BitUtil.check(status, 1));
+ status = buf.readUnsignedByte();
+ position.set(Position.PREFIX_IN + 1, BitUtil.check(status, 4));
+ if (BitUtil.check(status, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
}
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
position.set("gsmStatus", buf.readUnsignedByte());
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
- position.set(Position.KEY_POWER, buf.readUnsignedShort());
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
+
+ if (hardware != 0x407) {
+ position.set(Position.KEY_POWER, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+ } else {
+ int temperature = buf.readUnsignedShort();
+ if (!BitUtil.check(temperature, 15)) {
+ double value = BitUtil.to(temperature, 14) * 0.1;
+ position.set(Position.PREFIX_TEMP + 1, BitUtil.check(temperature, 14) ? -value : value);
+ }
+ int humidity = buf.readUnsignedShort();
+ if (!BitUtil.check(humidity, 15)) {
+ position.set("humidity", BitUtil.to(humidity, 15) * 0.1);
+ }
+ position.set("lightSensor", buf.readUnsignedByte() == 0);
+ }
}
if (blockLength >= 15) {
@@ -312,6 +371,10 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
}
+ if (Context.getConfig().getBoolean(Keys.PROTOCOL_ACK.withPrefix(getProtocolName()))) {
+ sendResponse(channel, remoteAddress, buf.getUnsignedShort(buf.writerIndex() - 6));
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java b/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java
index 545784865..1081fa811 100644
--- a/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/UuxProtocolDecoder.java
@@ -40,6 +40,7 @@ public class UuxProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_IMMOBILIZER = 0x9E;
public static final int MSG_ACK = 0xD0;
public static final int MSG_NACK = 0xF0;
+ public static final int MSG_KEEPALIVE = 0xFF;
private void sendResponse(Channel channel, int productCode, int protocolVersion, int type) {
if (channel != null && BitUtil.check(protocolVersion, 7)) {
@@ -71,6 +72,10 @@ public class UuxProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // length
int type = buf.readUnsignedByte();
+ if (type == MSG_KEEPALIVE) {
+ return null;
+ }
+
String vehicleId = buf.readCharSequence(10, StandardCharsets.US_ASCII).toString();
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, vehicleId);
if (deviceSession == null) {
diff --git a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
index 5ff0bbb6c..4990cfd65 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2021 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.
@@ -89,8 +89,6 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_GEOFENCE_EXIT;
} else if (BitUtil.check(status, 2)) {
return Position.ALARM_GEOFENCE_ENTER;
- } else if (BitUtil.check(status, 3)) {
- return Position.ALARM_OVERSPEED;
} else if (BitUtil.check(status, 16)) {
return Position.ALARM_SOS;
} else if (BitUtil.check(status, 17)) {
@@ -101,7 +99,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_GEOFENCE_ENTER;
} else if (BitUtil.check(status, 20)) {
return Position.ALARM_REMOVING;
- } else if (BitUtil.check(status, 21)) {
+ } else if (BitUtil.check(status, 21) || BitUtil.check(status, 22)) {
return Position.ALARM_FALL_DOWN;
}
return null;
@@ -145,8 +143,8 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
int cellCount = Integer.parseInt(values[index++]);
index += 1; // timing advance
- int mcc = Integer.parseInt(values[index++]);
- int mnc = Integer.parseInt(values[index++]);
+ int mcc = !values[index].isEmpty() ? Integer.parseInt(values[index++]) : 0;
+ int mnc = !values[index].isEmpty() ? Integer.parseInt(values[index++]) : 0;
for (int i = 0; i < cellCount; i++) {
network.addCellTower(CellTower.from(mcc, mnc,
@@ -254,10 +252,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
Position position = decodePosition(deviceSession, buf.toString(StandardCharsets.US_ASCII));
- if (type.equals("AL")) {
- if (position != null) {
- position.set(Position.KEY_ALARM, Position.ALARM_SOS);
- }
+ if (type.startsWith("AL")) {
sendResponse(channel, id, index, "AL");
}
@@ -270,7 +265,8 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
} else if (type.equalsIgnoreCase("PULSE")
|| type.equalsIgnoreCase("HEART")
|| type.equalsIgnoreCase("BLOOD")
- || type.equalsIgnoreCase("BPHRT")) {
+ || type.equalsIgnoreCase("BPHRT")
+ || type.equalsIgnoreCase("btemp2")) {
if (buf.isReadable()) {
@@ -282,13 +278,18 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
String[] values = buf.toString(StandardCharsets.US_ASCII).split(",");
int valueIndex = 0;
- if (type.equalsIgnoreCase("BPHRT") || type.equalsIgnoreCase("BLOOD")) {
- position.set("pressureHigh", values[valueIndex++]);
- position.set("pressureLow", values[valueIndex++]);
- }
-
- if (valueIndex <= values.length - 1) {
- position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[valueIndex]));
+ if (type.equalsIgnoreCase("btemp2")) {
+ if (Integer.parseInt(values[valueIndex++]) > 0) {
+ position.set(Position.PREFIX_TEMP + 1, Double.parseDouble(values[valueIndex]));
+ }
+ } else {
+ if (type.equalsIgnoreCase("BPHRT") || type.equalsIgnoreCase("BLOOD")) {
+ position.set("pressureHigh", values[valueIndex++]);
+ position.set("pressureLow", values[valueIndex++]);
+ }
+ if (valueIndex <= values.length - 1) {
+ position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[valueIndex]));
+ }
}
return position;
diff --git a/src/main/java/org/traccar/protocol/Xexun2FrameDecoder.java b/src/main/java/org/traccar/protocol/Xexun2FrameDecoder.java
new file mode 100644
index 000000000..521d0209c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Xexun2FrameDecoder.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2021 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.BufferUtil;
+
+public class Xexun2FrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 5) {
+ return null;
+ }
+
+ ByteBuf flag = Unpooled.wrappedBuffer(new byte[] {(byte) 0xfa, (byte) 0xaf});
+ int index;
+ try {
+ index = BufferUtil.indexOf(flag, buf, buf.readerIndex() + 2, buf.writerIndex());
+ } finally {
+ flag.release();
+ }
+
+ if (index >= 0) {
+ ByteBuf result = Unpooled.buffer(index + 2 - buf.readerIndex());
+
+ while (buf.readerIndex() < index + 2) {
+ int b = buf.readUnsignedByte();
+ if (b == 0xfb && buf.isReadable() && buf.getUnsignedByte(buf.readerIndex()) == 0xbf) {
+ buf.readUnsignedByte(); // skip
+ int ext = buf.readUnsignedByte();
+ if (ext == 0x01) {
+ result.writeByte(0xfa);
+ result.writeByte(0xaf);
+ } else if (ext == 0x02) {
+ result.writeByte(0xfb);
+ result.writeByte(0xbf);
+ }
+ } else {
+ result.writeByte(b);
+ }
+ }
+
+ return result;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Xexun2Protocol.java b/src/main/java/org/traccar/protocol/Xexun2Protocol.java
new file mode 100644
index 000000000..265841c77
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Xexun2Protocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 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.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class Xexun2Protocol extends BaseProtocol {
+
+ public Xexun2Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new Xexun2FrameDecoder());
+ pipeline.addLast(new Xexun2ProtocolDecoder(Xexun2Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java
new file mode 100644
index 000000000..766a3f05b
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Xexun2ProtocolDecoder.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2021 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.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
+
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class Xexun2ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Xexun2ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_POSITION = 0x14;
+
+ private void sendResponse(Channel channel, int type, int index, ByteBuf imei) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0xfa);
+ response.writeByte(0xaf);
+
+ response.writeShort(type);
+ response.writeShort(index);
+ response.writeBytes(imei);
+ response.writeShort(1); // attributes / length
+ response.writeShort(0xfffe); // checksum
+ response.writeByte(1); // response
+
+ response.writeByte(0xfa);
+ response.writeByte(0xaf);
+
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private String decodeAlarm(long value) {
+ if (BitUtil.check(value, 0)) {
+ return Position.ALARM_SOS;
+ }
+ if (BitUtil.check(value, 15)) {
+ return Position.ALARM_FALL_DOWN;
+ }
+ return null;
+ }
+
+ private double convertCoordinate(double value) {
+ double degrees = Math.floor(value / 100);
+ double minutes = value - degrees * 100;
+ return degrees + minutes / 60;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // flag
+ int type = buf.readUnsignedShort();
+ int index = buf.readUnsignedShort();
+
+ ByteBuf imei = buf.readSlice(8);
+ DeviceSession deviceSession = getDeviceSession(
+ channel, remoteAddress, ByteBufUtil.hexDump(imei).substring(0, 15));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ sendResponse(channel, type, index, imei);
+
+ buf.readUnsignedShort(); // attributes
+ buf.readUnsignedShort(); // checksum
+
+ if (type == MSG_POSITION) {
+ List<Integer> lengths = new ArrayList<>();
+ List<Position> positions = new ArrayList<>();
+
+ int count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+ lengths.add(buf.readUnsignedShort());
+ }
+
+ for (int i = 0; i < count; i++) {
+ int endIndex = buf.readerIndex() + lengths.get(i);
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_INDEX, buf.readUnsignedByte());
+
+ position.setDeviceTime(new Date(buf.readUnsignedInt() * 1000));
+
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+
+ int battery = buf.readUnsignedShort();
+ position.set(Position.KEY_CHARGE, BitUtil.check(battery, 15));
+ position.set(Position.KEY_BATTERY_LEVEL, BitUtil.to(battery, 15));
+
+ int mask = buf.readUnsignedByte();
+
+ if (BitUtil.check(mask, 0)) {
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedInt()));
+ }
+ if (BitUtil.check(mask, 1)) {
+ int positionMask = buf.readUnsignedByte();
+ if (BitUtil.check(positionMask, 0)) {
+ position.setValid(true);
+ position.setFixTime(position.getDeviceTime());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.setLongitude(convertCoordinate(buf.readFloat()));
+ position.setLatitude(convertCoordinate(buf.readFloat()));
+ }
+ Network network = new Network();
+ if (BitUtil.check(positionMask, 1)) {
+ int wifiCount = buf.readUnsignedByte();
+ for (int j = 0; j < wifiCount; j++) {
+ String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), buf.readUnsignedByte()));
+ }
+ }
+ if (BitUtil.check(positionMask, 2)) {
+ int cellCount = buf.readUnsignedByte();
+ for (int j = 0; j < cellCount; j++) {
+ network.addCellTower(CellTower.from(
+ buf.readUnsignedShort(), buf.readUnsignedShort(),
+ buf.readInt(), buf.readUnsignedInt(), buf.readUnsignedByte()));
+ }
+ }
+ if (network.getWifiAccessPoints() != null || network.getCellTowers() != null) {
+ position.setNetwork(network);
+ }
+ if (BitUtil.check(positionMask, 3)) {
+ buf.skipBytes(12 * buf.readUnsignedByte()); // tof
+ }
+ if (BitUtil.check(positionMask, 5)) {
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
+ position.setCourse(buf.readUnsignedShort() * 0.1);
+ }
+ if (BitUtil.check(positionMask, 6)) {
+ position.setValid(true);
+ position.setFixTime(position.getDeviceTime());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.setLongitude(convertCoordinate(buf.readDouble()));
+ position.setLatitude(convertCoordinate(buf.readDouble()));
+
+ }
+ }
+ if (BitUtil.check(mask, 3)) {
+ buf.readUnsignedInt(); // fingerprint
+ }
+ if (BitUtil.check(mask, 4)) {
+ buf.skipBytes(20); // version
+ buf.skipBytes(8); // imsi
+ buf.skipBytes(10); // iccid
+ }
+ if (BitUtil.check(mask, 5)) {
+ buf.skipBytes(12); // device parameters
+ }
+
+ if (!position.getValid()) {
+ getLastLocation(position, position.getDeviceTime());
+ }
+ positions.add(position);
+
+ buf.readerIndex(endIndex);
+ }
+
+ return positions;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/schedule/ScheduleManager.java b/src/main/java/org/traccar/schedule/ScheduleManager.java
index 4def211d0..5d5054100 100644
--- a/src/main/java/org/traccar/schedule/ScheduleManager.java
+++ b/src/main/java/org/traccar/schedule/ScheduleManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 - 2021 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.
@@ -27,6 +27,7 @@ public class ScheduleManager {
executor = Executors.newSingleThreadScheduledExecutor();
new TaskDeviceInactivityCheck().schedule(executor);
+ new TaskWebSocketKeepalive().schedule(executor);
}
diff --git a/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java
new file mode 100644
index 000000000..953b0efea
--- /dev/null
+++ b/src/main/java/org/traccar/schedule/TaskWebSocketKeepalive.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2021 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 java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public class TaskWebSocketKeepalive implements Runnable {
+
+ private static final long PERIOD_SECONDS = 55;
+
+ public void schedule(ScheduledExecutorService executor) {
+ executor.scheduleAtFixedRate(this, PERIOD_SECONDS, PERIOD_SECONDS, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void run() {
+ Context.getConnectionManager().sendKeepalive();
+ }
+
+}
diff --git a/src/main/java/org/traccar/web/WebServer.java b/src/main/java/org/traccar/web/WebServer.java
index 04c320839..604edfedc 100644
--- a/src/main/java/org/traccar/web/WebServer.java
+++ b/src/main/java/org/traccar/web/WebServer.java
@@ -26,6 +26,11 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
+import org.eclipse.jetty.server.session.DatabaseAdaptor;
+import org.eclipse.jetty.server.session.DefaultSessionCache;
+import org.eclipse.jetty.server.session.JDBCSessionDataStoreFactory;
+import org.eclipse.jetty.server.session.SessionCache;
+import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
@@ -35,6 +40,7 @@ import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.Context;
import org.traccar.api.DateParameterConverterProvider;
import org.traccar.config.Config;
import org.traccar.api.AsyncSocketServlet;
@@ -172,6 +178,17 @@ public class WebServer {
}
private void initSessionConfig(Config config, ServletContextHandler servletHandler) {
+ if (config.getBoolean(Keys.WEB_PERSIST_SESSION)) {
+ DatabaseAdaptor databaseAdaptor = new DatabaseAdaptor();
+ databaseAdaptor.setDatasource(Context.getDataManager().getDataSource());
+ JDBCSessionDataStoreFactory jdbcSessionDataStoreFactory = new JDBCSessionDataStoreFactory();
+ jdbcSessionDataStoreFactory.setDatabaseAdaptor(databaseAdaptor);
+ SessionHandler sessionHandler = servletHandler.getSessionHandler();
+ SessionCache sessionCache = new DefaultSessionCache(sessionHandler);
+ sessionCache.setSessionDataStore(jdbcSessionDataStoreFactory.getSessionDataStore(sessionHandler));
+ sessionHandler.setSessionCache(sessionCache);
+ }
+
int sessionTimeout = config.getInteger(Keys.WEB_SESSION_TIMEOUT);
if (sessionTimeout > 0) {
servletHandler.getSessionHandler().setMaxInactiveInterval(sessionTimeout);
diff --git a/src/test/java/org/traccar/ProtocolTest.java b/src/test/java/org/traccar/ProtocolTest.java
index 56551f247..c40a15dcc 100644
--- a/src/test/java/org/traccar/ProtocolTest.java
+++ b/src/test/java/org/traccar/ProtocolTest.java
@@ -300,6 +300,10 @@ public class ProtocolTest extends BaseTest {
assertTrue(attributes.get(Position.KEY_HOURS) instanceof Number);
}
+ if (attributes.containsKey(Position.KEY_RESULT)) {
+ assertTrue(attributes.get(Position.KEY_RESULT) instanceof String);
+ }
+
if (position.getNetwork() != null && position.getNetwork().getCellTowers() != null) {
for (CellTower cellTower : position.getNetwork().getCellTowers()) {
checkInteger(cellTower.getMobileCountryCode(), 0, 999);
diff --git a/src/test/java/org/traccar/geocoder/GeocoderTest.java b/src/test/java/org/traccar/geocoder/GeocoderTest.java
index 5f1748984..380980d2b 100644
--- a/src/test/java/org/traccar/geocoder/GeocoderTest.java
+++ b/src/test/java/org/traccar/geocoder/GeocoderTest.java
@@ -101,4 +101,13 @@ public class GeocoderTest {
String address = geocoder.getAddress(40.733, -73.989, null);
assertEquals("120 East 13th Street, New York, New York 10003, United States", address);
}
+
+ @Ignore
+ @Test
+ public void testMapTiler() {
+ Geocoder geocoder = new MapTilerGeocoder("mnbnwLErpdspq13f0kC6", 0, new AddressFormat());
+ String address = geocoder.getAddress(40.733, -73.989, null);
+ assertEquals("East 13th Street, New York City, New York, United States", address);
+ }
+
}
diff --git a/src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java
deleted file mode 100644
index 11c9c65d8..000000000
--- a/src/test/java/org/traccar/protocol/AppletProtocolDecoderTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.traccar.protocol;
-
-import io.netty.handler.codec.http.DefaultHttpHeaders;
-import io.netty.handler.codec.http.HttpMethod;
-import org.junit.Test;
-import org.traccar.ProtocolTest;
-
-public class AppletProtocolDecoderTest extends ProtocolTest {
-
- @Test
- public void testDecode() throws Exception {
-
- var decoder = new AppletProtocolDecoder(null);
-
- DefaultHttpHeaders headers = new DefaultHttpHeaders();
-
- headers.add("HOST", "192.168.0.1:8080");
- headers.add("X-Admin-Protocol", "globalplatform-remote-admin/1.0");
- headers.add("X-Admin-From", "8943170080001406197F");
- headers.add("User-Agent", "oma-scws-admin-agent/1.1");
- headers.add("From", "8943170080001406197F");
-
- verifyNull(decoder, request(HttpMethod.POST, "/pli?=", headers));
-
- }
-
-}
diff --git a/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java
new file mode 100644
index 000000000..6b9c71b0e
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class B2316ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new B2316ProtocolDecoder(null);
+
+ verifyPositions(decoder, false, text(
+ "{\"imei\":\"866349041783600\",\"data\":[{\"tm\":1631162952,\"wn\":7},{\"tm\":1631158729,\"ic\":\"89883030000059398609\",\"ve\":\"B2316.TAU.U.TH01\"},{\"tm\":1631158805,\"te\":\"312,363\",\"st\":0,\"ba\":3,\"sn\":80},{\"tm\":1631158829,\"ci\":\"505,1,8218,133179149,-108\"},{\"tm\":1631162956,\"wi\":\"101331c17f4f,-74;f46bef7953bb,-81;b09575cff1c8,-86;e2b9e5d61a7a,-88;b0ee7b4dee2f,-88;e0b9e5d61a77,-89;f66bef7953b9,-89;\",\"te\":\"335,366\",\"hr\":58,\"bp\":\"113,73\",\"st\":0,\"ba\":3,\"sn\":60},{\"tm\":1631162968,\"ci\":\"505,1,8218,133179149,-105\"}]}"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java
index 634e37b89..31d0efa12 100644
--- a/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java
@@ -11,6 +11,18 @@ public class DmtHttpProtocolDecoderTest extends ProtocolTest {
var decoder = new DmtHttpProtocolDecoder(null);
+ verifyAttributes(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"date\":\"2021-12-12T16:04:52Z\",\"device\":{\"sn\":\"416581\",\"prod\":85,\"rev\":1,\"fw\":\"1.12\",\"iccid\":\"89011702278612797427\",\"imei\":\"351358810439486\"},\"sqn\":1549,\"reason\":42,\"counters\":[{\"id\":0,\"val\":5304},{\"id\":3,\"val\":3200},{\"id\":4,\"val\":5066},{\"id\":128,\"val\":1},{\"id\":129,\"val\":8},{\"id\":130,\"val\":0},{\"id\":131,\"val\":0},{\"id\":132,\"val\":0},{\"id\":134,\"val\":1},{\"id\":138,\"val\":0},{\"id\":139,\"val\":36},{\"id\":142,\"val\":1629023},{\"id\":145,\"val\":0},{\"id\":146,\"val\":1}]}")));
+
+ verifyAttributes(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"date\":\"2021-10-04T18:15:47Z\",\"device\":{\"sn\":\"403809\",\"prod\":85,\"rev\":1,\"fw\":\"1.12\",\"iccid\":\"89011702278483601922\",\"imei\":\"352656106127312\"},\"sqn\":40927,\"reason\":11,\"analogues\":[{\"id\":1,\"val\":4265},{\"id\":3,\"val\":3800},{\"id\":4,\"val\":12},{\"id\":5,\"val\":4251}],\"inputs\":1,\"outputs\":0,\"status\":137}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"date\":\"2021-10-04T18:14:55Z\",\"device\":{\"sn\":\"403809\",\"prod\":85,\"rev\":1,\"fw\":\"1.12\",\"iccid\":\"89011702278483601922\",\"imei\":\"352656106127312\"},\"sqn\":40925,\"reason\":1,\"lat\":26.87366,\"lng\":-80.10618,\"posAcc\":47.7,\"posInfo\":{\"GDOP\":4.68,\"BSat\":2,\"GSat\":4,\"Src\":1},\"analogues\":[{\"id\":1,\"val\":4265},{\"id\":3,\"val\":3800},{\"id\":4,\"val\":16},{\"id\":5,\"val\":4255}],\"inputs\":1,\"outputs\":0,\"status\":137}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{ \"date\": \"2021-04-20T11:10:03.702659861Z\", \"device\":{ \"sn\": \"0016C001F000ABEC\", \"prod\": 0.2, \"rev\": 0.3, \"fw\": \"1.1\", \"module\": \"LR 34.3.3\", \"iccid\": \"89610180000000000000\", \"imei\": \"354043000000000\" }, \"sqn\": 347263802, \"reason\":3, \"lat\": 1.1, \"lng\": 2.2, \"posAcc\": 30.1, \"posInfo\":{ \"HDOP\": 0.1, \"PDOP\": 0.2, \"GDOP\": 0.3, \"BSat\":1, \"GSat\":2, \"Src\":2 }, \"analogues\":[{ \"id\":1, \"val\": 300 },{ \"id\":2, \"val\": 500} ], \"inputs\": 5001, \"outputs\":0, \"status\": 17, \"counters\":[{ \"id\": 11, \"val\": 43 },{ \"id\": 23, \"val\": 8800} ], \"lora\":{ \"dev_id\": \"yabby-abec\", \"app_id\": \"digital-matter\", \"dev_addr\": \"260B567A\", \"gw\": [ { \"id\": \"dm-sentrius\", \"snr\": 10, \"rssi\": -36 } ] }}")));
+
verifyPositions(decoder, request(HttpMethod.POST, "/",
buffer("{\"SerNo\":131693,\"IMEI\":\"356692063643328\",\"ICCID\":\"8944538523010771676\",\"ProdId\":33,\"FW\":\"33.4.1.27\",\"Records\":[{\"SeqNo\":125,\"Reason\":11,\"DateUTC\":\"2017-05-11 05:58:44\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":2,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14641,\"3\":2484,\"4\":26,\"5\":10868},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":0,\"13\":309,\"14\":9921,\"15\":3},\"FType\":7}]},{\"SeqNo\":128,\"Reason\":11,\"DateUTC\":\"2017-05-11 17:59:45\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":2,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14607,\"3\":2752,\"4\":26,\"5\":11062},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":1,\"13\":325,\"14\":10881,\"15\":3},\"FType\":7}]},{\"SeqNo\":130,\"Reason\":9,\"DateUTC\":\"2017-05-11 19:30:03\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":3,\"FType\":0},{\"DIn\":6,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14599,\"3\":2731,\"4\":27,\"5\":10965},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":2,\"13\":329,\"14\":11121,\"15\":3},\"FType\":7}]},{\"SeqNo\":131,\"Reason\":11,\"DateUTC\":\"2017-05-11 19:32:03\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":6,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14403,\"3\":2783,\"4\":27,\"5\":10965},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":2,\"13\":330,\"14\":11181,\"15\":3},\"FType\":7}]},{\"SeqNo\":133,\"Reason\":11,\"DateUTC\":\"2017-05-11 19:36:15\",\"Fields\":[{\"GpsUTC\":\"2017-05-08 18:04:57\",\"Lat\":43.7370138,\"Long\":-79.3462607,\"Alt\":197,\"Spd\":0,\"SpdAcc\":13,\"Head\":66,\"PDOP\":18,\"PosAcc\":37,\"GpsStat\":7,\"FType\":0},{\"DIn\":6,\"DOut\":0,\"DevStat\":2,\"FType\":2},{\"AnalogueData\":{\"1\":14319,\"3\":2898,\"4\":23,\"5\":10965},\"FType\":6},{\"AnalogueData\":{\"11\":34,\"12\":3,\"13\":331,\"14\":11241,\"15\":3},\"FType\":7}]}]}")));
diff --git a/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java
new file mode 100644
index 000000000..fc18b0560
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Dsf22FrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Dsf22FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new Dsf22FrameDecoder();
+
+ verifyFrame(
+ binary("4642000101A8EE5F0ECA5FF421B33F524E32610401"),
+ decoder.decode(null, null, binary("4642000101A8EE5F0ECA5FF421B33F524E32610401")));
+
+ verifyFrame(
+ binary("4642000103A8EE5F0ECA5FF421B33F524E326104010216600EFC92F421B63F524E366104013238600E1EBEF421B93F524E35610401"),
+ decoder.decode(null, null, binary("4642000103A8EE5F0ECA5FF421B33F524E326104010216600EFC92F421B63F524E366104013238600E1EBEF421B93F524E35610401")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java
new file mode 100644
index 000000000..96cd78f03
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+@Ignore
+public class Dsf22ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new Dsf22ProtocolDecoder(null);
+
+ verifyPositions(decoder, binary(
+ "4642000101A8EE5F0ECA5FF421B33F524E32610401"));
+
+ verifyPositions(decoder, binary(
+ "4642000103A8EE5F0ECA5FF421B33F524E326104010216600EFC92F421B63F524E366104013238600E1EBEF421B93F524E35610401"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java b/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java
new file mode 100644
index 000000000..46f32a8ae
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/DualcamFrameDecoderTest.java
@@ -0,0 +1,23 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class DualcamFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new DualcamFrameDecoder();
+
+ verifyFrame(
+ binary("000000050001403a4abaa31444000400"),
+ decoder.decode(null, null, binary("000000050001403a4abaa31444000400")));
+
+ verifyFrame(
+ binary("00010006000000110000"),
+ decoder.decode(null, null, binary("00010006000000110000")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java
new file mode 100644
index 000000000..3dd11bdf7
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/DualcamProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class DualcamProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new DualcamProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "000000050001403a4abaa31444000400"));
+
+ verifyNull(decoder, binary(
+ "00010006000000110000"));
+
+ verifyNull(decoder, binary(
+ "0003000400000001"));
+
+ verifyNull(decoder, binary(
+ "00040402ffd8ffe000104a46494600010100000100010000ffdb00c500100b0c0e0c0a100e0d0e1211101318281a181616183123251d283a333d3c3933383740485c4e404457453738506d51575f626768673e4d71797064785c656763011112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363021112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363ffc000110801e0028003012200021101031102ffc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffdd00040000ffda000c03010002110311003f00cb97c350585ac0fabea5f61966dd88bc8326307d54fa107f1a65f787c5be9b1ea36575f6cb46cee93cbf2f6fcc14704e4e4e7f2aaba0e873eb574638cec893fd64b8076641c71919c918ad3d635881edd74bd246cd3d3a9c93e66486fe219186cf7e6818cd3742f3f4e7bfbbb8fb2db0c6d7d9bf77241e01c8c1c7e74dd63479b49b908e77c4ff00eae4e06ec019e32718cd751e24b1fed1bfd36d7ccf2f7f9bf36338c007a7e155107fc5bf00ff009fded2199ede1f8ad2085f52befb24b2eefddf9464c60faa9fa7e751ea1a2f91a7a5f5acff0069b639dcfb366de401c1393cff002a6e8fa44ba8cc510ec8d7efc9c1dbc1c719e7a558d6b5588c034ed386cb25ea793bf90ddc6460e68028e91a44dab5c948cec893fd6498076e41c71919ce2a7bbb0fecebb7b5f33ccd98f9b18ce403d3f1ab3e16d4af3fb46dac3ceff46f9fe4da3d09eb8cf5a975e1ff0013ab8ff80ffe82293d83a99c48740004040217d696b47fb0f51ff9f7ff00c7d7fc6a2b8d2af6da169a6876c6bd4ef53df1d8d2025b4d2fcdb37bbb89bc880636b6dddbb9c74073d69ba8e9d269f30563b91bee3f4ddd33c678eb5b7addafdb2f2c6df7ecdfe67cd8ce3001fe9500ff009147fcff00cf4a2c229c9a4c56f146d7d75f6777ce13cb2fd3dc1fa54777a6795669756f379f01cee6dbb71ce3a139eb51e9da7c9a84c554ed45fbefd76f5c719e7a558d47508cc42cac46db55fa9dfd0f7191839a0665370b4a9c0abeda26a27a5bff00e3ebfe351cda5dedb42d2cf0ed45ea7729f6ec695809ad74cf3acdaeae25f22118dadb77679c74073d6a9eb3a749a6cbcfcc8df75fa6ec633c67deba2d72dbed977636dbf6799e67cd8ce3001fe9550f3e0bff003ff3d29b42b941b468eda085b51bbfb34926711888be307d54fd3f3a6dde8c8b62b750ca2e2dce72c536639c0e09cf5aaf611dbb97fb45dfd9f18dbfbb2fbbf2e95b9767cbf0e2a5bfefe039dd37ddc7cffdd3cf5e285619"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
index fdac158dd..6480d1dc4 100644
--- a/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/FifotrackProtocolDecoderTest.java
@@ -11,6 +11,15 @@ public class FifotrackProtocolDecoderTest extends ProtocolTest {
var decoder = new FifotrackProtocolDecoder(null);
+ verifyPosition(decoder, buffer(
+ "$$95,866104023192332,1,A03,,210414055249,460|0|25FC|104C,4.18,100,000F,0,A,2,9,22.643175,114.018150*75"));
+
+ verifyAttributes(decoder, buffer(
+ "$$136,866104023192332,1,A03,,210414055249,460|0|25FC|104C,4.18,100,000F,1,94D9B377EB53:-60|EC6C9FA4CAD8:-55|CA50E9206252:-61|54E061260A89:-51*3E"));
+
+ verifyPosition(decoder, buffer(
+ "$$274,863003046499158,18D0,A01,,211026081639,A,13.934116,100.000463,0,263,16,366959,345180,80000040,02,0,520|0|FA8|1A9B5B9,9DE|141|2D,% ^YENSABAICHAI$SONGKRAN$MR.^^?;6007643190300472637=150519870412=?+ 14 1 0000155 00103 ?,*69"));
+
verifyAttribute(decoder, buffer(
"$$25,863003046473534,1,B03,OK*4D"),
Position.KEY_RESULT, "OK");
diff --git a/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java
new file mode 100644
index 000000000..a276a01e9
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java
@@ -0,0 +1,42 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class FlexApiProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new FlexApiProtocolDecoder(null);
+
+ verifyAttributes(decoder, text(
+ "${\"topic\":\"v1/VF3102021113001/gnss/info\",\"payload\":{\"gnss.ts\":1639713510,\"gnss.latitude\":30.587509,\"gnss.longitude\":104.053650,\"gnss.altitude\":391,\"gnss.speed\":0,\"gnss.heading\":0,\"gnss.hdop\":1.100000,\"gnss.fix\":4,\"gnss.num_sv\":10}}xx"));
+
+ verifyNull(decoder, text(
+ "${\"topic\":\"v1/VF3102021113001/cellular1/info\",\"payload\":{\"modem1.ts\":1639713510,\"modem1.imei\":\"863674047326655\",\"modem1.imsi\":\"\",\"modem1.iccid\":\"\",\"modem1.phone_num\":\"\",\"modem1.signal_lvl\":0,\"modem1.reg_status\":0,\"modem1.operator\":\"\",\"modem1.network\":0,\"modem1.lac\":\"\",\"modem1.cell_id\":\"\",\"modem1.rssi\":0,\"modem1.rsrp\":0,\"modem1.rsrq\":0,\"cellular1.status\":2,\"cellular1.ip\":\"0.0.0.0\",\"cellular1.netmask\":\"255.255.255.255\",\"cellular1.gateway\":\"0.0.0.0\",\"cellular1.dns1\":\"0.0.0.0\",\"cellular1.up_at\":602}}xx"));
+
+ verifyAttributes(decoder, text(
+ "${\"topic\":\"v1/VF3102029000003/obd/info\",\"payload\":{\"obd.ts\":1639037377,\"obd.speed\":211,\"obd.f_lvl\":50.196079,\"obd.mil\":0,\"obd.dtcs\":0,\"obd.rpm\":14531.250000,\"obd.e_load\":50.980392,\"obd.c_temp\":118,\"obd.o_temp\":56,\"obd.a_temp\":-40,\"obd.f_press\":48,\"obd.t_pos\":51.764706,\"obd.b_volt\":13.782000,\"obd.up_time\":2265,\"obd.m_dist\":4643,\"obd.m_time\":257,\"obd.d_dist\":200,\"obd.d_time\":771,\"obd.vin\":\"LFV3B28R8A3025310\",\"obd.f_rate\":10,\"obd.t_dist\":4843,\"obd.b_press\":101,\"obd.f_r_press\":48,\"obd.i_temp\":37,\"obd.i_press\":32,\"obd.r_torque\":4128,\"obd.a_torque\":2,\"obd.mf_mon\":1,\"obd.f_s_mon\":0,\"obd.c_c_mon\":0,\"obd.c_mon\":1,\"obd.e_s_mon\":1,\"obd.e_v_s_mon\":1,\"obd.o_s_mon\":1,\"obd.o_s_h_mon\":1}}xx"));
+
+ verifyAttributes(decoder, text(
+ "${\"topic\":\"v1/VF3102021111601/obd/info\",\"payload\":{\"obd.ts\":1637225390,\"obd.speed\":0,\"obd.f_lvl\":null,\"obd.odo\":0,\"obd.e_hours\":0,\"obd.ab_level\":null,\"obd.mil\":0,\"obd.dtcs\":null,\"obd.rpm\":0,\"obd.e_load\":null,\"obd.c_temp\":-40,\"obd.o_temp\":-273,\"obd.a_temp\":null,\"obd.f_press\":null,\"obd.t_pos\":0,\"obd.b_volt\":null,\"obd.up_time\":null,\"obd.m_dist\":null,\"obd.m_time\":null,\"obd.d_dist\":null,\"obd.d_time\":null,\"obd.vin\":\"NLVIN123456789ABC\",\"obd.brake\":0,\"obd.parking\":0,\"obd.s_w_angle\":null,\"obd.f_rate\":0,\"obd.f_econ\":0,\"obd.a_pos\":null,\"obd.t_dist\":0,\"obd.b_press\":null,\"obd.f_r_press\":null,\"obd.i_temp\":null,\"obd.i_press\":null,\"obd.r_torque\":null,\"obd.f_torque\":null,\"obd.max_avl_torque\":null,\"obd.a_torque\":-125,\"obd.d_e_f_vol\":null,\"obd.mf_mon\":null,\"obd.f_s_mon\":null,\"obd.c_c_mon\":null,\"obd.c_mon\":null,\"obd.h_c_mon\":null,\"obd.e_s_mon\":null,\"obd.s_a_s_mon\":null,\"obd.a_s_r_mon\":null,\"obd.e_g_s_mon\":null,\"obd.e_g_s_h_mon\":null,\"obd.e_v_s_mon\":null,\"obd.c_s_a_s_mon\":null,\"obd.b_p_c_s_mon\":null,\"obd.dpf_mon\":null,\"obd.n_c_mon\":null,\"obd.nmhc_mon\":null,\"obd.o_s_mon\":null,\"obd.o_s_h_mon\":null,\"obd.pf_mon\":null}}xx"));
+
+ verifyPosition(decoder, text(
+ "${\"topic\":\"v1/VF3102021111601/gnss/info\",\"payload\":{\"time\":1637225390,\"lat\":30.587942,\"log\":104.053543,\"gnss.altitude\":480.399994,\"gnss.speed\":0,\"gnss.heading\":0,\"gnss.hdop\":0.900000,\"gnss.fix\":4,\"gnss.num_sv\":11}}xx"));
+
+ verifyNull(decoder, text(
+ "${\"topic\":\"v1/VF3102021111601/motion/info\",\"payload\":{\"motion.ts\":1637225450,\"motion.ax\":0.009272,\"motion.ay\":0.278404,\"motion.az\":-0.941596,\"motion.gx\":0.420000,\"motion.gy\":-0.490000,\"motion.gz\":0.140000}}xx"));
+
+ verifyNull(decoder, text(
+ "${\"topic\":\"v1/VF3102021111601/sysinfo/info\",\"payload\":{\"sysinfo.ts\":1637224740,\"sysinfo.model_name\":\"310\",\"sysinfo.oem_name\":\"inhand\",\"sysinfo.serial_number\":\"VF3102021111601\",\"sysinfo.firmware_version\":\"VT3_V1.1.32\",\"sysinfo.product_number\":\"FQ58\",\"sysinfo.description\":\"www.inhand.com.cn\"}}xx"));
+
+ verifyNull(decoder, text(
+ "${\"topic\":\"v1/VF3102021111601/io/info\",\"payload\":{\"io.ts\":1637227722,\"io.AI1\":0,\"io.DI1\":1,\"io.DI2\":0,\"io.DI3\":0,\"io.DI4\":0,\"io.DI1_pullup\":0,\"io.DI2_pullup\":0,\"io.DI3_pullup\":0,\"io.DI4_pullup\":0,\"io.DO1\":0,\"io.DO2\":0,\"io.DO3\":0,\"io.IGT\":1}}xx"));
+
+ verifyNull(decoder, text(
+ "${\"topic\":\"v1/VF3102021111601/cellular1/info\",\"payload\":{\"modem1.ts\":1637225330,\"modem1.imei\":\"863674047324999\",\"modem1.imsi\":\"460111150414721\",\"modem1.iccid\":\"89860319482086580401\",\"modem1.phone_num\":\"\",\"modem1.signal_lvl\":25,\"modem1.reg_status\":1,\"modem1.operator\":\"46011\",\"modem1.network\":3,\"modem1.lac\":\"EA00\",\"modem1.cell_id\":\"E779B81\",\"modem1.rssi\":0,\"modem1.rsrp\":0,\"modem1.rsrq\":0,\"cellular1.status\":3,\"cellular1.ip\":\"10.136.143.193\",\"cellular1.netmask\":\"255.255.255.255\",\"cellular1.gateway\":\"10.64.64.64\",\"cellular1.dns1\":\"223.5.5.5\",\"cellular1.up_at\":450}}xx"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java
index 3ede9017f..bb79f4f25 100644
--- a/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/GoSafeProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class GoSafeProtocolDecoderTest extends ProtocolTest {
var decoder = new GoSafeProtocolDecoder(null);
+ verifyPositions(decoder, false, text(
+ "*GS06,357330050846344,RST#"));
+
verifyAttribute(decoder, text(
"*GS06,356449068350122,013519070819,,SYS:G6S;V3.37;V1.1.8,GPS:A;12;N23.169866;E113.450728;0;255;54;0.79,COT:18779;,ADC:12.66;0.58,DTT:4084;E1;0;0;0;1,IWD:0;1;ad031652643fff28;23.2;1;1;86031652504fff28;24.3;2;1;e603165252a5ff28;24.2;3;1;bb0416557da6ff28;24.0#"),
Position.PREFIX_TEMP + 3, 24.0);
diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
index f3e5779e8..d2d090c04 100644
--- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
@@ -18,6 +18,15 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest {
"78780D01086471700328358100093F040D0A"));
verifyNotNull(decoder, binary(
+ "78782111150b0b022c30c804b7af7808810cb0003c00012e02d075df0084890c000679950d0a"));
+
+ verifyNotNull(decoder, binary(
+ "797900377000000001020035000103002c0004616219d00043000b013601048153931500001a0001000808652820400643521000000101004e46760d0a"));
+
+ verifyNull(decoder, binary(
+ "7878171915061810051a01f90101700d08c8f50c0000065494ae0d0a"));
+
+ verifyNotNull(decoder, binary(
"78783B2E10010D02020201CC00287D001F713E287D001F7231287D001E232D287D001F4018000000000000000000000000000000000000FF00020005B14B0D0A"));
verifyPosition(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
index f9bf574cd..a0462e675 100644
--- a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class H02ProtocolDecoderTest extends ProtocolTest {
var decoder = new H02ProtocolDecoder(null);
+ verifyPosition(decoder, buffer(
+ "*HQ,4970105243,V1,104000,A,2235.1777,N,11357.8913,E,000.27,235,130721,FFFFFBFF,460,11,d18e105,7752,6#"));
+
verifyAttribute(decoder, buffer(
"*HQ,135790246811220,HTBT,100#"),
Position.KEY_BATTERY_LEVEL, 100);
diff --git a/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java
new file mode 100644
index 000000000..35d0f1423
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/HoopoProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class HoopoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new HoopoProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "{ \"deviceId\": \"BCCD0654\", \"assetName\": \"BCCD0654\", \"assetType\": \"???? ?????? - ??? 8\", \"eventData\": { \"latitude\": 31.97498, \"longitude\": 34.80802, \"locationName\": \"\", \"accuracyLevel\": \"High\", \"eventType\": \"Arrival\", \"batteryLevel\": 100, \"receiveTime\": \"2021-09-20T18:52:32Z\" }, \"eventTime\": \"2021-09-20T08:52:02Z\", \"serverReportTime\": \"0001-01-01T00:00:00Z\" }"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
index 51a5d18ae..74671b845 100644
--- a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
@@ -12,6 +12,9 @@ public class HuaShengProtocolDecoderTest extends ProtocolTest {
var decoder = new HuaShengProtocolDecoder(null);
verifyNull(decoder, binary(
+ "c00000007eaa000000000000cb8000000032313130313030393238323800e9abafffd615d2000000000008000000010015ffffff0000000000000004e7ffffffffff0005000a10080001d5ab000900154b4e4142323531324d4b54353638363630000f00133335343434343131353130333138380014000b00000000000000c0"));
+
+ verifyNull(decoder, binary(
"c0010c003c0002000000000044020010a0014f42445f3347315f56322e320013a0043335353835353035313032303536360006a08700000006a0a105c9c0"));
verifyNull(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
index 07442fbef..238799fac 100644
--- a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
@@ -14,6 +14,19 @@ public class HuabaoProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"7E01000021013345678906000F002C012F373031313142534A2D4D3742203030303030303001D4C1423838383838B47E"));
+ verifyAttribute(decoder, binary(
+ "7e0200008e01917159043700b300000000800000030158990606ca0fd7000400000000211129111705010400000000cc14383938363037423831303230393031363239363830010d8001aa81021388820200858301148401aa8502189b8601338702007e8801338901148a0200998b1131323334353637383941424344454647488c04000200a88d0200828e0114a00b50353338662c5530323966037e"),
+ Position.KEY_DTCS, "P538f U029f");
+
+ verifyPosition(decoder, binary(
+ "7E020000830191715904370A2E00000000800000030158991806CA0FEB00040000010D211108194050010400000003CC14383938363034373831303230373033313836303830011A8001AA810213888202007A8301148401AA8502189B8601338702007D028801338901148A0200998B1131323334353637383941424344454647488C04000200A88D0200828E0114A0002E7E"));
+
+ verifyPosition(decoder, binary(
+ "7e0200007c0191718447540dcd000000008000000b029eabc204ba78510004000000042111182321120104000017f6cc14383933303237323037303339333033383732373130011d800134810204718202008283010e84017b85021ae986012f870201788901038b114a4e31424a314352364b573333363533358c040008dcb68d02018c8e013da000c07e"));
+
+ verifyNotNull(decoder, binary(
+ "7e207002940121523001530047210927151009000e002d80ac210927151010000e002d80ab210927151011000e002d80ac210927151012000e002e80ab210927151013000e002d80ab210927151014000e002d80ab210927151015000e002d80ab210927151016000e002d80aa210927151017000e002e80ab210927151018000e002d80ab210927151019000e002e80ac210927151020000e002d80ab210927151021000e002d80ab210927151022000d002d80ac210927151023000e002d80ac210927151024000e002e80ab210927151025000e002e80b0210927151026000e002e80ab210927151027000e002d80ab210927151028000e002e80b0210927151029000e002d80b0210927151030000e002e80ab210927151031000e002d80ab210927151032000e002d80aa210927151033000e002d80ab210927151034000e002d80ab210927151035000e002d80ab210927151036000e002d80ab210927151037000e002d80ab210927151038000e002d80b0210927151039000d002e80aa210927151040000e002d80ab210927151041000e002d80a5210927151042000e002e80ab210927151043000e002d80aa210927151044000e002d80ab210927151045000e002d80ab210927151046000e002d80ac210927151047000e002e80ab210927151048000e002e80a5210927151049000e002d80ab210927151050000e002d80ab210927151051000e002d80ab210927151052000e002d80ab210927151053000e002d80aa210927151054000e002e80b0210927151055000e002e80ab210927151056000e002d80ac210927151057000e002e80ab210927151058000e002d80ab210927151059000e002e80ab210927151100000e002d80ab210927151101000e002e80aa210927151102000e002d80a6210927151103000e002e80a5847e"));
+
verifyNotNull(decoder, binary(
"7e07040226046110426684002b000601005f0000000000000000000000000000000000000000000021031410530001040000000030011b310101e4020064e50101e60100e7080000000000000000eb2101cc00253510260100000000000000000000000000000000000000000000000000005f00000000004c0001000000000000000000000000000021031410531401040000000030011f310103e4020064e50101e60100e7080000000000000000eb2101cc0025351026012535100f32263d11f931000000000000000000000000000000005f00000000004c0001000000000000000000000000000021031410534001040000000030011f310104e4020064e50101e60100e7080000000000000000eb2101cc002535102601263d11f92d0000000000000000000000000000000000000000005f00000000004c00010000000000000000000000000000210314105350010400000000300118310104e4020064e50101e60100e7080000000000000000eb2101cc002535102601263d11f92e25350f6f2c263d120d2c00000000000000000000005f00000000004c0001000000000000000000000000000021031410540001040000000030011d310105e4020064e50101e60100e7080000000000000000eb2101cc002535102601263d11f93025350f6f2e263d120d2e00000000000000000000003c00000000004c0003015ae3e106c82ab900000010010b21031410540901040000000030011b310105e4020064e50101e60100e7080000000000000000f97e"));
diff --git a/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java b/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java
new file mode 100644
index 000000000..42777e419
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class JsonFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new JsonFrameDecoder();
+
+ verifyFrame(
+ binary("7b226465764964223a2243485a4430384b504430323130343235303436222c2264657654797065223a322c226861726456657273696f6e223a224844545456413139222c226d736754797065223a3131302c2270726f746f636f6c56657273696f6e223a225631222c22736f667456657273696f6e223a22332e312e38222c22737769746368436162537461747573223a2231222c2274786e4e6f223a2231363235323132373431353337227d"),
+ decoder.decode(null, null, binary("7b226465764964223a2243485a4430384b504430323130343235303436222c2264657654797065223a322c226861726456657273696f6e223a224844545456413139222c226d736754797065223a3131302c2270726f746f636f6c56657273696f6e223a225631222c22736f667456657273696f6e223a22332e312e38222c22737769746368436162537461747573223a2231222c2274786e4e6f223a2231363235323132373431353337227d")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java
index cd2d5b102..eda97ba2d 100644
--- a/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Jt600FrameDecoderTest.java
@@ -11,6 +11,10 @@ public class Jt600FrameDecoderTest extends ProtocolTest {
var decoder = new Jt600FrameDecoder();
verifyFrame(
+ binary("2480413009781914003406102107544354193631006213423b00000000006c070000000020e064f91ea0671d00020f0f0f0f0f0f0f0f0f0f07f100ea0f6e"),
+ decoder.decode(null, null, binary("2480413009781914003406102107544354193631006213423b00000000006c070000000020e064f91ea0671d00020f0f0f0f0f0f0f0f0f0f07f100ea0f6e")));
+
+ verifyFrame(
binary("2478905197081711003405101917164812492365028134847d0a1c000002640c0000000020c032759600731000000f0f0f0f0f0f0f0f0f0f000702850274"),
decoder.decode(null, null, binary("2478905197081711003405101917164812492365028134847d0a1c000002640c0000000020c032759600731000000f0f0f0f0f0f0f0f0f0f000702850274")));
diff --git a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
index be384c0f1..3c681ec58 100644
--- a/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Jt600ProtocolDecoderTest.java
@@ -12,6 +12,9 @@ public class Jt600ProtocolDecoderTest extends ProtocolTest {
var decoder = new Jt600ProtocolDecoder(null);
verifyPositions(decoder, binary(
+ "2480413009781914003406102107544354193631006213423b00000000006c070000000020e064f91ea0671d00020f0f0f0f0f0f0f0f0f0f07f100ea0f6e"));
+
+ verifyPositions(decoder, binary(
"2478807035371711003419081920061851380856003256223b000000000000070000000020c0ff965d54de1800000f0f0f0f0f0f0f0f0f0f02d600ea0a21"));
verifyPositions(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java
index 76f4d83d2..5c2b0732b 100644
--- a/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/KhdProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class KhdProtocolDecoderTest extends ProtocolTest {
var decoder = new KhdProtocolDecoder(null);
verifyPosition(decoder, binary(
+ "2929800028258b8c10210731035840031534240542120200000337fb000000ffff5a00000a0000000005005d0d"));
+
+ verifyPosition(decoder, binary(
"292980002825863156210105095059035109370460010100000211ffff000002fc0000001e780b12000034e70d"));
verifyPosition(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.java
new file mode 100644
index 000000000..c407d1e64
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/LacakProtocolDecoderTest.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 LacakProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new LacakProtocolDecoder(null);
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"location\":{\"event\":\"motionchange\",\"is_moving\":false,\"uuid\":\"0e9a2473-a9a7-4c00-997b-fb97d2154e75\",\"timestamp\":\"2021-07-21T08:06:34.444Z\",\"odometer\":0,\"coords\":{\"latitude\":-6.1148096,\"longitude\":106.6837015,\"accuracy\":3.8,\"speed\":18.67,\"speed_accuracy\":0.26,\"heading\":63,\"heading_accuracy\":0.28,\"altitude\":35.7,\"altitude_accuracy\":3.8},\"activity\":{\"type\":\"still\",\"confidence\":100},\"battery\":{\"is_charging\":false,\"level\":0.79},\"extras\":{}},\"device_id\":\"8737767034\"}")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java
index f1650b1b2..19e5cb0ab 100644
--- a/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MegastekFrameDecoderTest.java
@@ -22,6 +22,10 @@ public class MegastekFrameDecoderTest extends ProtocolTest {
binary("53545832363034373520202020202020202020024f244750524d432c3133313131302e30302c562c2c2c2c2c2c2c3036303931332c2c2c4e2a37362c3232322c30312c383135412c443435352c31312c39372c303030302c303030312c302c54696d65723b3735"),
decoder.decode(null, null, binary("53545832363034373520202020202020202020024f244750524d432c3133313131302e30302c562c2c2c2c2c2c2c3036303931332c2c2c4e2a37362c3232322c30312c383135412c443435352c31312c39372c303030302c303030312c302c54696d65723b37350d0a")));
+ verifyFrame(
+ binary("636d643d6169643b757365723d2a2a2a3b7077643d2a2a2a3b6c61743d33382e35353239353338333b6c6f6e3d32312e30353337343530303b706163633d31303030"),
+ decoder.decode(null, null, binary("636d643d6169643b757365723d2a2a2a3b7077643d2a2a2a3b6c61743d33382e35353239353338333b6c6f6e3d32312e30353337343530303b706163633d313030300a")));
+
}
}
diff --git a/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java
index 3747d9200..83d62e766 100644
--- a/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MegastekProtocolDecoderTest.java
@@ -10,6 +10,12 @@ public class MegastekProtocolDecoderTest extends ProtocolTest {
public void testDecode() throws Exception {
var decoder = new MegastekProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$MGV002,860719020193193,,S,070521,160748,V,2255.09165,N,11404.01322,E,00,00,00,,,,,,,,,,,,,,,,,,,10,015,Restart;!"));
+
+ verifyPosition(decoder, text(
+ "$MGV002,860719020193193,,R,070621,115717,V,2255.09165,N,11404.01322,E,00,00,00,99.9,,,,,460,07,262C,0F54,20,,,,,,,,,10,039,Timer;!"));
verifyPosition(decoder, text(
"0132$MGV002,869152024261561,,S,310818,133945,V,3814.35442,N,02144.50662,E,00,00,00,99.9,,,44.2,,202,10,,,13,0,0,0,0,90,,,,11,100,Timer;!"));
@@ -40,6 +46,9 @@ public class MegastekProtocolDecoderTest extends ProtocolTest {
"$MGV002,869152024446923,869152024446923,S,240816,151631,A,5053.83335,N,00424.05702,E,00,10,00,0.88,2.645,76.09,22.7,,206,01,07D1,6600,28,,,,,,,,,01,100,Timer;!"));
verifyPosition(decoder, text(
+ "0143$MGV002,869152024261564,,R,220621,120804,V,5152.09429,N,01051.32158,E,00,00,00,99.9,,,,,232,10,A5AE,5A4908,22,0000,0000,0,,,,,,00,093,Timer,,;!"));
+
+ verifyPosition(decoder, text(
"STX,013950007137061,$GPRMC,191959.000,A,5203.09602,N,00830.77057,E,5.73,255.27,240716,,,A*62,L,Belt Up,imei:013950007137061,0/5,,Battery=52%,,1,262,03,0084,B20E;FD"));
verifyPosition(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
index 3f0e5b2d3..58861c139 100644
--- a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class MeitrackProtocolDecoderTest extends ProtocolTest {
var decoder = new MeitrackProtocolDecoder(null);
+ verifyPosition(decoder, buffer(
+ "$$D149,867047043162018,AAA,35,-1.264865,36.800705,211001105240,A,9,20,41.0,323,1,1697,1,0,000|00||,0000,4.33|12.96|1.92|2.72|2.69,0.000000|0|0.000000,*E1"));
+
verifyPositions(decoder, binary(
"2424413132332c3836313538353034333230303836322c4343452c010000000100590015000305010609071b0b081c000939010a07000b1700199e011a9505921a0099c4089c5500c93e00405a000602a8b114000343f12e0604d18806270c654a2e000da20537009bb8963904010e0c0d020300aa7a0af69e0100002a35340d0a"));
diff --git a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
index 5c9e74a2d..4c17bf1f8 100644
--- a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
@@ -48,6 +48,9 @@ public class MictrackProtocolDecoderTest extends ProtocolTest {
var decoder = new MictrackProtocolDecoder(null);
verifyPositions(decoder, text(
+ "861836051888035$162835.00,A,4139.6460,N,07009.7239,W,,41.53,-25.8,220621"));
+
+ verifyPositions(decoder, text(
"861108032038761$062232.00,A,2238.2832,N,11401.7381,E,0.01,309.62,95.0,131117"));
verifyPositions(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java
index 2d7e4e597..1a9756226 100644
--- a/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MiniFinderProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class MiniFinderProtocolDecoderTest extends ProtocolTest {
@@ -19,7 +20,11 @@ public class MiniFinderProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, text(
"!1,123456789012345"));
- verifyNull(decoder, text(
+ verifyAttribute(decoder, text(
+ "!5,17,V,50"),
+ Position.KEY_BATTERY_LEVEL, 50);
+
+ verifyAttributes(decoder, text(
"!5,17,V"));
verifyNull(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
index 407d065f1..ab23f277a 100644
--- a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
@@ -10,9 +10,15 @@ public class Minifinder2ProtocolDecoderTest extends ProtocolTest {
var decoder = new Minifinder2ProtocolDecoder(null);
+ verifyPositions(decoder, binary(
+ "ab10350015ae59010110013836333932313033333836353231360924723a12610042535a182ac0f6b4f2923100c900af02215c2b9bfb5461736b4c4d53"));
+
verifyNull(decoder, binary(
"ab10150076f1320003100133353534363530373130323933303602105a"));
+ verifyNull(decoder, binary(
+ "AB101400594A01000310013836333932323033343437333734350112"));
+
verifyPositions(decoder, binary(
"ab183200c6bd020101100138363838333230343730323133363209247a0b146090087a641528c03a79ba309be5dec3c2024122c21c2407676267"));
diff --git a/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java
index dc60edb15..ddfa6ad8b 100644
--- a/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class MobilogixProtocolDecoderTest extends ProtocolTest {
@@ -10,12 +11,58 @@ public class MobilogixProtocolDecoderTest extends ProtocolTest {
var decoder = new MobilogixProtocolDecoder(null);
+ verifyAttributes(decoder, text(
+ "[2021-08-20 19:27:14,T14,1,V1.3.5,201909000982,53,12.18"));
+
+ verifyAttributes(decoder, text(
+ "\r\n[2021-08-20 19:27:14,T14,1,V1.3.5,201909000982,53,12.18"));
+
verifyNull(decoder, text(
"[2020-12-01 14:00:22,T1,1,V1.1.1,201951132031,,,12345678,724108005415815,359366080211420"));
+ verifyNull(decoder, text(
+ "[2020-10-25 20:44:08,T8,1,V1.2.3,201951132044,3596"));
+
+ verifyPosition(decoder, text(
+ "[2020-10-25 20:45:09,T9,1,V1.2.3,201951132044,59,10.50,701,-25.236860,-45.708530,0,314"));
+
+ verifyPosition(decoder, text(
+ "[2021-10-25 20:46:10,T10,1,V1.2.3,201951132044,59,0.50,082,-25.909590,-47.045387,0,145"));
+
+ verifyPosition(decoder, text(
+ "[2021-10-25 20:47:11,T11,1,V1.2.3,201951132044,3F,9.23,991,-25.909262,-47.045387,1,341"));
+
+ verifyPosition(decoder, text(
+ "[2021-10-25 20:54:11,T12,1,V1.2.3,201951132044,3F,9.23,991,-25.909262,-47.045387,1,341"));
+
+ verifyAttributes(decoder, text(
+ "[2021-10-25 20:48:14,T14,1,V1.2.3,201951132044,51,0.50"));
+
+ verifyPosition(decoder, text(
+ "[2021-10-25 20:49:15,T15,1,V1.2.3,201951132044,59,0.50,591,-25.908621,-47.045971,2,127"));
+
+ verifyNull(decoder, text(
+ "[2021-10-25 20:50:16,T16,1,V1.2.3,201951132044,1"));
+
+ verifyPosition(decoder, text(
+ "[2021-10-25 20:51:21,T21,1,V1.2.3,201951132044,37,12.18,961,-25.932310,-47.022415,0,82"));
+
+ verifyPosition(decoder, text(
+ "[2021-10-25 20:52:22,T22,1,V1.2.3,201951132044,1B,12.05,082,-25.909590,-47.045387,0,145"));
+
+ verifyPosition(decoder, text(
+ "[2021-10-25 20:53:31,T31,1,V1.2.3,201951132044,D3,26.17,961,-23.458092,-46.392132,0,8"));
+
+ verifyAttribute(decoder, text(
+ "[2021-10-25 20:55:11,T13,1,V1.2.3,201951132044,3F,9.23,991,-25.909262,-47.045387,1,341"),
+ Position.KEY_TYPE, "T13");
+
verifyPosition(decoder, text(
"[2020-12-01 12:01:09,T3,1,V1.1.1,201951132031,3B,12.99,022,-23.563410,-46.588055,0,0"));
+ verifyPosition(decoder, text(
+ "[2021-09-30 20:06:35,T21,1,V1.3.5,201950130047,37,14.97,092,-23.494715,-46.851341,0,240,4.08,0,19516,4431,0.78,724,10,09111,00771,31,4680"));
+
}
}
diff --git a/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java
index 71ad22a96..301b6102b 100644
--- a/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MxtProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class MxtProtocolDecoderTest extends ProtocolTest {
var decoder = new MxtProtocolDecoder(null);
verifyPosition(decoder, binary(
+ "01a631a7627b00087dc41c40850006aab70affecdf23fd32200080000600000000000000000000001b2ff03b1bb9c4c60214f40100050000006c2d0000f427600051051101de0704"));
+
+ verifyPosition(decoder, binary(
"01a631144c7e0008643ad2f456fb2d49747cfe4cbe0ffd002008800000001021000fd43d3f1403000000ff300000f42760001031102445a81fda04"));
verifyPosition(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java b/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java
new file mode 100644
index 000000000..07b19651b
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/NavtelecomFrameDecoderTest.java
@@ -0,0 +1,44 @@
+package org.traccar.protocol;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class NavtelecomFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new NavtelecomFrameDecoder();
+
+ verifyFrame(
+ binary("404e5443010000000000000013004e452a3e533a383636373935303331343130363839"),
+ decoder.decode(null, null, binary("404e5443010000000000000013004e452a3e533a383636373935303331343130363839")));
+
+ verifyFrame(
+ binary("404e544301000000000000002a005e6c2a3e464c4558b01e1efffffe300a08080ffffe08000000580028002bc0000000000000b4000000000000"),
+ decoder.decode(null, null, binary("404e544301000000000000002a005e6c2a3e464c4558b01e1efffffe300a08080ffffe08000000580028002bc0000000000000b4000000000000")));
+
+ }
+
+ @Ignore
+ @Test
+ public void testDecodeFull() throws Exception {
+
+ var decoder = new NavtelecomFrameDecoder();
+
+ verifyFrame(
+ binary("404e5443010000000000000013004e452a3e533a383636373935303331343130363839"),
+ decoder.decode(null, null, binary("404e5443010000000000000013004e452a3e533a383636373935303331343130363839")));
+
+ verifyFrame(
+ binary("404e544301000000000000002a005e6c2a3e464c4558b01e1efffffe300a08080ffffe08000000580028002bc0000000000000b4000000000000"),
+ decoder.decode(null, null, binary("404e544301000000000000002a005e6c2a3e464c4558b01e1efffffe300a08080ffffe08000000580028002bc0000000000000b4000000000000")));
+
+ verifyFrame(
+ binary("7e4104022106000517c4ae2f6180a9000e2fc4ae2f61471dff0171b35801d2050000a9870e412801d9d096466a37061000009474270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0308000000000000090cf70900000826fa000200b3ad2b00000826fa000200aad75200000826fa000200aa9cae2f6158020000000000000000000a14000000000000000000000000000000000000000026000000032106000b17dbae2f6180a9000e33daae2f61a11dff01edb15801d00500009c50e83f2f01ecd09646793706100000ab74270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0408000000000000090bf70900000826fa000200af8bc70000256cfa000200ab3e7c0000256cfa000200aad7ae2f61fd080000000000000000000a140100000000000000000000000000000000000000260000000421060054a0e7ae2f6180a9000e33e6ae2f61ba1dff01beb15801d305000038b977402201f0d09646163706100000b674270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0309000000000000080bf70900000826fa000200af8bc70000256cfa000200ab3e7c0000256cfa000200aad7ae2f6173040000000000000000000a14080000000000000000000000000000000000000026000000052106000517efae2f6180a9000f33efae2f61c21dff0166b15801df05000017f145404d00f5d09646693706100000bf74270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0408000000000000090cf70900000826fa000200af8bc70000256cfa000200ab3e7c0000256cfa000200aad7ae2f615b030000000000000000000a14020000000000000000000000000000000000000026000000a9"),
+ decoder.decode(null, null, binary("7e4104022106000517c4ae2f6180a9000e2fc4ae2f61471dff0171b35801d2050000a9870e412801d9d096466a37061000009474270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0308000000000000090cf70900000826fa000200b3ad2b00000826fa000200aad75200000826fa000200aa9cae2f6158020000000000000000000a14000000000000000000000000000000000000000026000000032106000b17dbae2f6180a9000e33daae2f61a11dff01edb15801d00500009c50e83f2f01ecd09646793706100000ab74270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0408000000000000090bf70900000826fa000200af8bc70000256cfa000200ab3e7c0000256cfa000200aad7ae2f61fd080000000000000000000a140100000000000000000000000000000000000000260000000421060054a0e7ae2f6180a9000e33e6ae2f61ba1dff01beb15801d305000038b977402201f0d09646163706100000b674270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0309000000000000080bf70900000826fa000200af8bc70000256cfa000200ab3e7c0000256cfa000200aad7ae2f6173040000000000000000000a14080000000000000000000000000000000000000026000000052106000517efae2f6180a9000f33efae2f61c21dff0166b15801df05000017f145404d00f5d09646693706100000bf74270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0408000000000000090cf70900000826fa000200af8bc70000256cfa000200ab3e7c0000256cfa000200aad7ae2f615b030000000000000000000a14020000000000000000000000000000000000000026000000a9")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java
index b8797daf3..fd22049fc 100644
--- a/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java
@@ -13,6 +13,18 @@ public class NavtelecomProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"404e5443010000000000000013004e452a3e533a383636373935303331343130363839"));
+ verifyNull(decoder, binary(
+ "404e544301000000000000002a005e6c2a3e464c4558b01e1efffffe300a08080ffffe08000000580028002bc0000000000000b4000000000000"));
+
+ verifyPositions(decoder, binary(
+ "7e4104022106000517c4ae2f6180a9000e2fc4ae2f61471dff0171b35801d2050000a9870e412801d9d096466a37061000009474270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0308000000000000090cf70900000826fa000200b3ad2b00000826fa000200aad75200000826fa000200aa9cae2f6158020000000000000000000a14000000000000000000000000000000000000000026000000032106000b17dbae2f6180a9000e33daae2f61a11dff01edb15801d00500009c50e83f2f01ecd09646793706100000ab74270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0408000000000000090bf70900000826fa000200af8bc70000256cfa000200ab3e7c0000256cfa000200aad7ae2f61fd080000000000000000000a140100000000000000000000000000000000000000260000000421060054a0e7ae2f6180a9000e33e6ae2f61ba1dff01beb15801d305000038b977402201f0d09646163706100000b674270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0309000000000000080bf70900000826fa000200af8bc70000256cfa000200ab3e7c0000256cfa000200aad7ae2f6173040000000000000000000a14080000000000000000000000000000000000000026000000052106000517efae2f6180a9000f33efae2f61c21dff0166b15801df05000017f145404d00f5d09646693706100000bf74270080ff7f00000000ffff8000000000ffffffffffffffffffffffffffff7f00000000ffffff0408000000000000090cf70900000826fa000200af8bc70000256cfa000200ab3e7c0000256cfa000200aad7ae2f615b030000000000000000000a14020000000000000000000000000000000000000026000000a9"));
+
+ verifyNull(decoder, binary(
+ "404e544301000000000000001300f7fc2a3e464c4558b00a0a45fffe00000000000000"));
+
+ verifyNull(decoder, binary(
+ "404e544301000000000000001300cbc02a3e464c4558b00a0a45fffe300a0e08000000"));
+
}
}
diff --git a/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java
index e170bc98e..edf508314 100644
--- a/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/PacificTrackProtocolDecoderTest.java
@@ -25,6 +25,9 @@ public class PacificTrackProtocolDecoderTest extends ProtocolTest {
var decoder = new PacificTrackProtocolDecoder(null);
verifyAttributes(decoder, binary(
+ "FB80019702808835275309000091108181B2C08F0143000E10000000010000001400010192DF0143288063810A8202835584D285B486E68780882D89C38A788BCE8C3A8D3C8E418F809073A008ACA16600A225A0C0000F4240C10003DF2CC200004E20C3004428C0C4000008C6C5000316A4E011314334424A57464758444C35333137373302A086AB569DFE110E02A8811203FF81000190820100"));
+
+ verifyAttributes(decoder, binary(
"fb80c88181b00280883592151012618820b18b1f123340f004c90001300301928a0080008100c00000000091971c0b0417020d074df0ec03c242550b20081d0c009a0601a1855571a30000"));
verifyAttributes(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java
index 802d65ab9..5d22344fa 100644
--- a/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java
@@ -11,6 +11,22 @@ public class StartekProtocolDecoderTest extends ProtocolTest {
var decoder = new StartekProtocolDecoder(null);
+ verifyPosition(decoder, text(
+ "&&o142,860262050066062,000,27,,211111070826,V,28.653435,-106.077455,0,0.0,0,151,1412,918,0|0|4708|01402D19,6,0000001A,02,00,04C0|016C|0000|0000,1,,,BB"));
+
+ verifyPosition(decoder, text(
+ "&&W149,865429043319537,000,0,,211103013512,A,22.679003,114.045085,16,1.1,0,271,76,109075,460|0|249F|000010C5,19,0000003E,00,00,0A57|0168|0000|0000,1,0100000C"));
+
+ verifyAttribute(decoder, text(
+ "&&:23,860262050015424,129,OKA2"),
+ Position.KEY_RESULT, "129,OK");
+
+ verifyPosition(decoder, text(
+ "&&X152,861157040151686,000,18,,210907163833,A,10.232715,-67.880423,11,1.4,0,275,437,34804,734|2|3EE4|00579406,28,00000015,00,00,0000|017D|0000|0000,1,010000,,9A"));
+
+ verifyPosition(decoder, text(
+ "&&o125,861157040554384,000,0,,210702235150,A,27.263505,153.037061,11,1.2,0,0,31,5125,505|1|7032|8C89802,20,0000002D,00,00,01E2|019DF0"));
+
verifyAttribute(decoder, text(
"&&a152,860262050010565,000,53,8F5300,210528015706,A,-38.229746,145.043446,6,1.5,0,285,84,2102994,505|1|306E|082D6101,31,0000003D,02,02,04C0|01A0|0000|0000,1,,DC"),
Position.KEY_DRIVER_UNIQUE_ID, "8F5300");
diff --git a/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java
new file mode 100644
index 000000000..74b30f9eb
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/StbProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class StbProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new StbProtocolDecoder(null);
+
+ verifyAttributes(decoder, text(
+ "{\"attrList\":[{\"id\":\"02101001\",\"value\":\"510101051161205774\"},{\"id\":\"02105001\",\"value\":\"-61\"},{\"id\":\"02102001\",\"value\":\"1\"},{\"doorId\":\"1\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"1\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"1\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"1\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"2\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"2\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"2\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"2\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"3\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"3\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"3\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"3\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"4\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"4\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"4\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"4\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"5\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"5\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"5\",\"id\":\"02103001\",\"value\":\"1\"},{\"doorId\":\"5\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"6\",\"id\":\"02104001\",\"value\":\"2\"},{\"doorId\":\"6\",\"id\":\"02106001\",\"value\":\"BT106002320JPZZ210718002\"},{\"doorId\":\"6\",\"id\":\"02109001\",\"value\":\"98\"},{\"doorId\":\"6\",\"id\":\"02110001\",\"value\":\"100\"},{\"doorId\":\"6\",\"id\":\"01118001\",\"value\":\"27\"},{\"doorId\":\"6\",\"id\":\"01119001\",\"value\":\"26\"},{\"doorId\":\"6\",\"id\":\"01120001\",\"value\":\"28\"},{\"doorId\":\"6\",\"id\":\"02114001\",\"value\":\"0\"},{\"doorId\":\"6\",\"id\":\"02116001\",\"value\":\"0\"},{\"doorId\":\"6\",\"id\":\"02117001\",\"value\":\"0\"},{\"doorId\":\"6\",\"id\":\"01121001\",\"value\":\"2\"},{\"doorId\":\"6\",\"id\":\"02130001\",\"value\":\"0\"},{\"doorId\":\"6\",\"id\":\"01122001\",\"value\":\"4\"},{\"doorId\":\"6\",\"id\":\"02001001\",\"value\":\"000\"},{\"doorId\":\"6\",\"id\":\"02002001\",\"value\":\"000\"},{\"doorId\":\"6\",\"id\":\"01116001\",\"value\":\"20\"},{\"doorId\":\"6\",\"id\":\"01117001\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117002\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117003\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117004\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117005\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117006\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117007\",\"value\":\"3325\"},{\"doorId\":\"6\",\"id\":\"01117008\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117009\",\"value\":\"3325\"},{\"doorId\":\"6\",\"id\":\"01117010\",\"value\":\"3326\"},{\"doorId\":\"6\",\"id\":\"01117011\",\"value\":\"3326\"},{\"doorId\":\"6\",\"id\":\"01117012\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117013\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117014\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117015\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117016\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117017\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117018\",\"value\":\"3323\"},{\"doorId\":\"6\",\"id\":\"01117019\",\"value\":\"3324\"},{\"doorId\":\"6\",\"id\":\"01117020\",\"value\":\"3323\"},{\"batteryId\":\"BT106002320JPZZ210718002\",\"doorId\":\"6\",\"id\":\"02103001\",\"value\":\"1\"},{\"batteryId\":\"BT106002320JPZZ210718002\",\"doorId\":\"6\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"7\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"7\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"7\",\"id\":\"02103001\",\"value\":\"1\"},{\"doorId\":\"7\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"8\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"8\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"8\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"8\",\"id\":\"02118001\",\"value\":\"1\"},{\"id\":\"02111001\",\"value\":\"0.0\"},{\"id\":\"02112001\",\"value\":\"0.0\"},{\"id\":\"02107001\",\"value\":\"229.1\"},{\"id\":\"02108001\",\"value\":\"1.005\"},{\"id\":\"02120001\",\"value\":\"143.76\"},{\"id\":\"02113001\",\"value\":\"29\"},{\"id\":\"02119001\",\"value\":\"1\"}],\"devId\":\"CHZD08KPD0210425046\",\"isFull\":1,\"msgType\":310,\"txnNo\":\"1636707944778\"}"));
+
+ verifyAttributes(decoder, text(
+ "{\"attrList\":[{\"doorId\":\"4\",\"id\":\"02103001\",\"value\":\"1\"},{\"doorId\":\"2\",\"id\":\"02103001\",\"value\":\"0\"},{\"id\":\"02120001\",\"value\":\"11.37\"},{\"doorId\":\"6\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"5\",\"id\":\"02103001\",\"value\":\"0\"},{\"id\":\"02105001\",\"value\":\"-150\"},{\"id\":\"02102001\",\"value\":\"1\"},{\"doorId\":\"5\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"5\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"1\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"1\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"6\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"7\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"3\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"1\",\"id\":\"02106001\",\"value\":\"\"},{\"id\":\"02101001\",\"value\":\"\"},{\"id\":\"02119001\",\"value\":\"1\"},{\"doorId\":\"6\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"8\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"3\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"2\",\"id\":\"02106001\",\"value\":\"\"},{\"id\":\"02108001\",\"value\":\"0.922\"},{\"doorId\":\"2\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"7\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"4\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"3\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"8\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"1\",\"id\":\"02103001\",\"value\":\"0\"},{\"doorId\":\"2\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"7\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"8\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"3\",\"id\":\"02106001\",\"value\":\"\"},{\"doorId\":\"4\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"8\",\"id\":\"02106001\",\"value\":\"\"},{\"id\":\"02112001\",\"value\":\"0.0\"},{\"doorId\":\"4\",\"id\":\"02104001\",\"value\":\"0\"},{\"id\":\"02111001\",\"value\":\"0.0\"},{\"id\":\"02113001\",\"value\":\"27\"},{\"doorId\":\"5\",\"id\":\"02118001\",\"value\":\"1\"},{\"doorId\":\"7\",\"id\":\"02104001\",\"value\":\"0\"},{\"doorId\":\"6\",\"id\":\"02118001\",\"value\":\"1\"},{\"id\":\"02107001\",\"value\":\"229.7\"}],\"devId\":\"CHZD08KPD0210425046\",\"isFull\":0,\"msgType\":310,\"txnNo\":\"1626153841985\"}"));
+
+ verifyNull(decoder, text(
+ "{\"devId\":\"CHZD08KPD0210425046\",\"devType\":2,\"hardVersion\":\"HDTTVA19\",\"msgType\":110,\"protocolVersion\":\"V1\",\"softVersion\":\"3.1.8\",\"switchCabStatus\":\"1\",\"txnNo\":\"1625212741537\"}"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
index 8cc4148f0..a9720f437 100644
--- a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
@@ -82,6 +82,15 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
var decoder = new SuntechProtocolDecoder(null);
+ verifyPosition(decoder, buffer(
+ "BLE;1140000053;114;1.0.1;20211001;17:27:09;+28.433465;-82.565891;1;-43;-46;-41;ACB89523EF68;247;0;0"));
+
+ verifyPosition(decoder, buffer(
+ "BLE;0820012345;82;1.0.0;20191203;17:00:51;+32.691615;-117.297160;2;-32;-100;33;AABBCCDDEEFF;12;18;52;1;-44;44;112233445566;32;69;101"));
+
+ verifyNull(decoder, buffer(
+ "BSA;0820012345;001FFF;82;1.0.0;1;20191203;17:00:51;+32.691615;-117.297160;1;-55;68:11:6A:FD:1A:A7;6AA5;1DE8"));
+
verifyAttribute(decoder, buffer(
"ST300UEX;511331307;45;311;20210420;12:41:01;12361;-01.280825;-047.931773;000.000;000.00;16;1;0;12.54;000000;23;GTSL|6|1|0|9255143|2|;6F;000276;0.0;1;00000000000000;0"),
Position.KEY_DRIVER_UNIQUE_ID, "9255143");
diff --git a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
index 61fb658a6..1dd4e8619 100644
--- a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
@@ -18,6 +18,9 @@ public class T800xProtocolDecoderTest extends ProtocolTest {
"27271000277bb30860112047066487210407022840000004e6215130c50fff620a0c1518000156"));
verifyPosition(decoder, binary(
+ "252514005901c00867730050941347001e46501e03e80064f2c0001401000041000000000000000000ffffffff160000034ec40021100719073800000000c2fb90c21291fd400000000003961237ffff0000002effffffffff"));
+
+ verifyPosition(decoder, binary(
"27270200497d880860112047066487470021040702270500006442d4e2e342f671b441000000008000008080881dff3900000384700640003c0000001e1e00641e30d2800000000000"));
verifyAttributes(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java b/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java
new file mode 100644
index 000000000..dc4afb784
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TechtoCruzFrameDecoderTest.java
@@ -0,0 +1,25 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+import static org.junit.Assert.assertEquals;
+
+public class TechtoCruzFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new TechtoCruzFrameDecoder();
+
+ assertEquals(
+ buffer("$$A35,RESPO|G33|8612345678910|CRUZ,*E3"),
+ decoder.decode(null, null, buffer("$$A35,RESPO|G33|8612345678910|CRUZ,*E3")));
+
+ assertEquals(
+ buffer("$$A120,8612345678910,211005105836,A,FLEX,KCB 947C,000.0,0,-1.38047,S,36.93951,E,1648.4,243.140,21,28,12.1,3.7,0,1,0,0,0,*F6"),
+ decoder.decode(null, null, buffer("$$A120,8612345678910,211005105836,A,FLEX,KCB 947C,000.0,0,-1.38047,S,36.93951,E,1648.4,243.140,21,28,12.1,3.7,0,1,0,0,0,*F6")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java
new file mode 100644
index 000000000..4cef682b4
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TechtoCruzProtocolDecoderTest.java
@@ -0,0 +1,21 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class TechtoCruzProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new TechtoCruzProtocolDecoder(null);
+
+ verifyPosition(decoder, text(
+ "$$A120,8612345678910,211005105836,A,FLEX,KCB 947C,000.0,0,-1.38047,S,36.93951,E,1648.4,243.140,21,28,12.1,3.7,0,1,0,0,0,*F6"));
+
+ verifyNull(decoder, text(
+ "$$A35,RESPO|G33|8612345678910|CRUZ,*E3"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java
index 8f5b5ebbb..e8da93006 100644
--- a/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/TopinProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class TopinProtocolDecoderTest extends ProtocolTest {
@@ -16,6 +17,14 @@ public class TopinProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"78780d0103593390754169634d0d0a"));
+ verifyAttribute(decoder, binary(
+ "7878006921120412565802010601071e4a9764071e4a9864010d0a"),
+ Position.KEY_ALARM, Position.ALARM_VIBRATION);
+
+ verifyAttribute(decoder, binary(
+ "787801940D0A"),
+ Position.KEY_ALARM, Position.ALARM_VIBRATION);
+
verifyAttributes(decoder, binary(
"78780A13424008196400041F000D0A"));
diff --git a/src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java b/src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java
new file mode 100644
index 000000000..d3ff13941
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/TopinProtocolEncoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Command;
+
+public class TopinProtocolEncoderTest extends ProtocolTest {
+
+ @Test
+ public void testEncode() throws Exception {
+
+ var encoder = new TopinProtocolEncoder(null);
+
+ Command command = new Command();
+ command.setDeviceId(1);
+ command.setType(Command.TYPE_SOS_NUMBER);
+ command.set(Command.KEY_INDEX, 1);
+ command.set(Command.KEY_PHONE, "13533333333");
+
+ verifyCommand(encoder, command, binary("78780C4131333533333333333333330D0A"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
index 0aeea0f1a..fba8f7db4 100644
--- a/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/TzoneProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class TzoneProtocolDecoderTest extends ProtocolTest {
var decoder = new TzoneProtocolDecoder(null);
verifyAttributes(decoder, binary(
+ "545a004d24240407010d0000018032100000031515090c052c2100000022030a033400201347000056860a03340020134700002feb0a03340020134700007d96000baa10211f01810127022d000001ebe00d0a"));
+
+ verifyAttributes(decoder, binary(
"545A004B2424041302000000086706003324776413030C0A1A2900180513030C0A1A25080F7E1028CAC830000A000F0000000005000AA53201633D05046000010009AA201737019408973B0032B0260D0A"));
verifyAttributes(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java
index c1acfd740..753063d26 100644
--- a/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/UuxProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class UuxProtocolDecoderTest extends ProtocolTest {
var decoder = new UuxProtocolDecoder(null);
+ verifyNull(decoder, binary(
+ "81910b01ff"));
+
verifyAttributes(decoder, binary(
"81910c5a9031395533443630363631051e061a1e07397079712a000000000000413133333135332e333939304e30333531322e393837324530303031303030303000000200000000001f303036323236303030303030303030300000ffff"));
diff --git a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
index ddfc8e619..98e83f491 100644
--- a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
@@ -15,6 +15,16 @@ public class WatchProtocolDecoderTest extends ProtocolTest {
var decoder = new WatchProtocolDecoder(null);
+ verifyAttribute(decoder, buffer(
+ "[3G*2104326058*000E*btemp2,1,35.29]"),
+ Position.PREFIX_TEMP + 1, 35.29);
+
+ verifyPosition(decoder, buffer(
+ "[SG*9059056143*0053*UD,251021,223408,A,41.46500,N,081.53128,W,0.926,000,0,00,70,70,0,50,00000000,0,1,,,,00]"));
+
+ verifyPosition(decoder, buffer(
+ "[3G*2104326058*00E9*UD_LTE,300621,135101,A,32.162652,N,34.888748,E,30.84,265.158,65.621,18,100,83,0,0,00000000,1,1,425,01,10223,8012811,100,3,ES4104,22:74:1d:39:64:ff,-46,metropoline-wifi,a8:3f:a1:e0:66:ba,-89,Egged.co.il,00:0c:42:51:cf:cd,-81,1.7055488]"));
+
verifyPosition(decoder, buffer(
"[3G*358839237678820*0122*ALCUSTOMER1,251120,081821,V,0.0,N,0.0,E,2.58,317.462,35.147,14,100,2,11089,0,00100008,1,1,460,01,42308,101992452,100,5,shizhou1,44:56:e2:03:ea:2a,-69,FART3,30:0d:9e:bb:fa:4d,-70,ZKY-A209,88:c3:97:c1:f4:7f,-73,ChinaNet-HNeD,e8:84:c6:21:7c:dc,-77,,30:45:96:10:14:5d,-79,1.2035439]"));
diff --git a/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java b/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java
new file mode 100644
index 000000000..aeca95376
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Xexun2FrameDecoderTest.java
@@ -0,0 +1,19 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Xexun2FrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new Xexun2FrameDecoder();
+
+ verifyFrame(
+ binary("faaf0014000286147503139003400032f2b001002f4260b0d6a0008019104a3378323130333135317c323130333132303100704020308715758089502023015648643670faaf"),
+ decoder.decode(null, null, binary("faaf0014000286147503139003400032f2b001002f4260b0d6a0008019104a3378323130333135317c323130333132303100704020308715758089502023015648643670faaf")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java
new file mode 100644
index 000000000..89c499016
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/Xexun2ProtocolDecoderTest.java
@@ -0,0 +1,27 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Xexun2ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new Xexun2ProtocolDecoder(null);
+
+ verifyPositions(decoder, false, binary(
+ "FAAF00140004863921033475388000AFB7D203003800380038F9608A7B801E0060820205788A205DF523D97844FDB90443D37844FDB90465CFB4FBF946B0E8CEF639095803F8CC00000002350000004000FA608A7BA81E0060820205788A205DF523D97844FDB90443D2F639095803F8CFB4FBF946B0E8CE7844FDB90465CD00000002350000004000FB608A7BD01E0060820205788A205DF523D97844FDB90443D2F639095803F8CFB4FBF946B0E8CE7844FDB90465CD00000002350000004000FAAF"));
+
+ verifyPositions(decoder, false, binary(
+ "faaf0014000286147503139003400032f2b001002f4260b0d6a0008019104a3378323130333135317c323130333132303100704020308715758089502023015648643670faaf"));
+
+ verifyPositions(decoder, false, binary(
+ "FAAF0014000486188105421927500035E6D2010032FC60EC264D00002003000000020205444E6DD72699D674427F7712CBC3BCF2AFD910BAC1C6FBE474CFC7A9B4FBE474CFC7A6FAAF"));
+
+ verifyPositions(decoder, binary(
+ "FAAF00140CF18626490454584530002BF2DD0200130013D360EFD7F514006402010D46322C4A450BA026D460EFD7FA14006402010D46322C4A450BA026FAAF"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java
index 9562a75e2..1b3f6fcbb 100644
--- a/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java
@@ -10,7 +10,7 @@ public class Xt2400ProtocolDecoderTest extends ProtocolTest {
var decoder = new Xt2400ProtocolDecoder(null);
- decoder.setConfig("\n::wycfg pcr[1] 012801030405060708090a1213c8545657585a656e7d2cd055595d5e71797a7b7c7e7f80818285866b\n");
+ decoder.setConfig("\n:wycfg pcr[1] 012801030405060708090a1213c8545657585a656e7d2cd055595d5e71797a7b7c7e7f80818285866b\n");
verifyPosition(decoder, binary(
"010ae85be10801a05d52d590030b12d1f9330be9290a0000ff10008b00000000000000000000000000000000000000000000000000000000000000000000000000003839333032363930323031303036363039373733000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000"));