aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--schema/changelog-4.15.xml64
-rw-r--r--schema/changelog-master.xml1
-rw-r--r--setup/default.xml4
-rwxr-xr-xsetup/package.sh1
-rw-r--r--src/main/java/org/traccar/Context.java14
-rw-r--r--src/main/java/org/traccar/MainModule.java16
-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/database/DataManager.java3
-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.java4
-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/handler/events/GeofenceEventHandler.java11
-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/protocol/B2316Protocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/B2316ProtocolDecoder.java162
-rw-r--r--src/main/java/org/traccar/protocol/BceProtocolDecoder.java12
-rw-r--r--src/main/java/org/traccar/protocol/C2stekProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/DmtHttpProtocolDecoder.java88
-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.java125
-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/GoSafeProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java32
-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.java55
-rw-r--r--src/main/java/org/traccar/protocol/JsonFrameDecoder.java (renamed from src/main/java/org/traccar/protocol/StbFrameDecoder.java)2
-rw-r--r--src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java10
-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/PortmanProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java33
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/StbProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java117
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocolDecoder.java26
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java18
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocolDecoder.java33
-rw-r--r--src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java68
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocolDecoder.java4
-rw-r--r--src/test/java/org/traccar/geocoder/GeocoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/B2316ProtocolDecoderTest.java18
-rw-r--r--src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java9
-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/GoSafeProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.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.java3
-rw-r--r--src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java (renamed from src/test/java/org/traccar/protocol/StbFrameDecoderTest.java)4
-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/MobilogixProtocolDecoderTest.java41
-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.java9
-rw-r--r--src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java3
-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/TzoneProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/Xt2400ProtocolDecoderTest.java2
80 files changed, 1908 insertions, 190 deletions
diff --git a/README.md b/README.md
index 9d58c51b9..633140be1 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
## Overview
-Traccar is an open source GPS tracking system. This repository contains Java-based back-end service. It supports more than 170 GPS protocols and more than 1500 models of GPS tracking devices. Traccar can be used with any major SQL database system. It also provides easy to use [REST API](https://www.traccar.org/traccar-api/).
+Traccar is an open source GPS tracking system. This repository contains Java-based back-end service. It supports more than 200 GPS protocols and more than 2000 models of GPS tracking devices. Traccar can be used with any major SQL database system. It also provides easy to use [REST API](https://www.traccar.org/traccar-api/).
Other parts of Traccar solution include:
diff --git a/schema/changelog-4.15.xml b/schema/changelog-4.15.xml
new file mode 100644
index 000000000..57b9b341a
--- /dev/null
+++ b/schema/changelog-4.15.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<databaseChangeLog
+ xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
+ logicalFilePath="changelog-4.15">
+
+ <changeSet author="author" id="changelog-4.15">
+
+ <createTable tableName="tc_orders">
+ <column name="id" type="INT" autoIncrement="true">
+ <constraints primaryKey="true" />
+ </column>
+ <column name="uniqueid" type="VARCHAR(128)">
+ <constraints nullable="false" />
+ </column>
+ <column name="description" type="VARCHAR(512)" />
+ <column name="fromAddress" type="VARCHAR(512)" />
+ <column name="toAddress" type="VARCHAR(512)" />
+ <column name="attributes" type="VARCHAR(4000)">
+ <constraints nullable="false" />
+ </column>
+ </createTable>
+
+ <createTable tableName="tc_user_order">
+ <column name="userid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ <column name="orderid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ </createTable>
+
+ <addForeignKeyConstraint baseTableName="tc_user_order" baseColumnNames="userid" constraintName="fk_user_order_userid" referencedTableName="tc_users" referencedColumnNames="id" onDelete="CASCADE" />
+ <addForeignKeyConstraint baseTableName="tc_user_order" baseColumnNames="orderid" constraintName="fk_user_order_orderid" referencedTableName="tc_orders" referencedColumnNames="id" onDelete="CASCADE" />
+
+ <createTable tableName="tc_group_order">
+ <column name="groupid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ <column name="orderid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ </createTable>
+
+ <addForeignKeyConstraint baseTableName="tc_group_order" baseColumnNames="groupid" constraintName="fk_group_order_groupid" referencedTableName="tc_groups" referencedColumnNames="id" onDelete="CASCADE" />
+ <addForeignKeyConstraint baseTableName="tc_group_order" baseColumnNames="orderid" constraintName="fk_group_order_orderid" referencedTableName="tc_orders" referencedColumnNames="id" onDelete="CASCADE" />
+
+ <createTable tableName="tc_device_order">
+ <column name="deviceid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ <column name="orderid" type="INT">
+ <constraints nullable="false" />
+ </column>
+ </createTable>
+
+ <addForeignKeyConstraint baseTableName="tc_device_order" baseColumnNames="deviceid" constraintName="fk_device_order_deviceid" referencedTableName="tc_devices" referencedColumnNames="id" onDelete="CASCADE" />
+ <addForeignKeyConstraint baseTableName="tc_device_order" baseColumnNames="orderid" constraintName="fk_device_order_orderid" referencedTableName="tc_orders" referencedColumnNames="id" onDelete="CASCADE" />
+
+ </changeSet>
+
+</databaseChangeLog>
diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml
index df54737a2..245367de3 100644
--- a/schema/changelog-master.xml
+++ b/schema/changelog-master.xml
@@ -28,5 +28,6 @@
<include file="changelog-4.10.xml" relativeToChangelogFile="true" />
<include file="changelog-4.11.xml" relativeToChangelogFile="true" />
<include file="changelog-4.13.xml" relativeToChangelogFile="true" />
+ <include file="changelog-4.15.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
diff --git a/setup/default.xml b/setup/default.xml
index 20bf515d2..4d4de39b0 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -12,6 +12,7 @@
<entry key='web.port'>8082</entry>
<entry key='web.path'>./web</entry>
+ <entry key='web.persistSession'>false</entry>
<entry key='geocoder.enable'>false</entry>
<entry key='geocoder.type'>google</entry>
@@ -294,5 +295,8 @@
<entry key='flexiblereport.port'>5227</entry>
<entry key='thinkpower.port'>5228</entry>
<entry key='stb.port'>5229</entry>
+ <entry key='b2316.port'>5230</entry>
+ <entry key='hoopo.port'>5231</entry>
+ <entry key='dualcam.port'>5232</entry>
</properties>
diff --git a/setup/package.sh b/setup/package.sh
index bb890fbd0..e5f976b79 100755
--- a/setup/package.sh
+++ b/setup/package.sh
@@ -39,7 +39,6 @@ warn () {
VERSION=$1
PLATFORM=${2:-all}
-export EXTJS_PATH=$(cd ../..; pwd)/ext-6.2.0
PREREQ=true
check_requirement () {
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 e1a4d6b1c..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;
@@ -105,6 +107,11 @@ public class MainModule extends AbstractModule {
}
@Provides
+ public static ConnectionManager provideConnectionManager() {
+ return Context.getConnectionManager();
+ }
+
+ @Provides
public static Client provideClient() {
return Context.getClient();
}
@@ -188,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);
}
@@ -390,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/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/database/DataManager.java b/src/main/java/org/traccar/database/DataManager.java
index de6da8f23..00c802fde 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;
@@ -388,6 +389,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 3cd5b596e..f20aa79d6 100644
--- a/src/main/java/org/traccar/geocoder/JsonGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/JsonGeocoder.java
@@ -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();
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/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/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/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..c1a69981d 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.
@@ -177,10 +177,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..15cf84a5f 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,73 @@ 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"));
+
+ 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"));
+ }
+
+ int input = root.getInt("inputs");
+ int output = root.getInt("outputs");
+ int status = root.getInt("status");
+
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 0));
+
+ position.set(Position.KEY_INPUT, input);
+ position.set(Position.KEY_OUTPUT, output);
+ position.set(Position.KEY_STATUS, 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/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..457b5ae62
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/DualcamProtocolDecoder.java
@@ -0,0 +1,125 @@
+/*
+ * 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) {
+ if (BitUtil.check(settings, 26)) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(MSG_FILE_REQUEST);
+ String file = "%photof";
+ response.writeShort(file.length());
+ response.writeCharSequence(file, StandardCharsets.US_ASCII);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ } else {
+ ByteBuf response = Unpooled.buffer();
+ 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/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/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 281234ebb..45218aba2 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -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,11 @@ 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_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:
@@ -142,9 +147,11 @@ 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_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 +170,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;
@@ -311,7 +319,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);
@@ -690,9 +698,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
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_LBS_WIFI || type == MSG_LBS_2 || type == MSG_WIFI_3 || type == MSG_WIFI_5) {
- boolean longFormat = type == MSG_LBS_2 || type == MSG_WIFI_3;
+ 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())
@@ -703,7 +711,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();
@@ -1131,7 +1141,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else if (type == MSG_GPS_MODULAR) {
- return decodeExtendedModular(buf, deviceSession);
+ return decodeExtendedModular(channel, buf, deviceSession);
} else {
@@ -1142,7 +1152,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());
@@ -1243,6 +1253,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/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..5db7f0bc0
--- /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(eventData.getString("receiveTime")).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 37d7ae718..3c01c0468 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -53,12 +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;
@@ -71,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();
@@ -135,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 {
@@ -176,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);
@@ -215,6 +222,33 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
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;
@@ -332,6 +366,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)) {
@@ -389,6 +430,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;
}
diff --git a/src/main/java/org/traccar/protocol/StbFrameDecoder.java b/src/main/java/org/traccar/protocol/JsonFrameDecoder.java
index 6a4157f17..b2d7fbd53 100644
--- a/src/main/java/org/traccar/protocol/StbFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/JsonFrameDecoder.java
@@ -20,7 +20,7 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import org.traccar.BaseFrameDecoder;
-public class StbFrameDecoder extends BaseFrameDecoder {
+public class JsonFrameDecoder extends BaseFrameDecoder {
@Override
protected Object decode(
diff --git a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
index d745153a4..37c1674d4 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) {
@@ -176,7 +176,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);
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/MegastekProtocolDecoder.java b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
index e63dd3b32..7233280c2 100644
--- a/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MegastekProtocolDecoder.java
@@ -423,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/MobilogixProtocolDecoder.java b/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java
index 8677ba9ec..ba70a8884 100644
--- a/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MobilogixProtocolDecoder.java
@@ -39,7 +39,7 @@ public class MobilogixProtocolDecoder extends BaseProtocolDecoder {
.text("[")
.number("(dddd)-(dd)-(dd) ") // date (yyyymmdd)
.number("(dd):(dd):(dd),") // time (hhmmss)
- .number("Td,") // type
+ .number("Td+,") // type
.number("d+,") // device type
.expression("[^,]+,") // protocol version
.expression("([^,]+),") // serial number
@@ -60,7 +60,7 @@ public class MobilogixProtocolDecoder extends BaseProtocolDecoder {
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
String sentence = (String) msg;
- String type = sentence.substring(21, 21 + 2);
+ String type = sentence.substring(21, sentence.indexOf(',', 21));
if (channel != null) {
String time = sentence.substring(1, 20);
@@ -68,12 +68,12 @@ 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;
}
@@ -88,6 +88,8 @@ public class MobilogixProtocolDecoder extends BaseProtocolDecoder {
}
position.setDeviceId(deviceSession.getDeviceId());
+ position.set(Position.KEY_TYPE, type);
+
int status = parser.nextHexInt();
position.set(Position.KEY_IGNITION, BitUtil.check(status, 2));
position.set(Position.KEY_MOTION, BitUtil.check(status, 3));
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 29ad62e48..29ce8c41e 100644
--- a/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
+++ b/src/main/java/org/traccar/protocol/NavtelecomProtocol.java
@@ -15,20 +15,17 @@
*/
package org.traccar.protocol;
-import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
-import java.nio.ByteOrder;
-
public class NavtelecomProtocol extends BaseProtocol {
public NavtelecomProtocol() {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 12, 2, 2, 0, true));
+ 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/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/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 aae764993..65d295dc3 100644
--- a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
@@ -77,7 +77,7 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
.text(",")
.number("d,") // extended
.expression("([^,]+)?,") // fuel
- .expression("([^,]+)?") // temperature
+ .expression("([^,]+)?,?") // temperature
.groupEnd("?")
.groupEnd("?")
.compile();
@@ -168,8 +168,12 @@ 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);
diff --git a/src/main/java/org/traccar/protocol/StbProtocol.java b/src/main/java/org/traccar/protocol/StbProtocol.java
index ca95787d0..002ed86c7 100644
--- a/src/main/java/org/traccar/protocol/StbProtocol.java
+++ b/src/main/java/org/traccar/protocol/StbProtocol.java
@@ -27,7 +27,7 @@ public class StbProtocol extends BaseProtocol {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new StbFrameDecoder());
+ 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/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 72b277c8c..7f7873e50 100644
--- a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
@@ -58,6 +58,8 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
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) {
@@ -134,11 +136,12 @@ 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);
@@ -343,12 +346,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);
@@ -473,7 +483,9 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
}
}
- 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/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/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..249915b39 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.
@@ -21,6 +21,7 @@ import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.Protocol;
+import org.traccar.helper.BcdUtil;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
@@ -256,9 +257,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 +288,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) {
diff --git a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
index a6b89dde4..4990cfd65 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
@@ -143,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,
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/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..bed56ba30 100644
--- a/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/DmtHttpProtocolDecoderTest.java
@@ -11,6 +11,15 @@ public class DmtHttpProtocolDecoderTest extends ProtocolTest {
var decoder = new DmtHttpProtocolDecoder(null);
+ 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/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/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 fd1eb3040..3caba32e6 100644
--- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
@@ -17,6 +17,9 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"78780D01086471700328358100093F040D0A"));
+ verifyNotNull(decoder, binary(
+ "797900377000000001020035000103002c0004616219d00043000b013601048153931500001a0001000808652820400643521000000101004e46760d0a"));
+
verifyNull(decoder, binary(
"7878171915061810051a01f90101700d08c8f50c0000065494ae0d0a"));
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..bf94c5dc5 100644
--- a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
@@ -15,6 +15,9 @@ public class HuabaoProtocolDecoderTest extends ProtocolTest {
"7E01000021013345678906000F002C012F373031313142534A2D4D3742203030303030303001D4C1423838383838B47E"));
verifyNotNull(decoder, binary(
+ "7e207002940121523001530047210927151009000e002d80ac210927151010000e002d80ab210927151011000e002d80ac210927151012000e002e80ab210927151013000e002d80ab210927151014000e002d80ab210927151015000e002d80ab210927151016000e002d80aa210927151017000e002e80ab210927151018000e002d80ab210927151019000e002e80ac210927151020000e002d80ab210927151021000e002d80ab210927151022000d002d80ac210927151023000e002d80ac210927151024000e002e80ab210927151025000e002e80b0210927151026000e002e80ab210927151027000e002d80ab210927151028000e002e80b0210927151029000e002d80b0210927151030000e002e80ab210927151031000e002d80ab210927151032000e002d80aa210927151033000e002d80ab210927151034000e002d80ab210927151035000e002d80ab210927151036000e002d80ab210927151037000e002d80ab210927151038000e002d80b0210927151039000d002e80aa210927151040000e002d80ab210927151041000e002d80a5210927151042000e002e80ab210927151043000e002d80aa210927151044000e002d80ab210927151045000e002d80ab210927151046000e002d80ac210927151047000e002e80ab210927151048000e002e80a5210927151049000e002d80ab210927151050000e002d80ab210927151051000e002d80ab210927151052000e002d80ab210927151053000e002d80aa210927151054000e002e80b0210927151055000e002e80ab210927151056000e002d80ac210927151057000e002e80ab210927151058000e002d80ab210927151059000e002e80ab210927151100000e002d80ab210927151101000e002e80aa210927151102000e002d80a6210927151103000e002e80a5847e"));
+
+ verifyNotNull(decoder, binary(
"7e07040226046110426684002b000601005f0000000000000000000000000000000000000000000021031410530001040000000030011b310101e4020064e50101e60100e7080000000000000000eb2101cc00253510260100000000000000000000000000000000000000000000000000005f00000000004c0001000000000000000000000000000021031410531401040000000030011f310103e4020064e50101e60100e7080000000000000000eb2101cc0025351026012535100f32263d11f931000000000000000000000000000000005f00000000004c0001000000000000000000000000000021031410534001040000000030011f310104e4020064e50101e60100e7080000000000000000eb2101cc002535102601263d11f92d0000000000000000000000000000000000000000005f00000000004c00010000000000000000000000000000210314105350010400000000300118310104e4020064e50101e60100e7080000000000000000eb2101cc002535102601263d11f92e25350f6f2c263d120d2c00000000000000000000005f00000000004c0001000000000000000000000000000021031410540001040000000030011d310105e4020064e50101e60100e7080000000000000000eb2101cc002535102601263d11f93025350f6f2e263d120d2e00000000000000000000003c00000000004c0003015ae3e106c82ab900000010010b21031410540901040000000030011b310105e4020064e50101e60100e7080000000000000000f97e"));
verifyAttribute(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/StbFrameDecoderTest.java b/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java
index 45d998fb1..42777e419 100644
--- a/src/test/java/org/traccar/protocol/StbFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/JsonFrameDecoderTest.java
@@ -3,12 +3,12 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
-public class StbFrameDecoderTest extends ProtocolTest {
+public class JsonFrameDecoderTest extends ProtocolTest {
@Test
public void testDecode() throws Exception {
- var decoder = new StbFrameDecoder();
+ var decoder = new JsonFrameDecoder();
verifyFrame(
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/MobilogixProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MobilogixProtocolDecoderTest.java
index dc60edb15..efa46b9f6 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 {
@@ -13,9 +14,49 @@ public class MobilogixProtocolDecoderTest extends ProtocolTest {
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"));
+
+ verifyNull(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 8b22c6e79..fd22049fc 100644
--- a/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/NavtelecomProtocolDecoderTest.java
@@ -14,8 +14,17 @@ public class NavtelecomProtocolDecoderTest extends ProtocolTest {
"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/StartekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java
index e85672326..1fbe71988 100644
--- a/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java
@@ -16,6 +16,9 @@ public class StartekProtocolDecoderTest extends ProtocolTest {
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(
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/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/WatchProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
index 4544b5827..98e83f491 100644
--- a/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/WatchProtocolDecoderTest.java
@@ -20,6 +20,9 @@ public class WatchProtocolDecoderTest extends ProtocolTest {
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(
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"));