diff options
Diffstat (limited to 'src/main')
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, |