aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/traccar/BaseHttpProtocolDecoder.java15
-rw-r--r--src/main/java/org/traccar/BaseProtocol.java22
-rw-r--r--src/main/java/org/traccar/BaseProtocolDecoder.java28
-rw-r--r--src/main/java/org/traccar/Main.java58
-rw-r--r--src/main/java/org/traccar/MainModule.java7
-rw-r--r--src/main/java/org/traccar/Protocol.java7
-rw-r--r--src/main/java/org/traccar/WebDataHandler.java141
-rw-r--r--src/main/java/org/traccar/api/ExtendedObjectResource.java2
-rw-r--r--src/main/java/org/traccar/api/HealthCheckService.java91
-rw-r--r--src/main/java/org/traccar/api/resource/PositionResource.java11
-rw-r--r--src/main/java/org/traccar/config/Keys.java38
-rw-r--r--src/main/java/org/traccar/database/ActiveDevice.java13
-rw-r--r--src/main/java/org/traccar/database/BaseObjectManager.java60
-rw-r--r--src/main/java/org/traccar/database/CommandsManager.java48
-rw-r--r--src/main/java/org/traccar/database/ConnectionManager.java4
-rw-r--r--src/main/java/org/traccar/database/DeviceManager.java118
-rw-r--r--src/main/java/org/traccar/database/DriversManager.java51
-rw-r--r--src/main/java/org/traccar/database/ExtendedObjectManager.java78
-rw-r--r--src/main/java/org/traccar/database/GroupsManager.java5
-rw-r--r--src/main/java/org/traccar/database/LdapProvider.java36
-rw-r--r--src/main/java/org/traccar/database/SimpleObjectManager.java30
-rw-r--r--src/main/java/org/traccar/database/UsersManager.java6
-rw-r--r--src/main/java/org/traccar/geocoder/FactualGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geocoder/MapQuestGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geocoder/OpenCageGeocoder.java12
-rw-r--r--src/main/java/org/traccar/helper/Checksum.java8
-rw-r--r--src/main/java/org/traccar/helper/ServletHelper.java26
-rw-r--r--src/main/java/org/traccar/notification/NotificatorManager.java4
-rw-r--r--src/main/java/org/traccar/notificators/NotificatorPushover.java100
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java179
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java82
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocol.java9
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java80
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java108
-rw-r--r--src/main/java/org/traccar/protocol/BceProtocolDecoder.java16
-rw-r--r--src/main/java/org/traccar/protocol/BlueProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/BlueProtocolDecoder.java63
-rw-r--r--src/main/java/org/traccar/protocol/EskyProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/EskyProtocolDecoder.java20
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java28
-rw-r--r--src/main/java/org/traccar/protocol/GatorProtocolDecoder.java21
-rw-r--r--src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java13
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java29
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java13
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java28
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/OkoProtocolDecoder.java23
-rw-r--r--src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java31
-rw-r--r--src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java23
-rw-r--r--src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java54
-rw-r--r--src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java17
-rw-r--r--src/main/java/org/traccar/protocol/RstProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java72
-rw-r--r--src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java18
-rw-r--r--src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java7
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java17
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocolDecoder.java11
-rw-r--r--src/main/java/org/traccar/protocol/TelicProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocolDecoder.java28
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocol.java11
-rw-r--r--src/test/java/org/traccar/geocoder/GeocoderTest.java2
-rw-r--r--src/test/java/org/traccar/helper/ChecksumTest.java10
-rw-r--r--src/test/java/org/traccar/helper/ServletHelperTest.java13
-rw-r--r--src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java38
-rw-r--r--src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java51
-rw-r--r--src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java (renamed from src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java)22
-rw-r--r--src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java16
-rw-r--r--src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java9
-rw-r--r--src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java6
88 files changed, 1902 insertions, 432 deletions
diff --git a/src/main/java/org/traccar/BaseHttpProtocolDecoder.java b/src/main/java/org/traccar/BaseHttpProtocolDecoder.java
index 57a68acac..b762be12c 100644
--- a/src/main/java/org/traccar/BaseHttpProtocolDecoder.java
+++ b/src/main/java/org/traccar/BaseHttpProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
*/
package org.traccar;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
@@ -29,9 +31,16 @@ public abstract class BaseHttpProtocolDecoder extends BaseProtocolDecoder {
}
public void sendResponse(Channel channel, HttpResponseStatus status) {
+ sendResponse(channel, status, null);
+ }
+
+ public void sendResponse(Channel channel, HttpResponseStatus status, ByteBuf buf) {
if (channel != null) {
- HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);
- response.headers().add(HttpHeaderNames.CONTENT_LENGTH, 0);
+ if (buf == null) {
+ buf = Unpooled.buffer(0);
+ }
+ HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, buf);
+ response.headers().add(HttpHeaderNames.CONTENT_LENGTH, buf.readableBytes());
channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
}
diff --git a/src/main/java/org/traccar/BaseProtocol.java b/src/main/java/org/traccar/BaseProtocol.java
index c0fd1e27f..bd3391822 100644
--- a/src/main/java/org/traccar/BaseProtocol.java
+++ b/src/main/java/org/traccar/BaseProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,12 +15,14 @@
*/
package org.traccar;
+import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
import io.netty.handler.codec.string.StringEncoder;
-import org.traccar.database.ActiveDevice;
import org.traccar.helper.DataConverter;
import org.traccar.model.Command;
+import java.net.SocketAddress;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
@@ -68,11 +70,6 @@ public abstract class BaseProtocol implements Protocol {
supportedTextCommands.addAll(Arrays.asList(commands));
}
- public void setSupportedCommands(String... commands) {
- supportedDataCommands.addAll(Arrays.asList(commands));
- supportedTextCommands.addAll(Arrays.asList(commands));
- }
-
@Override
public Collection<String> getSupportedDataCommands() {
Set<String> commands = new HashSet<>(supportedDataCommands);
@@ -88,15 +85,16 @@ public abstract class BaseProtocol implements Protocol {
}
@Override
- public void sendDataCommand(ActiveDevice activeDevice, Command command) {
+ public void sendDataCommand(Channel channel, SocketAddress remoteAddress, Command command) {
if (supportedDataCommands.contains(command.getType())) {
- activeDevice.write(command);
+ channel.writeAndFlush(new NetworkMessage(command, remoteAddress));
} else if (command.getType().equals(Command.TYPE_CUSTOM)) {
String data = command.getString(Command.KEY_DATA);
- if (BasePipelineFactory.getHandler(activeDevice.getChannel().pipeline(), StringEncoder.class) != null) {
- activeDevice.write(data);
+ if (BasePipelineFactory.getHandler(channel.pipeline(), StringEncoder.class) != null) {
+ channel.writeAndFlush(new NetworkMessage(data, remoteAddress));
} else {
- activeDevice.write(Unpooled.wrappedBuffer(DataConverter.parseHex(data)));
+ ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(data));
+ channel.writeAndFlush(new NetworkMessage(buf, remoteAddress));
}
} else {
throw new RuntimeException("Command " + command.getType() + " is not supported in protocol " + getName());
diff --git a/src/main/java/org/traccar/BaseProtocolDecoder.java b/src/main/java/org/traccar/BaseProtocolDecoder.java
index e6e02c2d6..8981fe4c8 100644
--- a/src/main/java/org/traccar/BaseProtocolDecoder.java
+++ b/src/main/java/org/traccar/BaseProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,10 +21,12 @@ import io.netty.handler.codec.http.HttpRequestDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.config.Config;
+import org.traccar.database.CommandsManager;
import org.traccar.database.ConnectionManager;
import org.traccar.database.IdentityManager;
import org.traccar.database.StatisticsManager;
import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Command;
import org.traccar.model.Device;
import org.traccar.model.Position;
@@ -227,20 +229,32 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
if (decodedMessage instanceof Position) {
position = (Position) decodedMessage;
} else if (decodedMessage instanceof Collection) {
- Collection positions = (Collection) decodedMessage;
+ Collection<Position> positions = (Collection) decodedMessage;
if (!positions.isEmpty()) {
- position = (Position) positions.iterator().next();
+ position = positions.iterator().next();
}
}
}
+ long deviceId = 0;
if (position != null) {
- connectionManager.updateDevice(
- position.getDeviceId(), Device.STATUS_ONLINE, new Date());
+ deviceId = position.getDeviceId();
} else {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession != null) {
- connectionManager.updateDevice(
- deviceSession.getDeviceId(), Device.STATUS_ONLINE, new Date());
+ deviceId = deviceSession.getDeviceId();
+ }
+ }
+ if (deviceId > 0) {
+ connectionManager.updateDevice(deviceId, Device.STATUS_ONLINE, new Date());
+ }
+ sendQueuedCommands(channel, remoteAddress, deviceId);
+ }
+
+ protected void sendQueuedCommands(Channel channel, SocketAddress remoteAddress, long deviceId) {
+ CommandsManager commandsManager = Context.getCommandsManager();
+ if (commandsManager != null) {
+ for (Command command : commandsManager.readQueuedCommands(deviceId)) {
+ protocol.sendDataCommand(channel, remoteAddress, command);
}
}
}
diff --git a/src/main/java/org/traccar/Main.java b/src/main/java/org/traccar/Main.java
index 6ebd1d399..47d6e91df 100644
--- a/src/main/java/org/traccar/Main.java
+++ b/src/main/java/org/traccar/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import com.google.inject.Guice;
import com.google.inject.Injector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.api.HealthCheckService;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
@@ -105,6 +106,27 @@ public final class Main {
}
}
+ private static void scheduleHealthCheck() {
+ HealthCheckService service = new HealthCheckService();
+ if (service.isEnabled()) {
+ new Timer().scheduleAtFixedRate(
+ service.createTask(), service.getPeriod(), service.getPeriod());
+ }
+ }
+
+ private static void scheduleDatabaseCleanup() {
+ new Timer().scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ Context.getDataManager().clearHistory();
+ } catch (SQLException error) {
+ LOGGER.warn("Clear history error", error);
+ }
+ }
+ }, 0, CLEAN_PERIOD);
+ }
+
public static void run(String configFile) {
try {
Context.init(configFile);
@@ -118,35 +140,19 @@ public final class Main {
Context.getWebServer().start();
}
- new Timer().scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- try {
- Context.getDataManager().clearHistory();
- } catch (SQLException error) {
- LOGGER.warn("Clear history error", error);
- }
- }
- }, 0, CLEAN_PERIOD);
+ scheduleHealthCheck();
+ scheduleDatabaseCleanup();
- Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(Thread t, Throwable e) {
- LOGGER.error("Thread exception", e);
- }
- });
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> LOGGER.error("Thread exception", e));
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- LOGGER.info("Shutting down server...");
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ LOGGER.info("Shutting down server...");
- if (Context.getWebServer() != null) {
- Context.getWebServer().stop();
- }
- Context.getServerManager().stop();
+ if (Context.getWebServer() != null) {
+ Context.getWebServer().stop();
}
- });
+ Context.getServerManager().stop();
+ }));
} catch (Exception e) {
LOGGER.error("Main method error", e);
throw new RuntimeException(e);
diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java
index 0957d9fe3..c3c0182d7 100644
--- a/src/main/java/org/traccar/MainModule.java
+++ b/src/main/java/org/traccar/MainModule.java
@@ -73,6 +73,7 @@ import org.traccar.reports.model.TripsConfig;
import javax.annotation.Nullable;
import javax.ws.rs.client.Client;
+import io.netty.util.Timer;
public class MainModule extends AbstractModule {
@@ -375,6 +376,12 @@ public class MainModule extends AbstractModule {
return new DriverEventHandler(identityManager);
}
+ @Singleton
+ @Provides
+ public static Timer provideTimer() {
+ return GlobalTimer.getTimer();
+ }
+
@Override
protected void configure() {
binder().requireExplicitBindings();
diff --git a/src/main/java/org/traccar/Protocol.java b/src/main/java/org/traccar/Protocol.java
index 3b66f2598..aea69b353 100644
--- a/src/main/java/org/traccar/Protocol.java
+++ b/src/main/java/org/traccar/Protocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,10 @@
*/
package org.traccar;
-import org.traccar.database.ActiveDevice;
+import io.netty.channel.Channel;
import org.traccar.model.Command;
+import java.net.SocketAddress;
import java.util.Collection;
public interface Protocol {
@@ -28,7 +29,7 @@ public interface Protocol {
Collection<String> getSupportedDataCommands();
- void sendDataCommand(ActiveDevice activeDevice, Command command);
+ void sendDataCommand(Channel channel, SocketAddress remoteAddress, Command command);
Collection<String> getSupportedTextCommands();
diff --git a/src/main/java/org/traccar/WebDataHandler.java b/src/main/java/org/traccar/WebDataHandler.java
index 64396de03..d6bfb126b 100644
--- a/src/main/java/org/traccar/WebDataHandler.java
+++ b/src/main/java/org/traccar/WebDataHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,12 @@ package org.traccar;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.ChannelHandler;
+import io.netty.util.Timer;
+import io.netty.util.Timeout;
+import io.netty.util.TimerTask;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import org.traccar.config.Config;
import org.traccar.config.Keys;
import org.traccar.database.IdentityManager;
@@ -27,9 +33,13 @@ import org.traccar.model.Position;
import org.traccar.model.Group;
import javax.inject.Inject;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.InvocationCallback;
import java.util.HashMap;
import java.util.Map;
import java.io.UnsupportedEncodingException;
@@ -39,10 +49,14 @@ import java.util.Calendar;
import java.util.Formatter;
import java.util.Locale;
import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
@ChannelHandler.Sharable
public class WebDataHandler extends BaseDataHandler {
+ private static final Logger LOGGER = LoggerFactory.getLogger(WebDataHandler.class);
+
private static final String KEY_POSITION = "position";
private static final String KEY_DEVICE = "device";
@@ -53,16 +67,33 @@ public class WebDataHandler extends BaseDataHandler {
private final String url;
private final String header;
private final boolean json;
+ private final boolean urlVariables;
+
+ private final boolean retryEnabled;
+ private final int retryDelay;
+ private final int retryCount;
+ private final int retryLimit;
+
+ private AtomicInteger deliveryPending;
@Inject
public WebDataHandler(
Config config, IdentityManager identityManager, ObjectMapper objectMapper, Client client) {
+
this.identityManager = identityManager;
this.objectMapper = objectMapper;
this.client = client;
this.url = config.getString(Keys.FORWARD_URL);
this.header = config.getString(Keys.FORWARD_HEADER);
this.json = config.getBoolean(Keys.FORWARD_JSON);
+ this.urlVariables = config.getBoolean(Keys.FORWARD_URL_VARIABLES);
+
+ this.retryEnabled = config.getBoolean(Keys.FORWARD_RETRY_ENABLE);
+ this.retryDelay = config.getInteger(Keys.FORWARD_RETRY_DELAY, 100);
+ this.retryCount = config.getInteger(Keys.FORWARD_RETRY_COUNT, 10);
+ this.retryLimit = config.getInteger(Keys.FORWARD_RETRY_LIMIT, 100);
+
+ this.deliveryPending = new AtomicInteger(0);
}
private static String formatSentence(Position position) {
@@ -152,35 +183,111 @@ public class WebDataHandler extends BaseDataHandler {
return request;
}
- @Override
- protected Position handlePosition(Position position) {
+ class AsyncRequestAndCallback implements InvocationCallback<Response>, TimerTask {
- String url;
- if (json) {
- url = this.url;
- } else {
+ private int retries = 0;
+ private Map<String, Object> payload;
+ private Invocation.Builder requestBuilder;
+ private MediaType mediaType = MediaType.APPLICATION_JSON_TYPE;
+
+ AsyncRequestAndCallback(Position position) {
+
+ String formattedUrl;
try {
- url = formatRequest(position);
+ formattedUrl = (json && !urlVariables) ? url : formatRequest(position);
} catch (UnsupportedEncodingException | JsonProcessingException e) {
throw new RuntimeException("Forwarding formatting error", e);
}
+
+ requestBuilder = client.target(formattedUrl).request();
+ if (header != null && !header.isEmpty()) {
+ for (String line: header.split("\\r?\\n")) {
+ String[] values = line.split(":", 2);
+ String headerName = values[0].trim();
+ String headerValue = values[1].trim();
+ if (headerName.equals(HttpHeaders.CONTENT_TYPE)) {
+ mediaType = MediaType.valueOf(headerValue);
+ } else {
+ requestBuilder.header(headerName, headerValue);
+ }
+ }
+ }
+
+ if (json) {
+ payload = prepareJsonPayload(position);
+ }
+
+ deliveryPending.incrementAndGet();
}
- Invocation.Builder requestBuilder = client.target(url).request();
+ private void send() {
+ if (json) {
+ try {
+ Entity<String> entity = Entity.entity(objectMapper.writeValueAsString(payload), mediaType);
+ requestBuilder.async().post(entity, this);
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException("Failed to serialize location to json", e);
+ }
+ } else {
+ requestBuilder.async().get(this);
+ }
+ }
- if (header != null && !header.isEmpty()) {
- for (String line: header.split("\\r?\\n")) {
- String[] values = line.split(":", 2);
- requestBuilder.header(values[0].trim(), values[1].trim());
+ private void retry() {
+ boolean scheduled = false;
+ try {
+ if (retryEnabled && deliveryPending.get() <= retryLimit && retries < retryCount) {
+ schedule();
+ scheduled = true;
+ }
+ } finally {
+ int pending = scheduled ? deliveryPending.get() : deliveryPending.decrementAndGet();
+ LOGGER.warn("Position forwarding failed: " + pending + " pending");
}
}
- if (json) {
- requestBuilder.async().post(Entity.json(prepareJsonPayload(position)));
- } else {
- requestBuilder.async().get();
+ private void schedule() {
+ Main.getInjector().getInstance(Timer.class).newTimeout(
+ this, retryDelay * (int) Math.pow(2, retries++), TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void completed(Response response) {
+ if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
+ deliveryPending.decrementAndGet();
+ } else {
+ retry();
+ }
+ }
+
+ @Override
+ public void failed(Throwable throwable) {
+ retry();
+ }
+
+ @Override
+ public void run(Timeout timeout) {
+ boolean sent = false;
+ try {
+ if (!timeout.isCancelled()) {
+ send();
+ sent = true;
+ }
+ } finally {
+ if (!sent) {
+ deliveryPending.decrementAndGet();
+ }
+ }
}
+ }
+
+ @Override
+ protected Position handlePosition(Position position) {
+
+ AsyncRequestAndCallback request = new AsyncRequestAndCallback(position);
+ request.send();
+
return position;
}
diff --git a/src/main/java/org/traccar/api/ExtendedObjectResource.java b/src/main/java/org/traccar/api/ExtendedObjectResource.java
index 007a7b1bd..9e554217e 100644
--- a/src/main/java/org/traccar/api/ExtendedObjectResource.java
+++ b/src/main/java/org/traccar/api/ExtendedObjectResource.java
@@ -55,8 +55,8 @@ public class ExtendedObjectResource<T extends BaseModel> extends BaseObjectResou
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
result.retainAll(manager.getDeviceItems(deviceId));
}
- return manager.getItems(result);
+ return manager.getItems(result);
}
}
diff --git a/src/main/java/org/traccar/api/HealthCheckService.java b/src/main/java/org/traccar/api/HealthCheckService.java
new file mode 100644
index 000000000..1e8f0d731
--- /dev/null
+++ b/src/main/java/org/traccar/api/HealthCheckService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Context;
+
+import java.util.TimerTask;
+
+public class HealthCheckService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(HealthCheckService.class);
+
+ private SystemD systemD;
+
+ private boolean enabled;
+ private long period;
+
+ public HealthCheckService() {
+ if (Context.getConfig().getBoolean("web.healthCheck")
+ && System.getProperty("os.name").toLowerCase().startsWith("linux")) {
+ try {
+ systemD = Native.load("systemd", SystemD.class);
+ String watchdogTimer = System.getenv("WATCHDOG_USEC");
+ if (watchdogTimer != null && !watchdogTimer.isEmpty()) {
+ period = Long.parseLong(watchdogTimer) / 1000 * 4 / 5;
+ }
+ if (period > 0) {
+ LOGGER.info("Health check enabled with period {}", period);
+ enabled = true;
+ }
+ } catch (UnsatisfiedLinkError e) {
+ LOGGER.warn("No systemd support", e);
+ }
+ }
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public long getPeriod() {
+ return period;
+ }
+
+ private String getUrl() {
+ String address = Context.getConfig().getString("web.address", "localhost");
+ int port = Context.getConfig().getInteger("web.port", 8082);
+ return "http://" + address + ":" + port + "/api/server";
+ }
+
+ public TimerTask createTask() {
+ return new TimerTask() {
+ @Override
+ public void run() {
+ LOGGER.debug("Health check running");
+ int status = Context.getClient().target(getUrl()).request().get().getStatus();
+ if (status == 200) {
+ int result = systemD.sd_notify(0, "WATCHDOG=1");
+ if (result < 0) {
+ LOGGER.warn("Health check notify error {}", result);
+ }
+ } else {
+ LOGGER.warn("Health check failed with status {}", status);
+ }
+ }
+ };
+ }
+
+ interface SystemD extends Library {
+ @SuppressWarnings("checkstyle:MethodName")
+ int sd_notify(@SuppressWarnings("checkstyle:ParameterName") int unset_environment, String state);
+ }
+
+}
diff --git a/src/main/java/org/traccar/api/resource/PositionResource.java b/src/main/java/org/traccar/api/resource/PositionResource.java
index c031b842f..67aa6dd32 100644
--- a/src/main/java/org/traccar/api/resource/PositionResource.java
+++ b/src/main/java/org/traccar/api/resource/PositionResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@ import javax.ws.rs.core.Response;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
@Path("positions")
@@ -63,8 +64,12 @@ public class PositionResource extends BaseResource {
return Context.getDeviceManager().getInitialState(getUserId());
} else {
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
- return Context.getDataManager().getPositions(
- deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to));
+ if (from != null && to != null) {
+ return Context.getDataManager().getPositions(
+ deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to));
+ } else {
+ return Collections.singleton(Context.getDeviceManager().getLastPosition(deviceId));
+ }
}
}
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java
index 2c5dcefd5..200ef8aa3 100644
--- a/src/main/java/org/traccar/config/Keys.java
+++ b/src/main/java/org/traccar/config/Keys.java
@@ -101,6 +101,44 @@ public final class Keys {
"forward.json", Boolean.class);
/**
+ * Boolean value to enable URL parameters in json mode. For example, {uniqueId} for device identifier,
+ * {latitude} and {longitude} for coordinates.
+ */
+ public static final ConfigKey FORWARD_URL_VARIABLES = new ConfigKey(
+ "forward.urlVariables", Boolean.class);
+
+ /**
+ * Position forwarding retrying enable. When enabled, additional attempts are made to deliver positions. If initial
+ * delivery fails, because of an unreachable server or an HTTP response different from '2xx', the software waits
+ * for 'forward.retry.delay' milliseconds to retry delivery. On subsequent failures, this delay is duplicated.
+ * If forwarding is retried for 'forward.retry.count', retrying is canceled and the position is dropped. Positions
+ * pending to be delivered are limited to 'forward.retry.limit'. If this limit is reached, positions get discarded.
+ */
+ public static final ConfigKey FORWARD_RETRY_ENABLE = new ConfigKey(
+ "forward.retry.enable", Boolean.class);
+
+ /**
+ * Position forwarding retry first delay in milliseconds.
+ * Can be set to anything greater than 0. Defaults to 100 milliseconds.
+ */
+ public static final ConfigKey FORWARD_RETRY_DELAY = new ConfigKey(
+ "forward.retry.delay", Integer.class);
+
+ /**
+ * Position forwarding retry maximum retries.
+ * Can be set to anything greater than 0. Defaults to 10 retries.
+ */
+ public static final ConfigKey FORWARD_RETRY_COUNT = new ConfigKey(
+ "forward.retry.count", Integer.class);
+
+ /**
+ * Position forwarding retry pending positions limit.
+ * Can be set to anything greater than 0. Defaults to 100 positions.
+ */
+ public static final ConfigKey FORWARD_RETRY_LIMIT = new ConfigKey(
+ "forward.retry.limit", Integer.class);
+
+ /**
* Boolean flag to enable or disable position filtering.
*/
public static final ConfigKey FILTER_ENABLE = new ConfigKey(
diff --git a/src/main/java/org/traccar/database/ActiveDevice.java b/src/main/java/org/traccar/database/ActiveDevice.java
index 207fc454b..e3ece6ad9 100644
--- a/src/main/java/org/traccar/database/ActiveDevice.java
+++ b/src/main/java/org/traccar/database/ActiveDevice.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,8 @@
package org.traccar.database;
import io.netty.channel.Channel;
-import org.traccar.NetworkMessage;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import org.traccar.BasePipelineFactory;
import org.traccar.Protocol;
import org.traccar.model.Command;
@@ -44,12 +45,12 @@ public class ActiveDevice {
return deviceId;
}
- public void sendCommand(Command command) {
- protocol.sendDataCommand(this, command);
+ public boolean supportsLiveCommands() {
+ return BasePipelineFactory.getHandler(channel.pipeline(), HttpRequestDecoder.class) == null;
}
- public void write(Object message) {
- channel.writeAndFlush(new NetworkMessage(message, remoteAddress));
+ public void sendCommand(Command command) {
+ protocol.sendDataCommand(channel, remoteAddress, command);
}
}
diff --git a/src/main/java/org/traccar/database/BaseObjectManager.java b/src/main/java/org/traccar/database/BaseObjectManager.java
index 8bf9ef860..e274e5aba 100644
--- a/src/main/java/org/traccar/database/BaseObjectManager.java
+++ b/src/main/java/org/traccar/database/BaseObjectManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,6 +23,8 @@ import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,6 +34,8 @@ public class BaseObjectManager<T extends BaseModel> {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseObjectManager.class);
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
private final DataManager dataManager;
private Map<Long, T> items;
@@ -43,6 +47,22 @@ public class BaseObjectManager<T extends BaseModel> {
refreshItems();
}
+ protected final void readLock() {
+ lock.readLock().lock();
+ }
+
+ protected final void readUnlock() {
+ lock.readLock().unlock();
+ }
+
+ protected final void writeLock() {
+ lock.writeLock().lock();
+ }
+
+ protected final void writeUnlock() {
+ lock.writeLock().unlock();
+ }
+
protected final DataManager getDataManager() {
return dataManager;
}
@@ -52,12 +72,18 @@ public class BaseObjectManager<T extends BaseModel> {
}
public T getById(long itemId) {
- return items.get(itemId);
+ try {
+ readLock();
+ return items.get(itemId);
+ } finally {
+ readUnlock();
+ }
}
public void refreshItems() {
if (dataManager != null) {
try {
+ writeLock();
Collection<T> databaseItems = dataManager.getObjects(baseClass);
if (items == null) {
items = new ConcurrentHashMap<>(databaseItems.size());
@@ -78,12 +104,19 @@ public class BaseObjectManager<T extends BaseModel> {
}
} catch (SQLException error) {
LOGGER.warn("Error refreshing items", error);
+ } finally {
+ writeUnlock();
}
}
}
protected void addNewItem(T item) {
- items.put(item.getId(), item);
+ try {
+ writeLock();
+ items.put(item.getId(), item);
+ } finally {
+ writeUnlock();
+ }
}
public void addItem(T item) throws SQLException {
@@ -92,7 +125,12 @@ public class BaseObjectManager<T extends BaseModel> {
}
protected void updateCachedItem(T item) {
- items.put(item.getId(), item);
+ try {
+ writeLock();
+ items.put(item.getId(), item);
+ } finally {
+ writeUnlock();
+ }
}
public void updateItem(T item) throws SQLException {
@@ -101,7 +139,12 @@ public class BaseObjectManager<T extends BaseModel> {
}
protected void removeCachedItem(long itemId) {
- items.remove(itemId);
+ try {
+ writeLock();
+ items.remove(itemId);
+ } finally {
+ writeUnlock();
+ }
}
public void removeItem(long itemId) throws SQLException {
@@ -121,7 +164,12 @@ public class BaseObjectManager<T extends BaseModel> {
}
public Set<Long> getAllItems() {
- return items.keySet();
+ try {
+ readLock();
+ return items.keySet();
+ } finally {
+ readUnlock();
+ }
}
}
diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java
index dc9512d9e..485402807 100644
--- a/src/main/java/org/traccar/database/CommandsManager.java
+++ b/src/main/java/org/traccar/database/CommandsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -76,7 +76,12 @@ public class CommandsManager extends ExtendedObjectManager<Command> {
} else {
ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(deviceId);
if (activeDevice != null) {
- activeDevice.sendCommand(command);
+ if (activeDevice.supportsLiveCommands()) {
+ activeDevice.sendCommand(command);
+ } else {
+ getDeviceQueue(deviceId).add(command);
+ return false;
+ }
} else if (!queueing) {
throw new RuntimeException("Device is not online");
} else {
@@ -142,21 +147,46 @@ public class CommandsManager extends ExtendedObjectManager<Command> {
}
private Queue<Command> getDeviceQueue(long deviceId) {
- if (!deviceQueues.containsKey(deviceId)) {
- deviceQueues.put(deviceId, new ConcurrentLinkedQueue<Command>());
+ Queue<Command> deviceQueue;
+ try {
+ readLock();
+ deviceQueue = deviceQueues.get(deviceId);
+ } finally {
+ readUnlock();
+ }
+ if (deviceQueue != null) {
+ return deviceQueue;
+ } else {
+ try {
+ writeLock();
+ return deviceQueues.computeIfAbsent(deviceId, key -> new ConcurrentLinkedQueue<>());
+ } finally {
+ writeUnlock();
+ }
}
- return deviceQueues.get(deviceId);
}
- public void sendQueuedCommands(ActiveDevice activeDevice) {
- Queue<Command> deviceQueue = deviceQueues.get(activeDevice.getDeviceId());
+ public Collection<Command> readQueuedCommands(long deviceId) {
+ return readQueuedCommands(deviceId, Integer.MAX_VALUE);
+ }
+
+ public Collection<Command> readQueuedCommands(long deviceId, int count) {
+ Queue<Command> deviceQueue;
+ try {
+ readLock();
+ deviceQueue = deviceQueues.get(deviceId);
+ } finally {
+ readUnlock();
+ }
+ Collection<Command> result = new ArrayList<>();
if (deviceQueue != null) {
Command command = deviceQueue.poll();
- while (command != null) {
- activeDevice.sendCommand(command);
+ while (command != null && result.size() < count) {
+ result.add(command);
command = deviceQueue.poll();
}
}
+ return result;
}
}
diff --git a/src/main/java/org/traccar/database/ConnectionManager.java b/src/main/java/org/traccar/database/ConnectionManager.java
index dd0071143..4d43bc71b 100644
--- a/src/main/java/org/traccar/database/ConnectionManager.java
+++ b/src/main/java/org/traccar/database/ConnectionManager.java
@@ -139,10 +139,6 @@ public class ConnectionManager {
}
updateDevice(device);
-
- if (status.equals(Device.STATUS_ONLINE) && !oldStatus.equals(Device.STATUS_ONLINE)) {
- Context.getCommandsManager().sendQueuedCommands(getActiveDevice(deviceId));
- }
}
public Map<Event, Position> updateDeviceState(long deviceId) {
diff --git a/src/main/java/org/traccar/database/DeviceManager.java b/src/main/java/org/traccar/database/DeviceManager.java
index fa95adeb2..fe17f7ced 100644
--- a/src/main/java/org/traccar/database/DeviceManager.java
+++ b/src/main/java/org/traccar/database/DeviceManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,11 +58,16 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
public DeviceManager(DataManager dataManager) {
super(dataManager, Device.class);
this.config = Context.getConfig();
- if (devicesByPhone == null) {
- devicesByPhone = new ConcurrentHashMap<>();
- }
- if (devicesByUniqueId == null) {
- devicesByUniqueId = new ConcurrentHashMap<>();
+ try {
+ writeLock();
+ if (devicesByPhone == null) {
+ devicesByPhone = new ConcurrentHashMap<>();
+ }
+ if (devicesByUniqueId == null) {
+ devicesByUniqueId = new ConcurrentHashMap<>();
+ }
+ } finally {
+ writeUnlock();
}
dataRefreshDelay = config.getLong("database.refreshDelay", DEFAULT_REFRESH_DELAY) * 1000;
lookupGroupsAttribute = config.getBoolean("deviceManager.lookupGroupsAttribute");
@@ -108,11 +113,20 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
@Override
public Device getByUniqueId(String uniqueId) throws SQLException {
- boolean forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean("database.ignoreUnknown");
-
+ boolean forceUpdate;
+ try {
+ readLock();
+ forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean("database.ignoreUnknown");
+ } finally {
+ readUnlock();
+ }
updateDeviceCache(forceUpdate);
-
- return devicesByUniqueId.get(uniqueId);
+ try {
+ readLock();
+ return devicesByUniqueId.get(uniqueId);
+ } finally {
+ readUnlock();
+ }
}
@Override
@@ -134,7 +148,12 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
}
public Device getDeviceByPhone(String phone) {
- return devicesByPhone.get(phone);
+ try {
+ readLock();
+ return devicesByPhone.get(phone);
+ } finally {
+ readUnlock();
+ }
}
@Override
@@ -176,8 +195,7 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
}
public Set<Long> getAllManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getAllUserItems(userId));
+ Set<Long> result = new HashSet<>(getAllUserItems(userId));
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getAllUserItems(managedUserId));
}
@@ -186,34 +204,68 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = new HashSet<>(getUserItems(userId));
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getUserItems(managedUserId));
}
return result;
}
- private void putUniqueDeviceId(Device device) {
- if (devicesByUniqueId == null) {
- devicesByUniqueId = new ConcurrentHashMap<>(getAllItems().size());
+ private void addByUniqueId(Device device) {
+ try {
+ writeLock();
+ if (devicesByUniqueId == null) {
+ devicesByUniqueId = new ConcurrentHashMap<>();
+ }
+ devicesByUniqueId.put(device.getUniqueId(), device);
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ private void removeByUniqueId(String deviceUniqueId) {
+ try {
+ writeLock();
+ if (devicesByUniqueId != null) {
+ devicesByUniqueId.remove(deviceUniqueId);
+ }
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ private void addByPhone(Device device) {
+ try {
+ writeLock();
+ if (devicesByPhone == null) {
+ devicesByPhone = new ConcurrentHashMap<>();
+ }
+ devicesByPhone.put(device.getPhone(), device);
+ } finally {
+ writeUnlock();
}
- devicesByUniqueId.put(device.getUniqueId(), device);
}
- private void putPhone(Device device) {
- if (devicesByPhone == null) {
- devicesByPhone = new ConcurrentHashMap<>(getAllItems().size());
+ private void removeByPhone(String phone) {
+ if (phone == null || phone.isEmpty()) {
+ return;
+ }
+ try {
+ writeLock();
+ if (devicesByPhone != null) {
+ devicesByPhone.remove(phone);
+ }
+ } finally {
+ writeUnlock();
}
- devicesByPhone.put(device.getPhone(), device);
}
@Override
protected void addNewItem(Device device) {
super.addNewItem(device);
- putUniqueDeviceId(device);
+ addByUniqueId(device);
if (device.getPhone() != null && !device.getPhone().isEmpty()) {
- putPhone(device);
+ addByPhone(device);
}
if (Context.getGeofenceManager() != null) {
Position lastPosition = getLastPosition(device.getId());
@@ -234,18 +286,16 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
cachedDevice.setDisabled(device.getDisabled());
cachedDevice.setAttributes(device.getAttributes());
if (!device.getUniqueId().equals(cachedDevice.getUniqueId())) {
- devicesByUniqueId.remove(cachedDevice.getUniqueId());
+ removeByUniqueId(cachedDevice.getUniqueId());
cachedDevice.setUniqueId(device.getUniqueId());
- putUniqueDeviceId(cachedDevice);
+ addByUniqueId(cachedDevice);
}
if (device.getPhone() != null && !device.getPhone().isEmpty()
&& !device.getPhone().equals(cachedDevice.getPhone())) {
String phone = cachedDevice.getPhone();
- if (phone != null && !phone.isEmpty()) {
- devicesByPhone.remove(phone);
- }
+ removeByPhone(phone);
cachedDevice.setPhone(device.getPhone());
- putPhone(cachedDevice);
+ addByPhone(cachedDevice);
}
}
@@ -256,10 +306,8 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
String deviceUniqueId = cachedDevice.getUniqueId();
String phone = cachedDevice.getPhone();
super.removeCachedItem(deviceId);
- devicesByUniqueId.remove(deviceUniqueId);
- if (phone != null && !phone.isEmpty()) {
- devicesByPhone.remove(phone);
- }
+ removeByUniqueId(deviceUniqueId);
+ removeByPhone(phone);
}
positions.remove(deviceId);
}
diff --git a/src/main/java/org/traccar/database/DriversManager.java b/src/main/java/org/traccar/database/DriversManager.java
index 930951460..d111cd643 100644
--- a/src/main/java/org/traccar/database/DriversManager.java
+++ b/src/main/java/org/traccar/database/DriversManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,22 +27,44 @@ public class DriversManager extends ExtendedObjectManager<Driver> {
public DriversManager(DataManager dataManager) {
super(dataManager, Driver.class);
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>();
+ try {
+ writeLock();
+ if (driversByUniqueId == null) {
+ driversByUniqueId = new ConcurrentHashMap<>();
+ }
+ } finally {
+ writeUnlock();
}
}
- private void putUniqueDriverId(Driver driver) {
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>(getAllItems().size());
+ private void addByUniqueId(Driver driver) {
+ try {
+ writeLock();
+ if (driversByUniqueId == null) {
+ driversByUniqueId = new ConcurrentHashMap<>();
+ }
+ driversByUniqueId.put(driver.getUniqueId(), driver);
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ private void removeByUniqueId(String driverUniqueId) {
+ try {
+ writeLock();
+ if (driversByUniqueId == null) {
+ driversByUniqueId = new ConcurrentHashMap<>();
+ }
+ driversByUniqueId.remove(driverUniqueId);
+ } finally {
+ writeUnlock();
}
- driversByUniqueId.put(driver.getUniqueId(), driver);
}
@Override
protected void addNewItem(Driver driver) {
super.addNewItem(driver);
- putUniqueDriverId(driver);
+ addByUniqueId(driver);
}
@Override
@@ -50,9 +72,9 @@ public class DriversManager extends ExtendedObjectManager<Driver> {
Driver cachedDriver = getById(driver.getId());
cachedDriver.setName(driver.getName());
if (!driver.getUniqueId().equals(cachedDriver.getUniqueId())) {
- driversByUniqueId.remove(cachedDriver.getUniqueId());
+ removeByUniqueId(cachedDriver.getUniqueId());
cachedDriver.setUniqueId(driver.getUniqueId());
- putUniqueDriverId(cachedDriver);
+ addByUniqueId(cachedDriver);
}
cachedDriver.setAttributes(driver.getAttributes());
}
@@ -63,11 +85,16 @@ public class DriversManager extends ExtendedObjectManager<Driver> {
if (cachedDriver != null) {
String driverUniqueId = cachedDriver.getUniqueId();
super.removeCachedItem(driverId);
- driversByUniqueId.remove(driverUniqueId);
+ removeByUniqueId(driverUniqueId);
}
}
public Driver getDriverByUniqueId(String uniqueId) {
- return driversByUniqueId.get(uniqueId);
+ try {
+ readLock();
+ return driversByUniqueId.get(uniqueId);
+ } finally {
+ readUnlock();
+ }
}
}
diff --git a/src/main/java/org/traccar/database/ExtendedObjectManager.java b/src/main/java/org/traccar/database/ExtendedObjectManager.java
index ceb85b537..93e5820fb 100644
--- a/src/main/java/org/traccar/database/ExtendedObjectManager.java
+++ b/src/main/java/org/traccar/database/ExtendedObjectManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -45,24 +45,45 @@ public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleO
}
public final Set<Long> getGroupItems(long groupId) {
- if (!groupItems.containsKey(groupId)) {
- groupItems.put(groupId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = groupItems.get(groupId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return groupItems.get(groupId);
}
public final Set<Long> getDeviceItems(long deviceId) {
- if (!deviceItems.containsKey(deviceId)) {
- deviceItems.put(deviceId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = deviceItems.get(deviceId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return deviceItems.get(deviceId);
}
public Set<Long> getAllDeviceItems(long deviceId) {
- if (!deviceItemsWithGroups.containsKey(deviceId)) {
- deviceItemsWithGroups.put(deviceId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = deviceItemsWithGroups.get(deviceId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return deviceItemsWithGroups.get(deviceId);
}
@Override
@@ -74,41 +95,48 @@ public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleO
public void refreshExtendedPermissions() {
if (getDataManager() != null) {
try {
-
Collection<Permission> databaseGroupPermissions =
getDataManager().getPermissions(Group.class, getBaseClass());
- groupItems.clear();
- for (Permission groupPermission : databaseGroupPermissions) {
- getGroupItems(groupPermission.getOwnerId()).add(groupPermission.getPropertyId());
- }
-
Collection<Permission> databaseDevicePermissions =
getDataManager().getPermissions(Device.class, getBaseClass());
+ writeLock();
+
+ groupItems.clear();
deviceItems.clear();
deviceItemsWithGroups.clear();
+ for (Permission groupPermission : databaseGroupPermissions) {
+ groupItems
+ .computeIfAbsent(groupPermission.getOwnerId(), key -> new HashSet<>())
+ .add(groupPermission.getPropertyId());
+ }
+
for (Permission devicePermission : databaseDevicePermissions) {
- getDeviceItems(devicePermission.getOwnerId()).add(devicePermission.getPropertyId());
- getAllDeviceItems(devicePermission.getOwnerId()).add(devicePermission.getPropertyId());
+ deviceItems
+ .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>())
+ .add(devicePermission.getPropertyId());
+ deviceItemsWithGroups
+ .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>())
+ .add(devicePermission.getPropertyId());
}
for (Device device : Context.getDeviceManager().getAllDevices()) {
long groupId = device.getGroupId();
- while (groupId != 0) {
- getAllDeviceItems(device.getId()).addAll(getGroupItems(groupId));
+ while (groupId > 0) {
+ deviceItemsWithGroups
+ .computeIfAbsent(device.getId(), key -> new HashSet<>())
+ .addAll(groupItems.getOrDefault(groupId, new HashSet<>()));
Group group = Context.getGroupsManager().getById(groupId);
- if (group != null) {
- groupId = group.getGroupId();
- } else {
- groupId = 0;
- }
+ groupId = group != null ? group.getGroupId() : 0;
}
}
} catch (SQLException | ClassNotFoundException error) {
LOGGER.warn("Refresh permissions error", error);
+ } finally {
+ writeUnlock();
}
}
}
diff --git a/src/main/java/org/traccar/database/GroupsManager.java b/src/main/java/org/traccar/database/GroupsManager.java
index d8404c614..81f1968aa 100644
--- a/src/main/java/org/traccar/database/GroupsManager.java
+++ b/src/main/java/org/traccar/database/GroupsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -95,8 +95,7 @@ public class GroupsManager extends BaseObjectManager<Group> implements Managable
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = getUserItems(userId);
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getUserItems(managedUserId));
}
diff --git a/src/main/java/org/traccar/database/LdapProvider.java b/src/main/java/org/traccar/database/LdapProvider.java
index d8b5c9f52..a8220ea8e 100644
--- a/src/main/java/org/traccar/database/LdapProvider.java
+++ b/src/main/java/org/traccar/database/LdapProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -81,7 +81,7 @@ public class LdapProvider {
if (this.adminFilter != null) {
try {
InitialDirContext context = initContext();
- String searchString = adminFilter.replace(":login", accountName);
+ String searchString = adminFilter.replace(":login", encodeForLdap(accountName));
SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<SearchResult> results = context.search(searchBase, searchString, searchControls);
@@ -107,7 +107,7 @@ public class LdapProvider {
private SearchResult lookupUser(String accountName) throws NamingException {
InitialDirContext context = initContext();
- String searchString = searchFilter.replace(":login", accountName);
+ String searchString = searchFilter.replace(":login", encodeForLdap(accountName));
SearchControls searchControls = new SearchControls();
String[] attributeFilter = {idAttribute, nameAttribute, mailAttribute};
@@ -176,4 +176,34 @@ public class LdapProvider {
return false;
}
+ public String encodeForLdap(String input) {
+ if (input == null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < input.length(); i++) {
+ char c = input.charAt(i);
+ switch (c) {
+ case '\\':
+ sb.append("\\5c");
+ break;
+ case '*':
+ sb.append("\\2a");
+ break;
+ case '(':
+ sb.append("\\28");
+ break;
+ case ')':
+ sb.append("\\29");
+ break;
+ case '\0':
+ sb.append("\\00");
+ break;
+ default:
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
}
diff --git a/src/main/java/org/traccar/database/SimpleObjectManager.java b/src/main/java/org/traccar/database/SimpleObjectManager.java
index 15dda4520..eb8284d4e 100644
--- a/src/main/java/org/traccar/database/SimpleObjectManager.java
+++ b/src/main/java/org/traccar/database/SimpleObjectManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -42,16 +42,22 @@ public abstract class SimpleObjectManager<T extends BaseModel> extends BaseObjec
@Override
public final Set<Long> getUserItems(long userId) {
- if (!userItems.containsKey(userId)) {
- userItems.put(userId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = userItems.get(userId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return userItems.get(userId);
}
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = getUserItems(userId);
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getUserItems(managedUserId));
}
@@ -71,16 +77,16 @@ public abstract class SimpleObjectManager<T extends BaseModel> extends BaseObjec
public final void refreshUserItems() {
if (getDataManager() != null) {
try {
- if (userItems != null) {
- userItems.clear();
- } else {
- userItems = new ConcurrentHashMap<>();
- }
+ writeLock();
+ userItems = new ConcurrentHashMap<>();
for (Permission permission : getDataManager().getPermissions(User.class, getBaseClass())) {
- getUserItems(permission.getOwnerId()).add(permission.getPropertyId());
+ Set<Long> items = userItems.computeIfAbsent(permission.getOwnerId(), key -> new HashSet<>());
+ items.add(permission.getPropertyId());
}
} catch (SQLException | ClassNotFoundException error) {
LOGGER.warn("Error getting permissions", error);
+ } finally {
+ writeUnlock();
}
}
}
diff --git a/src/main/java/org/traccar/database/UsersManager.java b/src/main/java/org/traccar/database/UsersManager.java
index 576a9e6c7..b741a85b6 100644
--- a/src/main/java/org/traccar/database/UsersManager.java
+++ b/src/main/java/org/traccar/database/UsersManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,6 @@
*/
package org.traccar.database;
-import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -73,8 +72,7 @@ public class UsersManager extends SimpleObjectManager<User> {
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = getUserItems(userId);
result.add(userId);
return result;
}
diff --git a/src/main/java/org/traccar/geocoder/FactualGeocoder.java b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
index c7a68c293..f540eb8fe 100644
--- a/src/main/java/org/traccar/geocoder/FactualGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,8 +20,16 @@ import javax.json.JsonObject;
public class FactualGeocoder extends JsonGeocoder {
+ private static String formatUrl(String url, String key) {
+ if (url == null) {
+ url = "https://api.factual.com/geotag";
+ }
+ url += "?latitude=%f&longitude=%f&KEY=" + key;
+ return url;
+ }
+
public FactualGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(url + "?latitude=%f&longitude=%f&KEY=" + key, cacheSize, addressFormat);
+ super(formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
index 3a173f985..b4881a006 100644
--- a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,12 +19,16 @@ import javax.json.JsonObject;
public class GisgraphyGeocoder extends JsonGeocoder {
- public GisgraphyGeocoder(AddressFormat addressFormat) {
- this("http://services.gisgraphy.com/reversegeocoding/search", 0, addressFormat);
+ private static String formatUrl(String url) {
+ if (url == null) {
+ url = "http://services.gisgraphy.com/reversegeocoding/search";
+ }
+ url += "?format=json&lat=%f&lng=%f&from=1&to=1";
+ return url;
}
public GisgraphyGeocoder(String url, int cacheSize, AddressFormat addressFormat) {
- super(url + "?format=json&lat=%f&lng=%f&from=1&to=1", cacheSize, addressFormat);
+ super(formatUrl(url), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
index 4029e3f07..8dc3f76f0 100644
--- a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,16 @@ import javax.json.JsonObject;
public class MapQuestGeocoder extends JsonGeocoder {
+ private static String formatUrl(String url, String key) {
+ if (url == null) {
+ url = "http://www.mapquestapi.com/geocoding/v1/reverse";
+ }
+ url += "?key=" + key + "&location=%f,%f";
+ return url;
+ }
+
public MapQuestGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(url + "?key=" + key + "&location=%f,%f", cacheSize, addressFormat);
+ super(formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
index 822b6e91e..56161e52c 100644
--- a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,16 @@ import javax.json.JsonObject;
public class OpenCageGeocoder extends JsonGeocoder {
+ private static String formatUrl(String url, String key) {
+ if (url == null) {
+ url = "https://api.opencagedata.com/geocode/v1";
+ }
+ url += "/json?q=%f,%f&no_annotations=1&key=" + key;
+ return url;
+ }
+
public OpenCageGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(url + "/json?q=%f,%f&no_annotations=1&key=" + key, cacheSize, addressFormat);
+ super(formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/helper/Checksum.java b/src/main/java/org/traccar/helper/Checksum.java
index adfa697c5..d41dc2992 100644
--- a/src/main/java/org/traccar/helper/Checksum.java
+++ b/src/main/java/org/traccar/helper/Checksum.java
@@ -168,6 +168,14 @@ public final class Checksum {
return checksum;
}
+ public static int modulo256(ByteBuffer buf) {
+ int checksum = 0;
+ while (buf.hasRemaining()) {
+ checksum = (checksum + buf.get()) & 0xFF;
+ }
+ return checksum;
+ }
+
public static String sum(String msg) {
byte checksum = 0;
for (byte b : msg.getBytes(StandardCharsets.US_ASCII)) {
diff --git a/src/main/java/org/traccar/helper/ServletHelper.java b/src/main/java/org/traccar/helper/ServletHelper.java
index e3481e249..b6c587ec3 100644
--- a/src/main/java/org/traccar/helper/ServletHelper.java
+++ b/src/main/java/org/traccar/helper/ServletHelper.java
@@ -1,8 +1,22 @@
+/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package org.traccar.helper;
import javax.servlet.http.HttpServletRequest;
-
public final class ServletHelper {
private ServletHelper() {
@@ -14,10 +28,14 @@ public final class ServletHelper {
String remoteAddress = request.getHeader("X-FORWARDED-FOR");
if (remoteAddress != null && !remoteAddress.isEmpty()) {
- return remoteAddress.substring(0, remoteAddress.indexOf(",")); // removes the additional data
+ int separatorIndex = remoteAddress.indexOf(",");
+ if (separatorIndex > 0) {
+ return remoteAddress.substring(0, separatorIndex); // remove the additional data
+ } else {
+ return remoteAddress;
+ }
} else {
- remoteAddress = request.getRemoteAddr();
- return remoteAddress;
+ return request.getRemoteAddr();
}
} else {
return null;
diff --git a/src/main/java/org/traccar/notification/NotificatorManager.java b/src/main/java/org/traccar/notification/NotificatorManager.java
index 191748379..c5f1ad8a0 100644
--- a/src/main/java/org/traccar/notification/NotificatorManager.java
+++ b/src/main/java/org/traccar/notification/NotificatorManager.java
@@ -32,6 +32,7 @@ import org.traccar.notificators.Notificator;
import org.traccar.notificators.NotificatorSms;
import org.traccar.notificators.NotificatorWeb;
import org.traccar.notificators.NotificatorTelegram;
+import org.traccar.notificators.NotificatorPushover;
public final class NotificatorManager {
@@ -61,6 +62,9 @@ public final class NotificatorManager {
case "telegram":
defaultNotificator = NotificatorTelegram.class.getCanonicalName();
break;
+ case "pushover":
+ defaultNotificator = NotificatorPushover.class.getCanonicalName();
+ break;
default:
break;
}
diff --git a/src/main/java/org/traccar/notificators/NotificatorPushover.java b/src/main/java/org/traccar/notificators/NotificatorPushover.java
new file mode 100644
index 000000000..141d652ca
--- /dev/null
+++ b/src/main/java/org/traccar/notificators/NotificatorPushover.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.notificators;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Context;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.model.User;
+import org.traccar.notification.NotificationFormatter;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.InvocationCallback;
+
+public class NotificatorPushover extends Notificator {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(NotificatorPushover.class);
+
+ private final String url;
+ private final String token;
+ private final String puser;
+
+ public static class Message {
+ @JsonProperty("token")
+ private String token;
+ @JsonProperty("user")
+ private String user;
+ @JsonProperty("device")
+ private String device;
+ @JsonProperty("message")
+ private String message;
+ }
+
+ public NotificatorPushover() {
+ url = "https://api.pushover.net/1/messages.json";
+ token = Context.getConfig().getString("notificator.pushover.token");
+ puser = Context.getConfig().getString("notificator.pushover.user");
+ }
+
+ @Override
+ public void sendSync(long userId, Event event, Position position) {
+
+ final User user = Context.getPermissionsManager().getUser(userId);
+
+ String device = "";
+
+ if (user.getAttributes().containsKey("notificator.pushover.device")) {
+ device = user.getString("notificator.pushover.device").replaceAll(" *, *", ",");
+ }
+
+ if (token == null) {
+ LOGGER.warn("Pushover token not found");
+ return;
+ }
+
+ if (puser == null) {
+ LOGGER.warn("Pushover user not found");
+ return;
+ }
+
+ Message message = new Message();
+ message.token = token;
+ message.user = puser;
+ message.device = device;
+ message.message = NotificationFormatter.formatShortMessage(userId, event, position);
+
+ Context.getClient().target(url).request()
+ .async().post(Entity.json(message), new InvocationCallback<Object>() {
+ @Override
+ public void completed(Object o) {
+ }
+
+ @Override
+ public void failed(Throwable throwable) {
+ LOGGER.warn("Pushover API error", throwable);
+ }
+ });
+ }
+
+ @Override
+ public void sendAsync(long userId, Event event, Position position) {
+ sendSync(userId, event, position);
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java
new file mode 100644
index 000000000..e957a6911
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Ivan Muratov (binakot@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ArnaviBinaryProtocolDecoder extends BaseProtocolDecoder {
+
+ private static final byte HEADER_START_SIGN = (byte) 0xff;
+ private static final byte HEADER_VERSION_1 = 0x22;
+ private static final byte HEADER_VERSION_2 = 0x23;
+
+ private static final byte RECORD_PING = 0x00;
+ private static final byte RECORD_DATA = 0x01;
+ private static final byte RECORD_TEXT = 0x03;
+ private static final byte RECORD_FILE = 0x04;
+ private static final byte RECORD_BINARY = 0x06;
+
+ private static final byte TAG_LATITUDE = 3;
+ private static final byte TAG_LONGITUDE = 4;
+ private static final byte TAG_COORD_PARAMS = 5;
+
+ public ArnaviBinaryProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private void sendResponse(Channel channel, byte version, int index) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0x7b);
+ if (version == HEADER_VERSION_1) {
+ response.writeByte(0x00);
+ response.writeByte((byte) index);
+ } else if (version == HEADER_VERSION_2) {
+ response.writeByte(0x04);
+ response.writeByte(0x00);
+ ByteBuffer time = ByteBuffer.allocate(4).putInt((int) (System.currentTimeMillis() / 1000));
+ ((Buffer) time).position(0);
+ response.writeByte(Checksum.modulo256(time.slice()));
+ response.writeBytes(time);
+ }
+ response.writeByte(0x7d);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private Position decodePosition(DeviceSession deviceSession, ByteBuf buf, int length, Date time) {
+
+ final Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(time);
+
+ int readBytes = 0;
+ while (readBytes < length) {
+ short tag = buf.readUnsignedByte();
+ switch (tag) {
+ case TAG_LATITUDE:
+ position.setLatitude(buf.readFloatLE());
+ position.setValid(true);
+ break;
+
+ case TAG_LONGITUDE:
+ position.setLongitude(buf.readFloatLE());
+ position.setValid(true);
+ break;
+
+ case TAG_COORD_PARAMS:
+ position.setCourse(buf.readUnsignedByte() * 2);
+ position.setAltitude(buf.readUnsignedByte() * 10);
+ byte satellites = buf.readByte();
+ position.set(Position.KEY_SATELLITES, satellites & 0x0F + (satellites >> 4) & 0x0F);
+ position.setSpeed(buf.readUnsignedByte());
+ break;
+
+ default:
+ buf.skipBytes(4);
+ break;
+ }
+
+ readBytes += 1 + 4;
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ byte startSign = buf.readByte();
+
+ if (startSign == HEADER_START_SIGN) {
+
+ byte version = buf.readByte();
+
+ String imei = String.valueOf(buf.readLongLE());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+
+ if (deviceSession != null) {
+ sendResponse(channel, version, 0);
+ }
+
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ int index = buf.readUnsignedByte();
+
+ byte recordType = buf.readByte();
+ while (buf.readableBytes() > 0) {
+ switch (recordType) {
+ case RECORD_PING:
+ case RECORD_DATA:
+ case RECORD_TEXT:
+ case RECORD_FILE:
+ case RECORD_BINARY:
+ int length = buf.readUnsignedShortLE();
+ Date time = new Date(buf.readUnsignedIntLE() * 1000);
+
+ if (recordType == RECORD_DATA) {
+ positions.add(decodePosition(deviceSession, buf, length, time));
+ } else {
+ buf.readBytes(length);
+ }
+
+ buf.readUnsignedByte(); // checksum
+ break;
+
+ default:
+ return null;
+ }
+
+ recordType = buf.readByte();
+ }
+
+ sendResponse(channel, HEADER_VERSION_1, index);
+
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java b/src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java
new file mode 100644
index 000000000..473e8b2c7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Ivan Muratov (binakot@gmail.com)
+ *
+ * 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.helper.BufferUtil;
+
+public class ArnaviFrameDecoder extends BaseFrameDecoder {
+
+ private static final int HEADER_LENGTH = 10;
+ private static final int PACKET_WRAPPER_LENGTH = 8;
+ private static final int RESULT_TYPE = 0xfd;
+ private static final byte PACKAGE_END_SIGN = 0x5d;
+
+ private boolean firstPacket = true;
+
+ @Override
+ protected Object decode(ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 4) {
+ return null;
+ }
+
+ if (buf.getByte(buf.readerIndex()) == '$') {
+
+ int index = BufferUtil.indexOf("\r\n", buf);
+ if (index > 0) {
+ ByteBuf frame = buf.readRetainedSlice(index - buf.readerIndex());
+ buf.skipBytes(2);
+ return frame;
+ }
+
+ } else {
+
+ int length;
+ if (firstPacket) {
+ firstPacket = false;
+ length = HEADER_LENGTH;
+ } else {
+ int type = buf.getUnsignedByte(1);
+ if (type == RESULT_TYPE) {
+ length = 4;
+ } else {
+ int index = 2;
+ while (index + PACKET_WRAPPER_LENGTH < buf.readableBytes()
+ && buf.getByte(index) != PACKAGE_END_SIGN) {
+ index += PACKET_WRAPPER_LENGTH + buf.getUnsignedShortLE(index + 1);
+ }
+ if (buf.getByte(index) != PACKAGE_END_SIGN) {
+ return null;
+ }
+ length = index + 1;
+ }
+ }
+
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocol.java b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
index afe491865..aecb42c8c 100644
--- a/src/main/java/org/traccar/protocol/ArnaviProtocol.java
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,6 @@
*/
package org.traccar.protocol;
-import io.netty.handler.codec.LineBasedFrameDecoder;
-import io.netty.handler.codec.string.StringDecoder;
-import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
@@ -28,9 +25,7 @@ public class ArnaviProtocol extends BaseProtocol {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new LineBasedFrameDecoder(1024));
- pipeline.addLast(new StringDecoder());
- pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new ArnaviFrameDecoder());
pipeline.addLast(new ArnaviProtocolDecoder(ArnaviProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
index 7996cf429..68a70c944 100644
--- a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,91 +15,35 @@
*/
package org.traccar.protocol;
+import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
import org.traccar.Protocol;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.Parser;
-import org.traccar.helper.PatternBuilder;
-import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.util.regex.Pattern;
public class ArnaviProtocolDecoder extends BaseProtocolDecoder {
+ private final ArnaviTextProtocolDecoder textProtocolDecoder;
+ private final ArnaviBinaryProtocolDecoder binaryProtocolDecoder;
+
public ArnaviProtocolDecoder(Protocol protocol) {
super(protocol);
+ textProtocolDecoder = new ArnaviTextProtocolDecoder(protocol);
+ binaryProtocolDecoder = new ArnaviBinaryProtocolDecoder(protocol);
}
- private static final Pattern PATTERN = new PatternBuilder()
- .text("$AV,")
- .number("Vd,") // type
- .number("(d+),") // device id
- .number("(d+),") // index
- .number("(d+),") // power
- .number("(d+),") // battery
- .number("-?d+,")
- .expression("[01],") // movement
- .expression("([01]),") // ignition
- .number("(d+),") // input
- .number("d+,d+,") // input 1
- .number("d+,d+,").optional() // input 2
- .expression("[01],") // fix type
- .number("(d+),") // satellites
- .groupBegin()
- .number("(d+.d+)?,") // altitude
- .number("(?:d+.d+)?,") // geoid height
- .groupEnd("?")
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(dd)(dd.d+)([NS]),") // latitude
- .number("(ddd)(dd.d+)([EW]),") // longitude
- .number("(d+.d+),") // speed
- .number("(d+.d+),") // course
- .number("(dd)(dd)(dd)") // date (ddmmyy)
- .any()
- .compile();
-
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- Parser parser = new Parser(PATTERN, (String) msg);
- if (!parser.matches()) {
- return null;
- }
+ ByteBuf buf = (ByteBuf) msg;
- Position position = new Position(getProtocolName());
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
+ if (buf.getByte(buf.readerIndex()) == '$') {
+ return textProtocolDecoder.decode(channel, remoteAddress, msg);
+ } else {
+ return binaryProtocolDecoder.decode(channel, remoteAddress, msg);
}
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.set(Position.KEY_INDEX, parser.nextInt());
- position.set(Position.KEY_POWER, parser.nextInt() * 0.01);
- position.set(Position.KEY_BATTERY, parser.nextInt() * 0.01);
- position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
- position.set(Position.KEY_INPUT, parser.nextInt());
- position.set(Position.KEY_SATELLITES, parser.nextInt());
-
- position.setAltitude(parser.nextDouble(0));
-
- DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
-
- position.setValid(true);
- position.setLatitude(parser.nextCoordinate());
- position.setLongitude(parser.nextCoordinate());
- position.setSpeed(parser.nextDouble());
- position.setCourse(parser.nextDouble());
-
- dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
- position.setTime(dateBuilder.getDate());
-
- return position;
}
}
diff --git a/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java
new file mode 100644
index 000000000..b99869e6e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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 org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Pattern;
+
+public class ArnaviTextProtocolDecoder extends BaseProtocolDecoder {
+
+ public ArnaviTextProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("$AV,")
+ .number("Vd,") // type
+ .number("(d+),") // device id
+ .number("(d+),") // index
+ .number("(d+),") // power
+ .number("(d+),") // battery
+ .number("-?d+,")
+ .expression("[01],") // movement
+ .expression("([01]),") // ignition
+ .number("(d+),") // input
+ .number("d+,d+,") // input 1
+ .number("d+,d+,").optional() // input 2
+ .expression("[01],") // fix type
+ .number("(d+),") // satellites
+ .groupBegin()
+ .number("(d+.d+)?,") // altitude
+ .number("(?:d+.d+)?,") // geoid height
+ .groupEnd("?")
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(dd)(dd.d+)([NS]),") // latitude
+ .number("(ddd)(dd.d+)([EW]),") // longitude
+ .number("(d+.d+),") // speed
+ .number("(d+.d+),") // course
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+ Parser parser = new Parser(PATTERN, buf.toString(StandardCharsets.US_ASCII));
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_INDEX, parser.nextInt());
+ position.set(Position.KEY_POWER, parser.nextInt() * 0.01);
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.01);
+ position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
+ position.set(Position.KEY_INPUT, parser.nextInt());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+
+ position.setAltitude(parser.nextDouble(0));
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+
+ position.setValid(true);
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/BceProtocolDecoder.java b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
index 30f9bb1f3..a259e027a 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 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -83,10 +83,10 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
buf.skipBytes(4);
}
if (BitUtil.check(mask, 12)) {
- buf.skipBytes(2);
+ position.set("fuel1", buf.readUnsignedShort());
}
if (BitUtil.check(mask, 13)) {
- buf.skipBytes(2);
+ position.set("fuel2", buf.readUnsignedShort());
}
if (BitUtil.check(mask, 14)) {
@@ -113,7 +113,7 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte());
}
if (BitUtil.check(mask, 4)) {
- position.set(Position.KEY_RPM, buf.readUnsignedShortLE());
+ position.set(Position.KEY_RPM, buf.readUnsignedShortLE() * 0.125);
}
if (BitUtil.check(mask, 5)) {
position.set(Position.KEY_HOURS, buf.readUnsignedIntLE());
@@ -122,7 +122,7 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
}
if (BitUtil.check(mask, 7)) {
- position.set(Position.KEY_COOLANT_TEMP, (int) buf.readByte());
+ position.set(Position.KEY_COOLANT_TEMP, buf.readByte() - 40);
}
if (BitUtil.check(mask, 8)) {
position.set("fuel2", buf.readUnsignedByte());
@@ -217,9 +217,9 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShortLE();
}
if (BitUtil.check(mask, 6)) {
- buf.readUnsignedByte(); // maximum acceleration
- buf.readUnsignedByte(); // maximum deceleration
- buf.readUnsignedByte(); // maximum cornering
+ position.set("maxAcceleration", buf.readUnsignedByte() * 0.02);
+ position.set("maxBraking", buf.readUnsignedByte() * 0.02);
+ position.set("maxCornering", buf.readUnsignedByte() * 0.02);
}
if (BitUtil.check(mask, 7)) {
buf.skipBytes(16);
diff --git a/src/main/java/org/traccar/protocol/BlueProtocol.java b/src/main/java/org/traccar/protocol/BlueProtocol.java
index 79f0714ec..d5dc5c421 100644
--- a/src/main/java/org/traccar/protocol/BlueProtocol.java
+++ b/src/main/java/org/traccar/protocol/BlueProtocol.java
@@ -26,7 +26,7 @@ public class BlueProtocol extends BaseProtocol {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 2, 3, 0));
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 2, -2, 0));
pipeline.addLast(new BlueProtocolDecoder(BlueProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java b/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java
index 98a8ae565..f35ac6fbe 100644
--- a/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,11 +16,14 @@
package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
import org.traccar.model.Position;
@@ -41,6 +44,41 @@ public class BlueProtocolDecoder extends BaseProtocolDecoder {
return negative ? -coordinate : coordinate;
}
+ private void sendResponse(Channel channel, int deviceIndex) {
+ if (channel != null) {
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0xaa);
+ response.writeShort(2 + 1 + 1 + 6 + 1);
+ response.writeByte(0x86); // version
+ response.writeByte(0);
+
+ response.writeByte(6); // data length
+ response.writeByte(0xa4); // type
+ response.writeByte(0); // server index
+ response.writeByte(deviceIndex);
+ response.writeByte(0);
+ response.writeByte(0);
+
+ response.writeByte(Checksum.xor(response.nioBuffer(1, response.writerIndex() - 1)));
+
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private String decodeAlarm(int value) {
+ switch (value) {
+ case 1:
+ return Position.ALARM_SOS;
+ case 8:
+ return Position.ALARM_OVERSPEED;
+ case 19:
+ return Position.ALARM_LOW_POWER;
+ default:
+ return null;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -66,7 +104,7 @@ public class BlueProtocolDecoder extends BaseProtocolDecoder {
int frameEnd = buf.readerIndex() + buf.readUnsignedByte();
int type = buf.readUnsignedByte();
- buf.readUnsignedByte(); // reference id
+ int index = buf.readUnsignedByte();
buf.readUnsignedByte();
buf.readUnsignedByte(); // flags
@@ -98,10 +136,29 @@ public class BlueProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // status 2
buf.readUnsignedByte(); // status 3
- buf.readUnsignedByte(); // status 4
+
+ status = buf.readUnsignedByte(); // status 4
+ int ignition = BitUtil.between(status, 2, 4);
+ if (ignition == 0b01) {
+ position.set(Position.KEY_IGNITION, false);
+ }
+ if (ignition == 0b10) {
+ position.set(Position.KEY_IGNITION, true);
+ }
+
buf.readUnsignedByte(); // status 5
buf.readUnsignedByte(); // status 6
+ position.set(Position.KEY_STATUS, buf.readUnsignedShort());
+
+ } else if (type == 0x81) {
+
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+
+ } else if (type == 0x84) {
+
+ sendResponse(channel, index);
+
}
buf.readerIndex(frameEnd);
diff --git a/src/main/java/org/traccar/protocol/EskyProtocol.java b/src/main/java/org/traccar/protocol/EskyProtocol.java
index aaa92da58..fb047c207 100644
--- a/src/main/java/org/traccar/protocol/EskyProtocol.java
+++ b/src/main/java/org/traccar/protocol/EskyProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,14 @@ public class EskyProtocol extends BaseProtocol {
pipeline.addLast(new EskyProtocolDecoder(EskyProtocol.this));
}
});
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new EskyProtocolDecoder(EskyProtocol.this));
+ }
+ });
}
}
diff --git a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
index 641b2e28f..1a4f9b906 100644
--- a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,9 +16,12 @@
package org.traccar.protocol;
import io.netty.channel.Channel;
+import io.netty.channel.socket.DatagramChannel;
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.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -35,7 +38,7 @@ public class EskyProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN = new PatternBuilder()
.expression("..;") // header
- .number("d+;") // index
+ .number("d+;")
.number("(d+);") // imei
.text("R;") // data type
.number("(d+)[+;]") // satellites
@@ -58,7 +61,8 @@ public class EskyProtocolDecoder extends BaseProtocolDecoder {
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- Parser parser = new Parser(PATTERN, (String) msg);
+ String sentence = (String) msg;
+ Parser parser = new Parser(PATTERN, sentence);
if (!parser.matches()) {
return null;
}
@@ -81,13 +85,21 @@ public class EskyProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(parser.nextDouble());
if (parser.hasNext(3)) {
- position.set(Position.KEY_INPUT, parser.nextHexInt());
+ int input = parser.nextHexInt();
+ position.set(Position.KEY_IGNITION, !BitUtil.check(input, 0));
+ position.set(Position.PREFIX_IN + 1, !BitUtil.check(input, 1));
+ position.set(Position.PREFIX_IN + 2, !BitUtil.check(input, 2));
position.set(Position.KEY_EVENT, parser.nextInt());
position.set(Position.KEY_ODOMETER, parser.nextInt());
}
position.set(Position.KEY_BATTERY, parser.nextInt() * 0.001);
+ int index = sentence.lastIndexOf('+');
+ if (index > 0 && channel instanceof DatagramChannel) {
+ channel.writeAndFlush(new NetworkMessage("ACK," + sentence.substring(index + 1) + "#", remoteAddress));
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
index 40e146e0b..9bc7cb504 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -116,6 +116,32 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_POWER_CUT;
case 16:
return Position.ALARM_POWER_RESTORED;
+ case 17:
+ return Position.ALARM_LOW_BATTERY;
+ case 18:
+ return Position.ALARM_OVERSPEED;
+ case 20:
+ return Position.ALARM_GPS_ANTENNA_CUT;
+ case 21:
+ return Position.ALARM_VIBRATION;
+ case 23:
+ return Position.ALARM_ACCELERATION;
+ case 24:
+ return Position.ALARM_BRAKING;
+ case 27:
+ return Position.ALARM_FATIGUE_DRIVING;
+ case 30:
+ case 32:
+ return Position.ALARM_JAMMING;
+ case 33:
+ return Position.ALARM_GEOFENCE_EXIT;
+ case 34:
+ return Position.ALARM_GEOFENCE_ENTER;
+ case 35:
+ return Position.ALARM_IDLE;
+ case 40:
+ case 41:
+ return Position.ALARM_TEMPERATURE;
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
index 043839be9..087861635 100644
--- a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
+import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -58,16 +59,16 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
return String.format("%02d%02d%02d%02d%02d", d1, d2, d3, d4, d5);
}
- private void sendResponse(Channel channel, SocketAddress remoteAddress, byte calibration) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, int type, int checksum) {
if (channel != null) {
ByteBuf response = Unpooled.buffer();
- response.writeByte(0x24); response.writeByte(0x24); // header
- response.writeByte(MSG_HEARTBEAT); // size
- response.writeShort(5);
- response.writeByte(calibration);
- response.writeByte(0); // main order
- response.writeByte(0); // slave order
- response.writeByte(1); // calibration
+ response.writeShort(0x2424); // header
+ response.writeByte(MSG_HEARTBEAT);
+ response.writeShort(5); // length
+ response.writeByte(checksum);
+ response.writeByte(type);
+ response.writeByte(0); // subtype
+ response.writeByte(Checksum.xor(response.nioBuffer(2, response.writerIndex())));
response.writeByte(0x0D);
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
@@ -87,7 +88,7 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(), buf.readUnsignedByte(),
buf.readUnsignedByte(), buf.readUnsignedByte());
- sendResponse(channel, remoteAddress, buf.getByte(buf.writerIndex() - 2));
+ sendResponse(channel, remoteAddress, type, buf.getByte(buf.writerIndex() - 2));
if (type == MSG_POSITION_DATA || type == MSG_ROLLCALL_RESPONSE
|| type == MSG_ALARM_DATA || type == MSG_BLIND_AREA) {
diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
index 26af8d5af..ab89f10c8 100644
--- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import io.netty.channel.Channel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import org.traccar.BaseHttpProtocolDecoder;
@@ -96,21 +97,23 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
rootElement.appendChild(state);
Element stateMessage = document.createElement("stateMessage");
- stateMessage.appendChild(document.createTextNode("Messages received and stored successfully"));
+ stateMessage.appendChild(document.createTextNode("Store OK"));
rootElement.appendChild(stateMessage);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
ByteBuf content = Unpooled.buffer();
transformer.transform(new DOMSource(document), new StreamResult(new ByteBufOutputStream(content)));
- FullHttpResponse response = new DefaultFullHttpResponse(
- HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
if (channel != null) {
+ FullHttpResponse response = new DefaultFullHttpResponse(
+ HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
+ response.headers()
+ .add(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes())
+ .add(HttpHeaderNames.CONTENT_TYPE, "text/xml");
channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
}
}
-
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
index c435b6cec..946652b03 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -730,6 +730,14 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
decodeStatus(position, buf);
}
+ if (type == MSG_GPS_LBS_1 && buf.readableBytes() > 75 + 6) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
+ buf.readUnsignedByte(); // alarm
+ buf.readUnsignedByte(); // swiped
+ position.set("driverLicense", data.trim());
+ }
+
if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 2 + 6) {
int mask = buf.readUnsignedShort();
position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7));
diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
index 9449e2d5c..eac06bbfe 100644
--- a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,10 @@ import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
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;
@@ -137,6 +140,8 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, buf.readUnsignedShort() * 1000);
+ Network network = new Network();
+
while (buf.readableBytes() > 4) {
int subtype = buf.readUnsignedShort();
int length = buf.readUnsignedShort() - 4;
@@ -161,12 +166,34 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
position.set(
Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString());
break;
+ case 0x0020:
+ String[] cells = buf.readCharSequence(
+ length, StandardCharsets.US_ASCII).toString().split("\\+");
+ for (String cell : cells) {
+ String[] values = cell.split("@");
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(values[0]), Integer.parseInt(values[1]),
+ Integer.parseInt(values[2], 16), Integer.parseInt(values[3], 16)));
+ }
+ break;
+ case 0x0021:
+ String[] points = buf.readCharSequence(
+ length, StandardCharsets.US_ASCII).toString().split("\\+");
+ for (String point : points) {
+ String[] values = point.split("@");
+ network.addWifiAccessPoint(WifiAccessPoint.from(values[0], Integer.parseInt(values[1])));
+ }
+ break;
default:
buf.skipBytes(length);
break;
}
}
+ if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) {
+ position.setNetwork(network);
+ }
+
sendResponse(channel, MSG_POSITION_RSP, index, null);
return position;
diff --git a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
index e8d77f1a8..6a107d67d 100644
--- a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
@@ -51,7 +51,7 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
.groupEnd()
.number("(d{15}),") // imei
.groupBegin()
- .expression("(..),") // status
+ .expression("([^,]{2}),") // status
.or()
.expression("[^,]*,") // vehicle registration
.number("([01]),").optional() // valid
diff --git a/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java b/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java
index 5b352a961..ad0c9bd32 100644
--- a/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/LeafSpyProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2019 Jesse Hills (jesserockz@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,16 +19,12 @@ package org.traccar.protocol;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
-import io.netty.handler.codec.http.DefaultFullHttpResponse;
-import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
-import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.traccar.BaseHttpProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.Protocol;
import org.traccar.model.Position;
-import org.traccar.NetworkMessage;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
@@ -122,13 +118,8 @@ public class LeafSpyProtocolDecoder extends BaseHttpProtocolDecoder {
}
if (position.getDeviceId() != 0) {
- if (channel != null) {
- HttpResponse response = new DefaultFullHttpResponse(
- HttpVersion.HTTP_1_1,
- HttpResponseStatus.OK,
+ sendResponse(channel, HttpResponseStatus.OK,
Unpooled.copiedBuffer("\"status\":\"0\"", StandardCharsets.US_ASCII));
- channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
- }
return position;
} else {
sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
index cbfc3660a..bd66cdc4b 100644
--- a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
}
private static final Pattern PATTERN = new PatternBuilder()
- .number("(dd)(dd)(dd).?d*,") // time (hhmmss)
+ .number("(d+)(dd)(dd).?d*,") // time (hhmmss)
.expression("([AV]),") // validity
.number("(d+)(dd.d+),") // latitude
.expression("([NS]),")
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
index 55260ef0c..529496928 100644
--- a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -70,7 +70,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
.number("(d+)|") // mnc
.number("(x+)|") // lac
.number("(x+),") // cid
- .number("(x+),") // state
+ .number("(xx)") // input
+ .number("(xx),") // output
.number("(x+)?|") // adc1
.number("(x+)?|") // adc2
.number("(x+)?|") // adc3
@@ -149,39 +150,38 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
}
position.setDeviceId(deviceSession.getDeviceId());
- int event = parser.nextInt(0);
+ int event = parser.nextInt();
position.set(Position.KEY_EVENT, event);
position.set(Position.KEY_ALARM, decodeAlarm(event));
- position.setLatitude(parser.nextDouble(0));
- position.setLongitude(parser.nextDouble(0));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
position.setTime(parser.nextDateTime());
position.setValid(parser.next().equals("A"));
position.set(Position.KEY_SATELLITES, parser.nextInt());
- int rssi = parser.nextInt(0);
+ int rssi = parser.nextInt();
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
position.set(Position.KEY_HDOP, parser.nextDouble());
- position.setAltitude(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble());
- position.set(Position.KEY_ODOMETER, parser.nextInt(0));
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
position.set("runtime", parser.next());
position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0), rssi)));
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), rssi)));
- position.set(Position.KEY_STATUS, parser.next());
+ position.set(Position.KEY_OUTPUT, parser.nextHexInt());
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
for (int i = 1; i <= 3; i++) {
- if (parser.hasNext()) {
- position.set(Position.PREFIX_ADC + i, parser.nextHexInt(0));
- }
+ position.set(Position.PREFIX_ADC + i, parser.nextHexInt());
}
String deviceModel = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getModel();
diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
index b6f257d2c..b8ab134c5 100644
--- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
@@ -154,6 +154,15 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
mac.substring(0, mac.length() - 1), rssi));
}
break;
+ case 0x23:
+ if (endIndex > buf.readerIndex()) {
+ buf.skipBytes(6); // mac
+ }
+ if (endIndex > buf.readerIndex()) {
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ }
+ break;
case 0x24:
position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
long status = buf.readUnsignedIntLE();
diff --git a/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java b/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java
index 5adf61494..4d9c9afc4 100644
--- a/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OkoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ public class OkoProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN = new PatternBuilder()
.text("{")
.number("(d{15}),").optional() // imei
- .number("(dd)(dd)(dd).d+,") // time
+ .number("(dd)(dd)(dd)(?:.d+)?,") // time
.expression("([AV]),") // validity
.number("(dd)(dd.d+),") // latitude
.expression("([NS]),")
@@ -46,14 +46,23 @@ public class OkoProtocolDecoder extends BaseProtocolDecoder {
.number("(d+.?d*)?,") // course
.number("(dd)(dd)(dd),") // date (ddmmyy)
.number("(d+),") // satellites
- .number("(d+.d+),") // adc
+ .number("(d+.d+|xx),") // adc
.number("(xx),") // event
- .number("(d+.d+),") // power
+ .number("(d+.d+|xx),") // power
.number("d,") // memory status
- .number("(xx)") // io
+ .number("(xx)?") // io
.any()
.compile();
+ private double decodeVoltage(Parser parser) {
+ String value = parser.next();
+ if (value.contains(".")) {
+ return Double.parseDouble(value);
+ } else {
+ return Integer.parseInt(value, 16) * 0.1;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -89,9 +98,9 @@ public class OkoProtocolDecoder extends BaseProtocolDecoder {
position.setTime(dateBuilder.getDate());
position.set(Position.KEY_SATELLITES, parser.nextInt());
- position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.PREFIX_ADC + 1, decodeVoltage(parser));
position.set(Position.KEY_EVENT, parser.next());
- position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_POWER, decodeVoltage(parser));
position.set(Position.KEY_INPUT, parser.nextHexInt());
return position;
diff --git a/src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java b/src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java
index 1caf6ceb9..314f19757 100644
--- a/src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java
@@ -27,11 +27,11 @@ public class OmnicommFrameDecoder extends BaseFrameDecoder {
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
- if (buf.readableBytes() < 10) {
+ if (buf.readableBytes() < 6) {
return null;
}
- int endIndex = buf.getUnsignedShortLE(2) + buf.readerIndex() + 6;
+ int endIndex = buf.getUnsignedShortLE(buf.readerIndex() + 2) + buf.readerIndex() + 6;
if (buf.writerIndex() < endIndex) {
return null;
}
diff --git a/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java b/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java
index cd8b74c9a..6e9cf52a5 100644
--- a/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -111,16 +111,29 @@ public class OmnicommProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
+ if (message.hasGeneral()) {
+ OmnicommMessageOuterClass.OmnicommMessage.General data = message.getGeneral();
+ position.set(Position.KEY_POWER, data.getUboard() * 0.1);
+ position.set(Position.KEY_BATTERY_LEVEL, data.getBatLife());
+ }
+
if (message.hasNAV()) {
- OmnicommMessageOuterClass.OmnicommMessage.NAV nav = message.getNAV();
+ OmnicommMessageOuterClass.OmnicommMessage.NAV data = message.getNAV();
position.setValid(true);
- position.setTime(new Date((nav.getGPSTime() + 1230768000) * 1000L)); // from 2009-01-01 12:00
- position.setLatitude(nav.getLAT() * 0.0000001);
- position.setLongitude(nav.getLON() * 0.0000001);
- position.setSpeed(UnitsConverter.knotsFromKph(nav.getGPSVel() * 0.1));
- position.setCourse(nav.getGPSDir());
- position.setAltitude(nav.getGPSAlt() * 0.1);
- position.set(Position.KEY_SATELLITES, nav.getGPSNSat());
+ position.setTime(new Date((data.getGPSTime() + 1230768000) * 1000L)); // from 2009-01-01 12:00
+ position.setLatitude(data.getLAT() * 0.0000001);
+ position.setLongitude(data.getLON() * 0.0000001);
+ position.setSpeed(UnitsConverter.knotsFromKph(data.getGPSVel() * 0.1));
+ position.setCourse(data.getGPSDir());
+ position.setAltitude(data.getGPSAlt() * 0.1);
+ position.set(Position.KEY_SATELLITES, data.getGPSNSat());
+ }
+
+ if (message.hasLLSDt()) {
+ OmnicommMessageOuterClass.OmnicommMessage.LLSDt data = message.getLLSDt();
+ position.set("fuel1Temp", data.getTLLS1());
+ position.set("fuel1", data.getCLLS1());
+ position.set("fuel1State", data.getFLLS1());
}
if (position.getFixTime() != null) {
diff --git a/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java b/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java
index 3bc71de81..ec9bbc240 100644
--- a/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/OsmAndProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,15 +15,19 @@
*/
package org.traccar.protocol;
+import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.Protocol;
+import org.traccar.database.CommandsManager;
import org.traccar.helper.DateUtil;
import org.traccar.model.CellTower;
+import org.traccar.model.Command;
import org.traccar.model.Network;
import org.traccar.model.Position;
import org.traccar.model.WifiAccessPoint;
@@ -173,7 +177,18 @@ public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder {
}
if (position.getDeviceId() != 0) {
- sendResponse(channel, HttpResponseStatus.OK);
+ String response = null;
+ CommandsManager commandsManager = Context.getCommandsManager();
+ if (commandsManager != null) {
+ for (Command command : commandsManager.readQueuedCommands(position.getDeviceId(), 1)) {
+ response = command.getString(Command.KEY_DATA);
+ }
+ }
+ if (response != null) {
+ sendResponse(channel, HttpResponseStatus.OK, Unpooled.copiedBuffer(response, StandardCharsets.UTF_8));
+ } else {
+ sendResponse(channel, HttpResponseStatus.OK);
+ }
return position;
} else {
sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
@@ -181,4 +196,8 @@ public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder {
}
}
+ @Override
+ protected void sendQueuedCommands(Channel channel, SocketAddress remoteAddress, long deviceId) {
+ }
+
}
diff --git a/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
index 15e08d7b1..d49a73a86 100644
--- a/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,7 +63,7 @@ public class PacificTrackProtocolDecoder extends BaseProtocolDecoder {
while (buf.isReadable()) {
int segmentId = readBitExt(buf);
- int segmentLength = readBitExt(buf);
+ int segmentEnd = readBitExt(buf) + buf.readerIndex();
switch (segmentId) {
case 0x01:
@@ -83,12 +83,60 @@ public class PacificTrackProtocolDecoder extends BaseProtocolDecoder {
position.setSpeed(UnitsConverter.knotsFromKph(BitUtil.to(speedAndCourse, 12) * 0.1));
position.set(Position.KEY_INDEX, buf.readUnsignedShort());
break;
+ case 0x92:
+ while (buf.readerIndex() < segmentEnd) {
+ int field = buf.readUnsignedByte();
+ int fieldPrefix = BitUtil.from(field, 5);
+ if (fieldPrefix < 0b100) {
+ switch (BitUtil.between(field, 2, 5)) {
+ case 0b000:
+ position.set("bus", BitUtil.to(field, 2));
+ case 0b001:
+ position.set("currentGear", BitUtil.to(field, 2));
+ break;
+ default:
+ break;
+ }
+ } else if (fieldPrefix < 0b101) {
+ switch (BitUtil.to(field, 5)) {
+ case 0b00000:
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte());
+ break;
+ case 0b00001:
+ position.set(Position.KEY_RPM, buf.readUnsignedByte() * 32);
+ break;
+ default:
+ buf.readUnsignedByte();
+ break;
+ }
+ } else if (fieldPrefix < 0b110) {
+ buf.readUnsignedShort();
+ } else if (fieldPrefix < 0b111) {
+ switch (BitUtil.to(field, 5)) {
+ case 0b00000:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100);
+ break;
+ case 0b00001:
+ position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 180);
+ break;
+ case 0b00010:
+ position.set("idleHours", buf.readUnsignedInt() * 180);
+ break;
+ default:
+ buf.readUnsignedInt();
+ break;
+ }
+ } else {
+ buf.skipBytes(buf.readUnsignedByte());
+ }
+ }
+ break;
case 0x100:
String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(0, 15);
deviceSession = getDeviceSession(channel, remoteAddress, imei);
break;
default:
- buf.skipBytes(segmentLength);
+ buf.readerIndex(segmentEnd);
break;
}
}
diff --git a/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java
index 47aa86da7..26ce2fe53 100644
--- a/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,15 +18,11 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
-import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
-import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
-import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
-import org.traccar.BaseProtocolDecoder;
+import org.traccar.BaseHttpProtocolDecoder;
import org.traccar.DeviceSession;
-import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
@@ -37,19 +33,14 @@ import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
-public class PiligrimProtocolDecoder extends BaseProtocolDecoder {
+public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder {
public PiligrimProtocolDecoder(Protocol protocol) {
super(protocol);
}
private void sendResponse(Channel channel, String message) {
- if (channel != null) {
- FullHttpResponse response = new DefaultFullHttpResponse(
- HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
- Unpooled.copiedBuffer(message, StandardCharsets.US_ASCII));
- channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
- }
+ sendResponse(channel, HttpResponseStatus.OK, Unpooled.copiedBuffer(message, StandardCharsets.US_ASCII));
}
public static final int MSG_GPS = 0xF1;
diff --git a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
index 8981c117b..05601ed51 100644
--- a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
index 304f61836..5fc81085b 100644
--- a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,6 +48,31 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
super(protocol);
}
+ private boolean jsonContains(JsonObject json, String key) {
+ if (json.containsKey(key)) {
+ JsonValue value = json.get(key);
+ if (value.getValueType() == JsonValue.ValueType.STRING) {
+ return !((JsonString) value).getString().equals("null");
+
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean getJsonBoolean(JsonObject json, String key) {
+ JsonValue value = json.get(key);
+ if (value != null) {
+ if (value.getValueType() == JsonValue.ValueType.STRING) {
+ return Boolean.parseBoolean(((JsonString) value).getString());
+ } else {
+ return value.getValueType() == JsonValue.ValueType.TRUE;
+ }
+ }
+ return false;
+ }
+
private int getJsonInt(JsonObject json, String key) {
JsonValue value = json.get(key);
if (value != null) {
@@ -83,7 +108,14 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
}
JsonObject json = Json.createReader(new StringReader(content)).readObject();
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, json.getString("device"));
+ String deviceId;
+ if (json.containsKey("device")) {
+ deviceId = json.getString("device");
+ } else {
+ deviceId = json.getString("deviceId");
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, deviceId);
if (deviceSession == null) {
sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
return null;
@@ -92,29 +124,36 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- if (json.containsKey("time")) {
+ if (jsonContains(json, "time")) {
position.setTime(new Date(getJsonInt(json, "time") * 1000L));
+ } else if (jsonContains(json, "positionTime")) {
+ position.setTime(new Date(getJsonInt(json, "positionTime") * 1000L));
} else {
position.setTime(new Date());
}
- if (json.containsKey("location")
- || json.containsKey("lat") && json.containsKey("lng") && !json.containsKey("data")) {
+ if (jsonContains(json, "lastSeen")) {
+ position.setDeviceTime(new Date(getJsonInt(json, "lastSeen") * 1000L));
+ }
+
+ if (jsonContains(json, "location")
+ || jsonContains(json, "lat") && jsonContains(json, "lng") && !jsonContains(json, "data")
+ || jsonContains(json, "latitude") && jsonContains(json, "longitude") && !jsonContains(json, "data")) {
JsonObject location;
- if (json.containsKey("location")) {
+ if (jsonContains(json, "location")) {
location = json.getJsonObject("location");
} else {
location = json;
}
position.setValid(true);
- position.setLatitude(getJsonDouble(location, "lat"));
- position.setLongitude(getJsonDouble(location, "lng"));
+ position.setLatitude(getJsonDouble(location, jsonContains(location, "lat") ? "lat" : "latitude"));
+ position.setLongitude(getJsonDouble(location, jsonContains(location, "lng") ? "lng" : "longitude"));
- } else {
+ } else if (jsonContains(json, "data") || jsonContains(json, "payload")) {
- String data = json.getString(json.containsKey("data") ? "data" : "payload");
+ String data = json.getString(jsonContains(json, "data") ? "data" : "payload");
ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(data));
try {
int event = buf.readUnsignedByte();
@@ -205,10 +244,19 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
getLastLocation(position, position.getDeviceTime());
}
- if (json.containsKey("rssi")) {
+ if (jsonContains(json, "moving")) {
+ position.set(Position.KEY_MOTION, getJsonBoolean(json, "moving"));
+ }
+ if (jsonContains(json, "magStatus")) {
+ position.set(Position.KEY_BLOCKED, getJsonBoolean(json, "magStatus"));
+ }
+ if (jsonContains(json, "temperature")) {
+ position.set(Position.KEY_DEVICE_TEMP, getJsonDouble(json, "temperature"));
+ }
+ if (jsonContains(json, "rssi")) {
position.set(Position.KEY_RSSI, getJsonDouble(json, "rssi"));
}
- if (json.containsKey("seqNumber")) {
+ if (jsonContains(json, "seqNumber")) {
position.set(Position.KEY_INDEX, getJsonInt(json, "seqNumber"));
}
diff --git a/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java b/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java
index eae37386a..6fe54b0b0 100644
--- a/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -77,10 +77,24 @@ public class SolarPoweredProtocolDecoder extends BaseProtocolDecoder {
position.setLongitude(-position.getLongitude());
}
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
- position.set(Position.KEY_DEVICE_TEMP, (int) buf.readByte());
+ int temperature = buf.readUnsignedByte();
+ if (BitUtil.check(temperature, 7)) {
+ position.set(Position.KEY_DEVICE_TEMP, -BitUtil.to(temperature, 7));
+ } else {
+ position.set(Position.KEY_DEVICE_TEMP, BitUtil.to(temperature, 7));
+ }
position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.02);
position.setCourse(buf.readUnsignedByte());
break;
+ case 0x83:
+ buf.readUnsignedInt(); // uptime
+ buf.readUnsignedInt(); // gps count
+ buf.readUnsignedInt(); // gsm count
+ buf.readUnsignedByte(); // positioning time
+ buf.readUnsignedByte(); // registration time
+ buf.readUnsignedByte(); // connection time
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ break;
default:
buf.skipBytes(length);
break;
diff --git a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
index bad6f03a9..2d1613e03 100644
--- a/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StarLinkProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -140,6 +140,11 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
event = Integer.parseInt(data[i]);
position.set(Position.KEY_ALARM, decodeAlarm(event));
position.set(Position.KEY_EVENT, event);
+ if (event == 24) {
+ position.set(Position.KEY_IGNITION, true);
+ } else if (event == 25) {
+ position.set(Position.KEY_IGNITION, false);
+ }
break;
case "#PDT#":
position.setFixTime(dateFormat.parse(data[i]));
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
index 915f764e1..978d768be 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -397,7 +397,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
if (isIncludeAdc(deviceSession.getDeviceId())) {
for (int i = 1; i <= 3; i++) {
- if (!values[index++].isEmpty()) {
+ if (index < values.length && !values[index++].isEmpty()) {
position.set(Position.PREFIX_ADC + i, Double.parseDouble(values[index - 1]));
}
}
@@ -614,10 +614,21 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
}
+ int alertId = 0;
if (BitUtil.check(mask, 19)) {
- int value = buf.readUnsignedByte();
+ alertId = buf.readUnsignedByte();
if (type == 0x82) {
- position.set(Position.KEY_ALARM, decodeAlert(value));
+ position.set(Position.KEY_ALARM, decodeAlert(alertId));
+ }
+ }
+
+ if (BitUtil.check(mask, 20)) {
+ buf.readUnsignedShort(); // alert mod
+ }
+
+ if (BitUtil.check(mask, 21)) {
+ if (alertId == 59) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, ByteBufUtil.hexDump(buf.readSlice(8)));
}
}
diff --git a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
index 9b146ec90..3331ebb71 100644
--- a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -177,8 +177,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
}
private Position decodePosition(
- Channel channel, DeviceSession deviceSession,
- ByteBuf buf, int type, int index, ByteBuf imei) {
+ Channel channel, DeviceSession deviceSession, ByteBuf buf, int type, int index, ByteBuf imei) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -214,8 +213,10 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.PREFIX_OUT + (i + 1), BitUtil.check(io, 7 + i));
}
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+ if (header != 0x2626) {
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
+ }
}
diff --git a/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java b/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java
index 457687b2e..a4f9e2989 100644
--- a/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TelicProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,8 +42,8 @@ public class TelicProtocolDecoder extends BaseProtocolDecoder {
.number("(dd)(dd)(dd)") // date (ddmmyy)
.number("(dd)(dd)(dd),") // time (hhmmss)
.groupBegin()
- .number("(-?d{9}),") // longitude
- .number("(-?d{8}),") // latitude
+ .number("(-?d{8,}),") // longitude
+ .number("(-?d{7,}),") // latitude
.or()
.number("(-?d+),") // longitude
.number("(-?d+),") // latitude
diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
index 7ad99b37e..3ce6438f8 100644
--- a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
@@ -38,7 +38,7 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN_HEADER = new PatternBuilder()
.number("#(d+)") // imei
.expression("#[^#]*") // user
- .number("#d+") // password
+ .number("#d*") // password
.groupBegin()
.number("#([01])") // door
.number("#(d+)") // fuel voltage
diff --git a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
index ea72b7cb8..eee0e9ae8 100644
--- a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,8 @@ import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
+import org.traccar.helper.BcdUtil;
+import org.traccar.helper.DateBuilder;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
@@ -44,11 +46,11 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_WIFI_OFFLINE = 0x17;
public static final int MSG_WIFI = 0x69;
- private void sendResponse(Channel channel, int type, ByteBuf content) {
+ private void sendResponse(Channel channel, int length, int type, ByteBuf content) {
if (channel != null) {
ByteBuf response = Unpooled.buffer();
response.writeShort(0x7878);
- response.writeByte(1 + content.readableBytes());
+ response.writeByte(length);
response.writeByte(type);
response.writeBytes(content);
response.writeByte('\r');
@@ -75,7 +77,7 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
deviceSession = getDeviceSession(channel, remoteAddress, imei);
ByteBuf content = Unpooled.buffer();
content.writeByte(deviceSession != null ? 0x01 : 0x44);
- sendResponse(channel, type, content);
+ sendResponse(channel, length, type, content);
return null;
} else {
deviceSession = getDeviceSession(channel, remoteAddress);
@@ -95,7 +97,7 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
ByteBuf content = Unpooled.buffer();
content.writeBytes(time);
- sendResponse(channel, type, content);
+ sendResponse(channel, length, type, content);
return position;
@@ -127,7 +129,7 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
if (length >= 7) {
content.writeByte(signal);
}
- sendResponse(channel, type, content);
+ sendResponse(channel, length, type, content);
return position;
@@ -136,9 +138,17 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- getLastLocation(position, null);
-
ByteBuf time = buf.readSlice(6);
+ DateBuilder dateBuilder = new DateBuilder()
+ .setYear(BcdUtil.readInteger(time, 2))
+ .setMonth(BcdUtil.readInteger(time, 2))
+ .setDay(BcdUtil.readInteger(time, 2))
+ .setHour(BcdUtil.readInteger(time, 2))
+ .setMinute(BcdUtil.readInteger(time, 2))
+ .setSecond(BcdUtil.readInteger(time, 2));
+ time.resetReaderIndex();
+
+ getLastLocation(position, dateBuilder.getDate());
Network network = new Network();
for (int i = 0; i < length; i++) {
@@ -160,7 +170,7 @@ public class TopinProtocolDecoder extends BaseProtocolDecoder {
ByteBuf content = Unpooled.buffer();
content.writeBytes(time);
- sendResponse(channel, type, content);
+ sendResponse(channel, length, type, content);
return position;
diff --git a/src/main/java/org/traccar/protocol/WondexProtocol.java b/src/main/java/org/traccar/protocol/WondexProtocol.java
index 035dd9160..6401fde85 100644
--- a/src/main/java/org/traccar/protocol/WondexProtocol.java
+++ b/src/main/java/org/traccar/protocol/WondexProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@ import io.netty.handler.codec.string.StringEncoder;
public class WondexProtocol extends BaseProtocol {
public WondexProtocol() {
- setSupportedCommands(
+ setSupportedDataCommands(
Command.TYPE_GET_DEVICE_STATUS,
Command.TYPE_GET_MODEM_STATUS,
Command.TYPE_REBOOT_DEVICE,
@@ -33,6 +33,13 @@ public class WondexProtocol extends BaseProtocol {
Command.TYPE_GET_VERSION,
Command.TYPE_IDENTIFICATION);
setTextCommandEncoder(new WondexProtocolEncoder(this));
+ setSupportedTextCommands(
+ Command.TYPE_GET_DEVICE_STATUS,
+ Command.TYPE_GET_MODEM_STATUS,
+ Command.TYPE_REBOOT_DEVICE,
+ Command.TYPE_POSITION_SINGLE,
+ Command.TYPE_GET_VERSION,
+ Command.TYPE_IDENTIFICATION);
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
diff --git a/src/test/java/org/traccar/geocoder/GeocoderTest.java b/src/test/java/org/traccar/geocoder/GeocoderTest.java
index 9f59d0b23..e70719b89 100644
--- a/src/test/java/org/traccar/geocoder/GeocoderTest.java
+++ b/src/test/java/org/traccar/geocoder/GeocoderTest.java
@@ -32,7 +32,7 @@ public class GeocoderTest {
@Ignore
@Test
public void testGisgraphy() {
- Geocoder geocoder = new GisgraphyGeocoder(new AddressFormat());
+ Geocoder geocoder = new GisgraphyGeocoder(null, 0, new AddressFormat());
String address = geocoder.getAddress(48.8530000, 2.3400000, null);
assertEquals("Rue du Jardinet, Paris, Île-de-France, FR", address);
}
diff --git a/src/test/java/org/traccar/helper/ChecksumTest.java b/src/test/java/org/traccar/helper/ChecksumTest.java
index 5737b9ff5..ff48527bc 100644
--- a/src/test/java/org/traccar/helper/ChecksumTest.java
+++ b/src/test/java/org/traccar/helper/ChecksumTest.java
@@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.junit.Test;
+import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import static org.junit.Assert.assertEquals;
@@ -36,4 +37,13 @@ public class ChecksumTest {
assertEquals(0, Checksum.luhn(63070019470771L));
}
+ @Test
+ public void testModulo256() {
+ assertEquals(0x00, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x00})));
+ assertEquals(0x00, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x00, 0x00, 0x00})));
+ assertEquals(0xca, Checksum.modulo256(ByteBuffer.wrap(new byte[] {0x77, 0x77, 0x77, 0x77, 0x77, 0x77})));
+
+
+ }
+
}
diff --git a/src/test/java/org/traccar/helper/ServletHelperTest.java b/src/test/java/org/traccar/helper/ServletHelperTest.java
index 7359bf3dd..e419b6491 100644
--- a/src/test/java/org/traccar/helper/ServletHelperTest.java
+++ b/src/test/java/org/traccar/helper/ServletHelperTest.java
@@ -11,7 +11,7 @@ import static org.junit.Assert.assertEquals;
public class ServletHelperTest {
@Test
- public void testIpBehindReverseProxy() {
+ public void testRetrieveRemoteAddressProxyMultiple() {
MockRequest request = new MockRequest();
request.setRemoteAddress("147.120.1.5");
request.addHeader("X-FORWARDED-FOR", "231.23.45.65, 10.20.10.33, 10.20.20.34");
@@ -20,7 +20,16 @@ public class ServletHelperTest {
}
@Test
- public void testNormalIp() {
+ public void testRetrieveRemoteAddressProxySingle() {
+ MockRequest request = new MockRequest();
+ request.setRemoteAddress("147.120.1.5");
+ request.addHeader("X-FORWARDED-FOR", "231.23.45.65");
+
+ assertEquals("231.23.45.65", ServletHelper.retrieveRemoteAddress(request));
+ }
+
+ @Test
+ public void testRetrieveRemoteAddressNoProxy() {
MockRequest request = new MockRequest();
request.setRemoteAddress("231.23.45.65");
diff --git a/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java
new file mode 100644
index 000000000..f2940de59
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ArnaviBinaryProtocolDecoderTest.java
@@ -0,0 +1,38 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ArnaviBinaryProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testHeader1Decode() throws Exception {
+
+ ArnaviBinaryProtocolDecoder decoder;
+
+ decoder = new ArnaviBinaryProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "ff22f30c45f5c90f0300"));
+
+ verifyPositions(decoder, binary(
+ "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ position("2017-07-07 05:09:55.000", true, 45.05597, 39.03347));
+ }
+
+ @Test
+ public void testHeader2Decode() throws Exception {
+
+ ArnaviBinaryProtocolDecoder decoder;
+
+ decoder = new ArnaviBinaryProtocolDecoder(null);
+
+ verifyNull(decoder, binary(
+ "ff23f30c45f5c90f0300"));
+
+ verifyPositions(decoder, binary(
+ "5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ position("2017-07-07 05:09:55.000", true, 45.05597, 39.03347));
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java
new file mode 100644
index 000000000..90eb20296
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ArnaviFrameDecoderTest.java
@@ -0,0 +1,51 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class ArnaviFrameDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecodeValidPackets() throws Exception {
+
+ ArnaviFrameDecoder decoder = new ArnaviFrameDecoder();
+
+ verifyFrame(
+ binary("2441562c563344492c38353136342c3231342c2d312c31392c30303030344634462c30303030303935452c30433030303030322c3836333037313031333034313631382c38393939373031353630333832353236363232462c2a3039"),
+ decoder.decode(null, null, binary("2441562c563344492c38353136342c3231342c2d312c31392c30303030344634462c30303030303935452c30433030303030322c3836333037313031333034313631382c38393939373031353630333832353236363232462c2a30390d0a")));
+
+ verifyFrame(
+ binary("ff22f30c45f5c90f0300"),
+ decoder.decode(null, null, binary("ff22f30c45f5c90f0300")));
+
+ verifyFrame(
+ binary("5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ decoder.decode(null, null, binary("5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d")));
+
+ verifyFrame(
+ binary("5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d"),
+ decoder.decode(null, null, binary("5b01012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa3701000029012800a3175f5903513934420447221c42055402781E0900f0c5215b4e0084005c00007c005d0000a300fa37010000295d")));
+
+ verifyFrame(
+ binary("5b01030700e3f16b50747261636361721b5d"),
+ decoder.decode(null, null, binary("5b01030700e3f16b50747261636361721b5d")));
+
+ verifyFrame(
+ binary("5b01030700e3f16b50747261636361721b030700e3f16b50747261636361721b5d"),
+ decoder.decode(null, null, binary("5b01030700e3f16b50747261636361721b030700e3f16b50747261636361721b5d")));
+
+ verifyFrame(
+ binary("5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d"),
+ decoder.decode(null, null, binary("5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d")));
+
+ verifyFrame(
+ binary("5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d"),
+ decoder.decode(null, null, binary("5b01061400e3f16b5003298b5e4204cbd514420500191000080400ff021b061400e3f16b5003298b5e4204cbd514420500191000080400ff021b5d")));
+
+ verifyFrame(
+ binary("5bfd005d"),
+ decoder.decode(null, null, binary("5bfd005d")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java
index 6b075facc..79179d4f3 100644
--- a/src/test/java/org/traccar/protocol/ArnaviProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/ArnaviTextProtocolDecoderTest.java
@@ -3,38 +3,38 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
-public class ArnaviProtocolDecoderTest extends ProtocolTest {
+public class ArnaviTextProtocolDecoderTest extends ProtocolTest {
@Test
public void testDecode() throws Exception {
- ArnaviProtocolDecoder decoder = new ArnaviProtocolDecoder(null);
+ ArnaviTextProtocolDecoder decoder = new ArnaviTextProtocolDecoder(null);
- verifyPosition(decoder, text(
+ verifyPosition(decoder, buffer(
"$AV,V4,999999,12487,2277,203,65534,0,0,193,65535,65535,65535,65535,1,13,80.0,56.1,200741,5950.6773N,03029.1043E,300.0,360.0,121012,65535,65535,65535,SF*6E"));
- verifyNull(decoder, text(
+ verifyNull(decoder, buffer(
"$AV,V3DI,85164,20707,-1,19,0008C56A,000879AC,0C000002,863071013041618,89997077111301204297,*0B"));
- verifyNull(decoder, text(
+ verifyNull(decoder, buffer(
"$AV,V6SD,85164,20708,-1,3,6,37,33,*52"));
- verifyAttributes(decoder, text(
+ verifyAttributes(decoder, buffer(
"$AV,V4,85164,20709,1148,418,-1,0,1,192,0,0,0,0,0,0,,,000023,0000.0000N,00000.0000E,0.0,0.0,060180,0,0,32767,*4F"));
- verifyNull(decoder, text(
+ verifyNull(decoder, buffer(
"$AV,V3GSMINFO,85164,-1,20450,KMOBILE,1,2,1,23,0,40101,cc3,1,ce19,401,16,65304,5613,72,,SF*7F"));
- verifyAttributes(decoder, text(
+ verifyAttributes(decoder, buffer(
"$AV,V4,85164,20451,1146,418,-1,1,1,192,0,0,0,0,0,0,,,104340,0000.0000N,00000.0000E,0.0,0.0,060219,11,0,32767,,SF*47"));
- verifyNull(decoder, text(
+ verifyNull(decoder, buffer(
"$AV,V6SD,85164,20452,-1,3,3,5,6769,,SF*5D"));
- verifyPosition(decoder, text(
+ verifyPosition(decoder, buffer(
"$AV,V2,32768,12487,2277,203,-1,0,0,193,0,0,1,13,200741,5950.6773N,03029.1043E,0.0,0.0,121012,*6E"));
- verifyPosition(decoder, text(
+ verifyPosition(decoder, buffer(
"$AV,V3,999999,12487,2277,203,65534,0,0,193,65535,65535,65535,65535,1,13,200741,5950.6773N,03029.1043E,300.0,360.0,121012,65535,65535,65535,SF*6E"));
}
diff --git a/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java
index 1d1716238..4aa50e56b 100644
--- a/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/BlueProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class BlueProtocolDecoderTest extends ProtocolTest {
@@ -10,6 +11,21 @@ public class BlueProtocolDecoderTest extends ProtocolTest {
BlueProtocolDecoder decoder = new BlueProtocolDecoder(null);
+ verifyAttribute(decoder, binary(
+ "AA0056860080E3E79E0C811F80000114020207170520011F00407F8005EE1938113B270000000000000000140202071705005AC7A621121F0002000100B7000080110000000000001A3A0000000001F400000000000078"),
+ Position.KEY_ALARM, Position.ALARM_SOS);
+
+ verifyAttribute(decoder, binary(
+ "AA004A860080E3E79E20015FBE40148005EE193B113B263700000000000000140202080C09005AC7A621125F0002000000BB000000000000000000001A3A0007000001F400000000000008"),
+ Position.KEY_IGNITION, true);
+
+ verifyAttribute(decoder, binary(
+ "AA004A860080E3E79E200160BE40148005EE193B113B263700000000000000140202080C13005AC7A62112600002000000B7000000110000000000001A3A0007000001F400000000000012"),
+ Position.KEY_STATUS, 0x11);
+
+ verifyPosition(decoder, binary(
+ "aa00550000813f6f840b840380001032000000002001030040008005ee1938113b26f300000000000000140114082833044d27602112030002000000b70000020000000000000000650000001601f4000000000000e4"));
+
verifyPosition(decoder, binary(
"aa0055860080e3e79e0b840f800010320000000020010f0040008005ee197f113b26e800000000000000130c11091a2b005ac7a621120f0002000000b7000002000000000000001a3a0000000001f40000000000003f"));
diff --git a/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java
index da7df0ab2..4db80c7ce 100644
--- a/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/EskyProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class EskyProtocolDecoderTest extends ProtocolTest {
@@ -10,6 +11,10 @@ public class EskyProtocolDecoderTest extends ProtocolTest {
EskyProtocolDecoder decoder = new EskyProtocolDecoder(null);
+ verifyAttribute(decoder, text(
+ "ET;0;860337031078319;R;6+190317162511+41.32536+19.83144+0.14+0+0x0+0+18460312+0+1233+192"),
+ Position.KEY_IGNITION, true);
+
verifyPosition(decoder, text(
"EO;0;861311006461908;R;6;180420104751;2.97896;101.65091;0.75;320;3398;1;|"));
diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
index fd5d55a50..672711f22 100644
--- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
@@ -18,6 +18,9 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest {
"78780D01086471700328358100093F040D0A"));
verifyAttributes(decoder, binary(
+ "7878711213081f081d0fc6017ba3fa0ac62a923e550e02080503f300b26d000000004b20202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202030300017c7470d0a"));
+
+ verifyAttributes(decoder, binary(
"797900B2700000000102003500010400330012000000000000000000000000000000000000003400061354A48DFF00003400061154A48E56000011000A000000000000000000000001000803537601000282180002000802140743044211890003000A89340752000038689636001800020182002B000116002C000454A4FF350009000100000A0001010028000100002E000400000000002A00010000290004000000000030000A000101680014016802D00000B38F0D0A"));
verifyAttribute(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
index 6033bc744..b624f69ab 100644
--- a/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/HuaShengProtocolDecoderTest.java
@@ -16,6 +16,9 @@ public class HuaShengProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"c000000077aa0200000000000e000100143347315f48312e315f56312e30372e54000300133335353835353035303434303635380004000b3531323030303000050005010006000400070004000800050000090018383936313032353431343533333239313833360d000a000f796573696e7465726e6574c0"));
+ verifyNotNull(decoder, binary(
+ "c000000077aa00000000000070020000003230303132373035313635330000000000000000000000000000000000010015ffffffff000000000000019dffffffffff0005000a1f00000e455a00200019313238354031406666666540386233663930634030000f0013333536373236313038313335343530c0"));
+
verifyPosition(decoder, binary(
"c000000060aa000000000000fa8000000031393037303431363434323700e9900affd61c1b00000000003a000000010015ffffff0000000000000004c2ffffffffff0005000a0d080000ca6a000900155741555a5a5a344730454e313133373233c0"));
diff --git a/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
index da5a81144..94f4c8202 100644
--- a/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MeiligaoProtocolDecoderTest.java
@@ -10,6 +10,12 @@ public class MeiligaoProtocolDecoderTest extends ProtocolTest {
MeiligaoProtocolDecoder decoder = new MeiligaoProtocolDecoder(null);
+ verifyPosition(decoder, binary(
+ "2424011e143190975469ff99993130343634382e3030302c562c303735332e353338332c4e2c30393832322e313737382c452c302e30302c302c3230303132302c2c2a31417c302e307c307c363430307c303030302c303030302c303130312c303238467c30323038303030353137444630304633363838467c30387c30303030314242367c30307c2520205e59454e53414241494348414924534f4e474b52414e244d522e5e5e3f3b363030373634333130303530303337333835333d3135303531393637303631343d3f2b2020202020202020202020202032342020202020202020202020203120202020202020202020202030303034313131202030303130302020202020202020202020202020202020202020203f7b850d0a"));
+
+ verifyPosition(decoder, binary(
+ "2424008d143190975469ff99993130343634312e3030302c562c303735332e353338332c4e2c30393832322e313737382c452c302e30302c302c3230303132302c2c2a31337c302e307c307c323430307c303030302c303030302c303130302c303238397c30323038303030353137444630304633363838467c30387c30303030314242367c3030be980d0a"));
+
verifyNull(decoder, binary(
"24240012254748594772ff080002ffff0d0a"));
diff --git a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
index 3e05d5243..9f9da26ca 100644
--- a/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MeitrackProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class MeitrackProtocolDecoderTest extends ProtocolTest {
MeitrackProtocolDecoder decoder = new MeitrackProtocolDecoder(null);
+ verifyPosition(decoder, buffer(
+ "$$O160,863835028611502,AAA,35,7.887840,98.375193,200202020238,A,12,4,0,279,0.6,45,32121,442492,520|3|12DF|015273E2,0000,0000|0000|0000|018D|04F0,00000001,,1,0000*F3"));
+
verifyNull(decoder, binary(
"242441313038362c3836343530373033313231393937342c4430302c3138303232343037323631345f4331453130395f4e31553144312e6a70672c31342c302cffd8ffdb008400140e0f120f0d14121012171514181e32211e1c1c1e3d2c2e243249404c4b47404645505a736250556d5645466488656d777b8182814e608d978c7d96737e817c011517171e1a1e3b21213b7c5346537c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7cffc000110801e0028003012100021101031101ffdd0004000affc401a20000010501010101010100000000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa0100030101010101010101010000000000000102030405060708090a0b1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffda000c03010002110311003f00cca69ac8d06e3348569884db4845021b498a60371494008692980119a8ca7a5342101a5cd5221a0ab312ed1ee68b943e80dce2a467ffd0c806a48e592270f13b230e841a0096eeea7bb09e6c85b667033552800069c2980e14f15422418a916ad099228a95455089505584140993a2d5fb598a7cae72bd8fa536ae892e8e69e2b9d971168a459fffd1ece8a0028a006b534f4a68ce5b9130a89ab444919a61a6c634d34d21894952310d25002514084a4a00ffd2d2349564086929082929805250025140094940c4a4a04251400949408292819fffd3cca31591a098a5c62801a45464531098a69a6210d371400629a6980628eb400c64cd3791c1aa16c491479393563343105424fcc4d007ffd4c3463c0a94500381a5e3b8cd000c99e57f2a8f3835402834e0d4d08914d4aa6ac4c954d4aad4c4c955aa647a6496236ab51b552132e412e383d3f955b0722b09ab32a0c5a2a0d0ffd5ece8a0028a0061a6d5193dc8daa36ab422334c34c634"));
diff --git a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
index c0ce67cb6..5898b74a8 100644
--- a/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Minifinder2ProtocolDecoderTest.java
@@ -13,6 +13,9 @@ public class Minifinder2ProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"ab10150076f1320003100133353534363530373130323933303602105a"));
+ verifyNotNull(decoder, binary(
+ "ab1024009b3f9742011001383635323039303336333430303235113154cfc95d0a00000080d0c95d0a000000"));
+
verifyPosition(decoder, binary(
"ab103f007e2533000110013335353436353037313032393330360930e09d245d210100000924b49e245d01025b201620e6c03b1ef367420400000000aa026d00c90e0000100110"));
diff --git a/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java
index e2f72c161..d6b8e9a4a 100644
--- a/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/OkoProtocolDecoderTest.java
@@ -11,6 +11,12 @@ public class OkoProtocolDecoderTest extends ProtocolTest {
OkoProtocolDecoder decoder = new OkoProtocolDecoder(null);
verifyPosition(decoder, text(
+ "{868204000482330,125138,A,5026.821,N,03032.472,E,0.0,171,240200,7,00,F9,7D,1,,,,,,,91,,,187.7,M,2,,}"));
+
+ verifyPosition(decoder, text(
+ "{123456789098765,132810.000,A,4926.4243,N,03203.6831,E,0.08,83.52,131010,07,5C,FB,7A,1,27,,,,,,CB,128,15grn,197.6,M,3,01FE,02AC}"));
+
+ verifyPosition(decoder, text(
"{861694033681089,045403.00,A,4924.14181,N,03207.43787,E,0.080,,151117,07,0.00,01,24.8,1,02,5n4}"));
verifyPosition(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java b/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java
index 06c6e24eb..ae21de107 100644
--- a/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/OmnicommFrameDecoderTest.java
@@ -11,6 +11,10 @@ public class OmnicommFrameDecoderTest extends ProtocolTest {
OmnicommFrameDecoder decoder = new OmnicommFrameDecoder();
verifyFrame(
+ binary("c08600004566"),
+ decoder.decode(null, null, binary("c08600004566")));
+
+ verifyFrame(
binary("c080080061a61915340100001dec"),
decoder.decode(null, null, binary("c080080061a61915340100001dec")));
diff --git a/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java
index 66286776a..fb6dca3e6 100644
--- a/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/RstProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class RstProtocolDecoderTest extends ProtocolTest {
RstProtocolDecoder decoder = new RstProtocolDecoder(null);
+ verifyPosition(decoder, text(
+ "RST;L;RST-MINIv2;V7.02;008068078;61;1;27-01-2020 21:36:33;27-01-2020 21:36:33;-16.696159;-49.284275;0;67;786;1;15;0;00;B0;00;19;06;12.42;4.16;79;20;FE;0000;01;E0;00800020;0;467;FIM;"));
+
verifyAttribute(decoder, text(
"RST;A;RST-MINIv2;V7.00;008033985;1;7;30-08-2019 11:31:38;30-08-2019 11:31:15;-23.645868;-46.637741;0;226;828;0;10;0;00;20;00;1A;02;0.02;3.40;0;0;FE;0000;04;80;11;0;FIM;"),
Position.KEY_BATTERY, 3.40);
diff --git a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
index 4ab343876..0ee34a4fc 100644
--- a/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SigfoxProtocolDecoderTest.java
@@ -12,6 +12,15 @@ public class SigfoxProtocolDecoderTest extends ProtocolTest {
SigfoxProtocolDecoder decoder = new SigfoxProtocolDecoder(null);
+ verifyAttributes(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"messageType\":\"accelerometer\",\"deviceId\":\"testdev001\",\"snr\":\"1234\",\"rssi\":\"-120.00\",\"station\":\"5678\",\"seqNum\":\"9123\",\"newPosition\":false,\"latitude\":\"null\",\"longitude\":\"null\",\"positionTime\":\"null\",\"moving\":true,\"magChange\":\"true\",\"magStatus\":\"true\",\"temperature\":\"7.5\",\"battery\":\"null\",\"batteryPercentage\":\"null\",\"lastSeen\":\"1582560425\",\"fwVersion\":\"null\",\"dlConfig\":\"null\",\"recievedPayload\":\"cb020051\"}")));
+
+ verifyAttributes(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"messageType\":\"downlinkAcknowledgement\",\"deviceId\":\"testdev002\",\"snr\":\"1234\",\"rssi\":\"-120.00\",\"station\":\"5678\",\"seqNum\":\"9123\",\"newPosition\":false,\"latitude\":\"null\",\"longitude\":\"null\",\"positionTime\":\"null\",\"moving\":false,\"magChange\":\"true\",\"magStatus\":\"true\",\"temperature\":\"8.5\",\"battery\":\"3.6\",\"batteryPercentage\":\"100\",\"lastSeen\":\"1582560425\",\"fwVersion\":\"1.15\",\"dlConfig\":\"808c180202140216\",\"recievedPayload\":\"cf808c180202140216b4010f\"}")));
+
+ verifyPosition(decoder, request(HttpMethod.POST, "/",
+ buffer("{\"deviceId\":\"3377BC\",\"snr\":\"16.46\",\"rssi\":\"-123.00\",\"station\":\"-123.00\",\"seqNum\":\"3042\",\"newPosition\":true,\"latitude\":51.9189749,\"longitude\":-8.3979322,\"positionTime\":\"1582801850\",\"moving\":false,\"magChange\":false,\"magStatus\":false,\"temperature\":-2,\"battery\":\"null\",\"batteryPercentage\":\"null\",\"lastSeen\":\"1582801850\",\"fwVersion\":\"null\",\"dlConfig\":\"null\",\"recievedPayload\":\"09495a9085f5c94c\"}")));
+
verifyPosition(decoder, request(HttpMethod.POST, "/",
buffer("{ \"device\" : \"33827B\", \"data\" : \"1f03198e63807f08836402ff\", \"time\" : \"1574346702\", \"snr\" : \"8.82\", \"station\" : \"140A\", \"avgSnr\" : \"11.28\", \"lat\" : \"52.0\", \"lng\" : \"-8.0\", \"rssi\" : \"-141.00\", \"seqNumber\" : \"3662\"}")));
diff --git a/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java
index b040d4ecf..4e08ce7a5 100644
--- a/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SolarPoweredProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class SolarPoweredProtocolDecoderTest extends ProtocolTest {
@@ -10,6 +11,10 @@ public class SolarPoweredProtocolDecoderTest extends ProtocolTest {
SolarPoweredProtocolDecoder decoder = new SolarPoweredProtocolDecoder(null);
+ verifyAttribute(decoder, binary(
+ "7e850256553309440011003e81131914030600332301a61ed709209ff40014b89082020f0283100000f908000000440000003d1f19021784114161726f6e34475630312d323030333031057e"),
+ Position.KEY_RSSI, 23);
+
verifyPosition(decoder, binary(
"7e850256553304728011003e811319130b0b11211f01a2e6be091fa0e10114cc1582020f00831000004e7400000044000000223819020c84114161726f6e34475630312d313931303331127e"));
diff --git a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java
index 97246a665..459dad978 100644
--- a/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/StarLinkProtocolDecoderTest.java
@@ -2,6 +2,7 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
public class StarLinkProtocolDecoderTest extends ProtocolTest {
@@ -10,6 +11,14 @@ public class StarLinkProtocolDecoderTest extends ProtocolTest {
StarLinkProtocolDecoder decoder = new StarLinkProtocolDecoder(null);
+ decoder.setFormat("#IMEI#,#EDT#,#PDT#,#LAT#,#LONG#,#SPD#,#IGN#,#ODO#,#DUR#,#TDUR#,#LAC#,#CID#,#VIN#,#VBAT#,#EID#,#EDSC#,#DRV#,#SATU#,#CSS#,#OUT1#,#OUT2#");
+
+ verifyAttribute(decoder, text(
+ "$SLU862549048472545,06,25,862549048472545,200304085936,200304085937,+4126.1828,+00209.8472,013.9,0,000000,,1,2120,6306,14.452,03.980,33,External Device,0,9,67,0,0,7,0,137,13,2,5625,-11,-20,99*1F"),
+ Position.KEY_IGNITION, false);
+
+ decoder.setFormat("#EDT#,#EID#,#PDT#,#LAT#,#LONG#,#SPD#,#HEAD#,#ODO#,#IN1#,#IN2#,#IN3#,#IN4#,#OUT1#,#OUT2#,#OUT3#,#OUT4#,#LAC#,#CID#,#VIN#,#VBAT#,#DEST#,#IGN#,#ENG#");
+
verifyAttributes(decoder, text(
"$SLU068328,06,55,170518122023,16,,,,,,000000,1,1,0,0,0,0,0,0,10443,32722,12.664,03.910,,0,0,,01000001FDB3A9*BF"));
diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
index ad9be942f..692a13131 100644
--- a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
@@ -13,6 +13,11 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
decoder.setHbm(true);
decoder.setIncludeAdc(true);
+
+ verifyAttribute(decoder, buffer(
+ "ST600STT;008594432;20;492;20200212;18:58:30;060bb0e1;334;20;36bb;45;+19.337897;-099.064489;000.398;000.00;12;1;5049883;13.61;100100;2;1198;013762;4.2;1;4.68"),
+ Position.PREFIX_ADC + 1, 4.68);
+
decoder.setIncludeTemp(true);
verifyAttribute(decoder, buffer(
@@ -62,6 +67,10 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(null);
+ verifyAttribute(decoder, binary(
+ "82004d05800000553fffff360100100114020410293902ccccf102dc007b00053c00476fa18469e87f000000000b0100003b00081d00000113f3f8010000049e00000000000000001d00000113f3f801"),
+ Position.KEY_DRIVER_UNIQUE_ID, "1d00000113f3f801");
+
verifyPosition(decoder, buffer(
"ST410STT;007638094;426;01;24153;724;4;-65;365;0;24161;724;4;365;0;0;24162;724;4;365;0;0;24363;724;4;365;0;0;24151;724;4;365;0;0;24991;724;4;365;0;0;24373;724;4;365;0;0;3.98;1;0176;2;016;20200106;19:18:04;-15.571860;-056.062637;000.852;238.28;6;1;201"));
diff --git a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
index 28b3fc5c6..48c535c96 100644
--- a/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/T800xProtocolDecoderTest.java
@@ -12,6 +12,9 @@ public class T800xProtocolDecoderTest extends ProtocolTest {
T800xProtocolDecoder decoder = new T800xProtocolDecoder(null);
verifyPosition(decoder, binary(
+ "262602005308090865284040309670000f000f0f0000005a47c000050100000020000000008bfd0020022505185300004041dcc9d6c243b3c6410000012712400000000009e2ffffffffffffffffffffffff09"));
+
+ verifyPosition(decoder, binary(
"2727040049001b0866425039645728c916190604005240000000007739d2c25b681f420000000080000081000020174105000005458216001e000000f01e00001e30d0000000000000"));
verifyAttribute(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java
index b743cef96..3e3bafb34 100644
--- a/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/TelicProtocolDecoderTest.java
@@ -14,6 +14,9 @@ public class TelicProtocolDecoderTest extends ProtocolTest {
"0026355565071347499|206|01|001002008"));
verifyPosition(decoder, text(
+ "023035467909228696525,280220174140,0,280220174135,-80333933,25796530,3,1,0,5,3,3,-21,319064,26202,0000,00,0,206,0,0407,0,0,18,0,3,0"));
+
+ verifyPosition(decoder, text(
"052028495198,160917073641,0,160917073642,43879,511958,3,24,223,17,,,-3,142379,,0010,00,64,205,0,0499"));
verifyPosition(decoder, text(
@@ -23,7 +26,8 @@ public class TelicProtocolDecoderTest extends ProtocolTest {
"002135556507134749999,010817171138,0,010817171138,004560973,50667173,3,0,0,11,1,1,100,958071,20601,000000,00,4142,0000,0000,0208,10395,0"));
verifyPosition(decoder, text(
- "442045993198,290317131935,0,290317131935,269158,465748,3,26,183,,,,184,85316567,226,01,00,68,218"));
+ "442045993198,290317131935,0,290317131935,269158,465748,3,26,183,,,,184,85316567,226,01,00,68,218"),
+ position("2017-03-29 13:19:35.000", true, 46.57480, 26.91580));
verifyPosition(decoder, text(
"673091036017,290317131801,0,290317131801,262214,450536,3,40,199,8,,,154,19969553,,0011,00,59,240,0,0406"));