aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-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
51 files changed, 1585 insertions, 185 deletions
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,