aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/traccar/BasePipelineFactory.java18
-rw-r--r--src/main/java/org/traccar/BaseProtocol.java12
-rw-r--r--src/main/java/org/traccar/BaseProtocolPoller.java55
-rw-r--r--src/main/java/org/traccar/MainModule.java5
-rw-r--r--src/main/java/org/traccar/Protocol.java2
-rw-r--r--src/main/java/org/traccar/ServerManager.java19
-rw-r--r--src/main/java/org/traccar/TrackerClient.java125
-rw-r--r--src/main/java/org/traccar/TrackerConnector.java32
-rw-r--r--src/main/java/org/traccar/TrackerServer.java57
-rw-r--r--src/main/java/org/traccar/api/resource/PositionResource.java3
-rw-r--r--src/main/java/org/traccar/api/resource/ReportResource.java12
-rw-r--r--src/main/java/org/traccar/config/Keys.java38
-rw-r--r--src/main/java/org/traccar/database/PermissionsManager.java16
-rw-r--r--src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java81
-rw-r--r--src/main/java/org/traccar/handler/FilterHandler.java2
-rw-r--r--src/main/java/org/traccar/handler/OpenChannelHandler.java14
-rw-r--r--src/main/java/org/traccar/model/Calendar.java2
-rw-r--r--src/main/java/org/traccar/model/Server.java12
-rw-r--r--src/main/java/org/traccar/model/User.java10
-rw-r--r--src/main/java/org/traccar/protocol/ArmoliProtocol.java40
-rw-r--r--src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java151
-rw-r--r--src/main/java/org/traccar/protocol/ArmoliProtocolPoller.java35
-rw-r--r--src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java13
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java20
-rw-r--r--src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java62
-rw-r--r--src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java21
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java9
-rw-r--r--src/main/java/org/traccar/protocol/H02ProtocolDecoder.java34
-rw-r--r--src/main/java/org/traccar/protocol/H02ProtocolEncoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java17
-rw-r--r--src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java25
-rw-r--r--src/main/java/org/traccar/protocol/MiniFinderProtocol.java4
-rw-r--r--src/main/java/org/traccar/protocol/OrbcommProtocol.java40
-rw-r--r--src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java121
-rw-r--r--src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java74
-rw-r--r--src/main/java/org/traccar/protocol/S168ProtocolDecoder.java5
-rw-r--r--src/main/java/org/traccar/protocol/StartekProtocolDecoder.java30
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java97
-rw-r--r--src/main/java/org/traccar/protocol/T800xProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocolEncoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocolEncoder.java2
-rw-r--r--src/test/java/org/traccar/ProtocolTest.java6
-rw-r--r--src/test/java/org/traccar/geocoder/GeocoderTest.java10
-rw-r--r--src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java36
-rw-r--r--src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java5
-rw-r--r--src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java6
-rw-r--r--src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java13
-rw-r--r--src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java4
-rw-r--r--src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java24
-rw-r--r--src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java3
-rw-r--r--src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java13
57 files changed, 1333 insertions, 125 deletions
diff --git a/src/main/java/org/traccar/BasePipelineFactory.java b/src/main/java/org/traccar/BasePipelineFactory.java
index c9f3a2346..89ef76a80 100644
--- a/src/main/java/org/traccar/BasePipelineFactory.java
+++ b/src/main/java/org/traccar/BasePipelineFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 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.
@@ -54,12 +54,12 @@ import java.util.Map;
public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
- private final TrackerServer server;
+ private final TrackerConnector connector;
private final String protocol;
private int timeout;
- public BasePipelineFactory(TrackerServer server, String protocol) {
- this.server = server;
+ public BasePipelineFactory(TrackerConnector connector, String protocol) {
+ this.connector = connector;
this.protocol = protocol;
timeout = Context.getConfig().getInteger(Keys.PROTOCOL_TIMEOUT.withPrefix(protocol));
if (timeout == 0) {
@@ -67,10 +67,12 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
}
}
+ protected abstract void addTransportHandlers(PipelineBuilder pipeline);
+
protected abstract void addProtocolHandlers(PipelineBuilder pipeline);
@SafeVarargs
- private final void addHandlers(ChannelPipeline pipeline, Class<? extends ChannelHandler>... handlerClasses) {
+ private void addHandlers(ChannelPipeline pipeline, Class<? extends ChannelHandler>... handlerClasses) {
for (Class<? extends ChannelHandler> handlerClass : handlerClasses) {
if (handlerClass != null) {
pipeline.addLast(Main.getInjector().getInstance(handlerClass));
@@ -97,10 +99,12 @@ public abstract class BasePipelineFactory extends ChannelInitializer<Channel> {
protected void initChannel(Channel channel) {
final ChannelPipeline pipeline = channel.pipeline();
- if (timeout > 0 && !server.isDatagram()) {
+ addTransportHandlers(pipeline::addLast);
+
+ if (timeout > 0 && !connector.isDatagram()) {
pipeline.addLast(new IdleStateHandler(timeout, 0, 0));
}
- pipeline.addLast(new OpenChannelHandler(server));
+ pipeline.addLast(new OpenChannelHandler(connector));
pipeline.addLast(new NetworkMessageHandler());
pipeline.addLast(new StandardLoggingHandler(protocol));
diff --git a/src/main/java/org/traccar/BaseProtocol.java b/src/main/java/org/traccar/BaseProtocol.java
index bd3391822..52d34dc44 100644
--- a/src/main/java/org/traccar/BaseProtocol.java
+++ b/src/main/java/org/traccar/BaseProtocol.java
@@ -35,7 +35,7 @@ public abstract class BaseProtocol implements Protocol {
private final String name;
private final Set<String> supportedDataCommands = new HashSet<>();
private final Set<String> supportedTextCommands = new HashSet<>();
- private final List<TrackerServer> serverList = new LinkedList<>();
+ private final List<TrackerConnector> connectorList = new LinkedList<>();
private StringProtocolEncoder textCommandEncoder = null;
@@ -54,12 +54,16 @@ public abstract class BaseProtocol implements Protocol {
}
protected void addServer(TrackerServer server) {
- serverList.add(server);
+ connectorList.add(server);
+ }
+
+ protected void addClient(TrackerClient client) {
+ connectorList.add(client);
}
@Override
- public Collection<TrackerServer> getServerList() {
- return serverList;
+ public Collection<TrackerConnector> getConnectorList() {
+ return connectorList;
}
public void setSupportedDataCommands(String... commands) {
diff --git a/src/main/java/org/traccar/BaseProtocolPoller.java b/src/main/java/org/traccar/BaseProtocolPoller.java
new file mode 100644
index 000000000..be6556374
--- /dev/null
+++ b/src/main/java/org/traccar/BaseProtocolPoller.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 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;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.util.concurrent.Future;
+
+import java.net.SocketAddress;
+import java.util.concurrent.TimeUnit;
+
+public abstract class BaseProtocolPoller extends ChannelDuplexHandler {
+
+ private final long interval;
+ private Future<?> timeout;
+
+ public BaseProtocolPoller(long interval) {
+ this.interval = interval;
+ }
+
+ protected abstract void sendRequest(Channel channel, SocketAddress remoteAddress);
+
+ @Override
+ public void channelActive(ChannelHandlerContext ctx) throws Exception {
+ super.channelActive(ctx);
+ if (interval > 0) {
+ timeout = ctx.executor().scheduleAtFixedRate(
+ () -> sendRequest(ctx.channel(), ctx.channel().remoteAddress()), 0, interval, TimeUnit.SECONDS);
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ super.channelInactive(ctx);
+ if (timeout != null) {
+ timeout.cancel(false);
+ timeout = null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java
index 11100f66e..aaa82adfc 100644
--- a/src/main/java/org/traccar/MainModule.java
+++ b/src/main/java/org/traccar/MainModule.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2022 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 org.traccar.geocoder.AddressFormat;
import org.traccar.geocoder.BanGeocoder;
import org.traccar.geocoder.BingMapsGeocoder;
import org.traccar.geocoder.FactualGeocoder;
+import org.traccar.geocoder.GeoapifyGeocoder;
import org.traccar.geocoder.GeocodeFarmGeocoder;
import org.traccar.geocoder.GeocodeXyzGeocoder;
import org.traccar.geocoder.Geocoder;
@@ -197,6 +198,8 @@ public class MainModule extends AbstractModule {
return new MapboxGeocoder(key, cacheSize, addressFormat);
case "maptiler":
return new MapTilerGeocoder(key, cacheSize, addressFormat);
+ case "geoapify":
+ return new GeoapifyGeocoder(key, language, cacheSize, addressFormat);
default:
return new GoogleGeocoder(key, language, cacheSize, addressFormat);
}
diff --git a/src/main/java/org/traccar/Protocol.java b/src/main/java/org/traccar/Protocol.java
index aea69b353..bc9c99557 100644
--- a/src/main/java/org/traccar/Protocol.java
+++ b/src/main/java/org/traccar/Protocol.java
@@ -25,7 +25,7 @@ public interface Protocol {
String getName();
- Collection<TrackerServer> getServerList();
+ Collection<TrackerConnector> getConnectorList();
Collection<String> getSupportedDataCommands();
diff --git a/src/main/java/org/traccar/ServerManager.java b/src/main/java/org/traccar/ServerManager.java
index 935a821aa..0db786bdb 100644
--- a/src/main/java/org/traccar/ServerManager.java
+++ b/src/main/java/org/traccar/ServerManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import org.traccar.config.Keys;
import java.io.File;
import java.io.IOException;
import java.net.BindException;
+import java.net.ConnectException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
@@ -39,7 +40,7 @@ public class ServerManager {
private static final Logger LOGGER = LoggerFactory.getLogger(ServerManager.class);
- private final List<TrackerServer> serverList = new LinkedList<>();
+ private final List<TrackerConnector> connectorList = new LinkedList<>();
private final Map<String, BaseProtocol> protocolList = new ConcurrentHashMap<>();
private void loadPackage(String packageName) throws IOException, URISyntaxException, ReflectiveOperationException {
@@ -75,7 +76,7 @@ public class ServerManager {
if (BaseProtocol.class.isAssignableFrom(protocolClass) && Context.getConfig().hasKey(
Keys.PROTOCOL_PORT.withPrefix(BaseProtocol.nameFromClass(protocolClass)))) {
BaseProtocol protocol = (BaseProtocol) protocolClass.getDeclaredConstructor().newInstance();
- serverList.addAll(protocol.getServerList());
+ connectorList.addAll(protocol.getConnectorList());
protocolList.put(protocol.getName(), protocol);
}
}
@@ -90,18 +91,20 @@ public class ServerManager {
}
public void start() throws Exception {
- for (TrackerServer server: serverList) {
+ for (TrackerConnector connector: connectorList) {
try {
- server.start();
+ connector.start();
} catch (BindException e) {
- LOGGER.warn("Port {} is disabled due to conflict", server.getPort());
+ LOGGER.warn("Port disabled due to conflict", e);
+ } catch (ConnectException e) {
+ LOGGER.warn("Connection failed", e);
}
}
}
public void stop() {
- for (TrackerServer server: serverList) {
- server.stop();
+ for (TrackerConnector connector: connectorList) {
+ connector.stop();
}
GlobalTimer.release();
}
diff --git a/src/main/java/org/traccar/TrackerClient.java b/src/main/java/org/traccar/TrackerClient.java
new file mode 100644
index 000000000..dda02f909
--- /dev/null
+++ b/src/main/java/org/traccar/TrackerClient.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2022 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;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.GenericFutureListener;
+import io.netty.util.concurrent.GlobalEventExecutor;
+import org.traccar.config.Keys;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import java.util.concurrent.TimeUnit;
+
+public abstract class TrackerClient implements TrackerConnector {
+
+ private final boolean secure;
+ private final long interval;
+
+ private final Bootstrap bootstrap;
+
+ private final int port;
+ private final String address;
+ private final String[] devices;
+
+ private final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+
+ @Override
+ public boolean isDatagram() {
+ return false;
+ }
+
+ @Override
+ public boolean isSecure() {
+ return secure;
+ }
+
+ public TrackerClient(String protocol) {
+
+ secure = Context.getConfig().getBoolean(Keys.PROTOCOL_SSL.withPrefix(protocol));
+ interval = Context.getConfig().getLong(Keys.PROTOCOL_INTERVAL.withPrefix(protocol));
+ address = Context.getConfig().getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol));
+ port = Context.getConfig().getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol), secure ? 443 : 80);
+ devices = Context.getConfig().getString(Keys.PROTOCOL_DEVICES.withPrefix(protocol)).split("[, ]");
+
+ BasePipelineFactory pipelineFactory = new BasePipelineFactory(this, protocol) {
+ @Override
+ protected void addTransportHandlers(PipelineBuilder pipeline) {
+ try {
+ if (isSecure()) {
+ SSLEngine engine = SSLContext.getDefault().createSSLEngine();
+ engine.setUseClientMode(true);
+ pipeline.addLast(new SslHandler(engine));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ try {
+ TrackerClient.this.addProtocolHandlers(pipeline);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ };
+
+ bootstrap = new Bootstrap()
+ .group(EventLoopGroupFactory.getWorkerGroup())
+ .channel(NioSocketChannel.class)
+ .handler(pipelineFactory);
+ }
+
+ protected abstract void addProtocolHandlers(PipelineBuilder pipeline) throws Exception;
+
+ public String[] getDevices() {
+ return devices;
+ }
+
+ @Override
+ public ChannelGroup getChannelGroup() {
+ return channelGroup;
+ }
+
+ @Override
+ public void start() throws Exception {
+ bootstrap.connect(address, port)
+ .syncUninterruptibly().channel().closeFuture().addListener(new GenericFutureListener<>() {
+ @Override
+ public void operationComplete(Future<? super Void> future) {
+ if (interval > 0) {
+ GlobalEventExecutor.INSTANCE.schedule(() -> {
+ bootstrap.connect(address, port)
+ .syncUninterruptibly().channel().closeFuture().addListener(this);
+ }, interval, TimeUnit.SECONDS);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void stop() {
+ channelGroup.close().awaitUninterruptibly();
+ }
+
+}
diff --git a/src/main/java/org/traccar/TrackerConnector.java b/src/main/java/org/traccar/TrackerConnector.java
new file mode 100644
index 000000000..9e2d27ae5
--- /dev/null
+++ b/src/main/java/org/traccar/TrackerConnector.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021 - 2022 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;
+
+import io.netty.channel.group.ChannelGroup;
+
+public interface TrackerConnector {
+
+ boolean isDatagram();
+
+ boolean isSecure();
+
+ ChannelGroup getChannelGroup();
+
+ void start() throws Exception;
+
+ void stop();
+
+}
diff --git a/src/main/java/org/traccar/TrackerServer.java b/src/main/java/org/traccar/TrackerServer.java
index 59ba123e2..8e2fce616 100644
--- a/src/main/java/org/traccar/TrackerServer.java
+++ b/src/main/java/org/traccar/TrackerServer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 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,28 +23,58 @@ import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.traccar.config.Keys;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
import java.net.InetSocketAddress;
-public abstract class TrackerServer {
+public abstract class TrackerServer implements TrackerConnector {
private final boolean datagram;
+ private final boolean secure;
+
+ @SuppressWarnings("rawtypes")
private final AbstractBootstrap bootstrap;
+ private final int port;
+ private final String address;
+
+ private final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+
+ @Override
public boolean isDatagram() {
return datagram;
}
+ @Override
+ public boolean isSecure() {
+ return secure;
+ }
+
public TrackerServer(boolean datagram, String protocol) {
this.datagram = datagram;
+ secure = Context.getConfig().getBoolean(Keys.PROTOCOL_SSL.withPrefix(protocol));
address = Context.getConfig().getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol));
port = Context.getConfig().getInteger(Keys.PROTOCOL_PORT.withPrefix(protocol));
BasePipelineFactory pipelineFactory = new BasePipelineFactory(this, protocol) {
@Override
+ protected void addTransportHandlers(PipelineBuilder pipeline) {
+ try {
+ if (isSecure()) {
+ SSLEngine engine = SSLContext.getDefault().createSSLEngine();
+ pipeline.addLast(new SslHandler(engine));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
TrackerServer.this.addProtocolHandlers(pipeline);
}
@@ -52,14 +82,14 @@ public abstract class TrackerServer {
if (datagram) {
- this.bootstrap = new Bootstrap()
+ bootstrap = new Bootstrap()
.group(EventLoopGroupFactory.getWorkerGroup())
.channel(NioDatagramChannel.class)
.handler(pipelineFactory);
} else {
- this.bootstrap = new ServerBootstrap()
+ bootstrap = new ServerBootstrap()
.group(EventLoopGroupFactory.getBossGroup(), EventLoopGroupFactory.getWorkerGroup())
.channel(NioServerSocketChannel.class)
.childHandler(pipelineFactory);
@@ -69,32 +99,20 @@ public abstract class TrackerServer {
protected abstract void addProtocolHandlers(PipelineBuilder pipeline);
- private int port;
-
public int getPort() {
return port;
}
- public void setPort(int port) {
- this.port = port;
- }
-
- private String address;
-
public String getAddress() {
return address;
}
- public void setAddress(String address) {
- this.address = address;
- }
-
- private final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
-
+ @Override
public ChannelGroup getChannelGroup() {
return channelGroup;
}
+ @Override
public void start() throws Exception {
InetSocketAddress endpoint;
if (address == null) {
@@ -103,12 +121,13 @@ public abstract class TrackerServer {
endpoint = new InetSocketAddress(address, port);
}
- Channel channel = bootstrap.bind(endpoint).sync().channel();
+ Channel channel = bootstrap.bind(endpoint).syncUninterruptibly().channel();
if (channel != null) {
getChannelGroup().add(channel);
}
}
+ @Override
public void stop() {
channelGroup.close().awaitUninterruptibly();
}
diff --git a/src/main/java/org/traccar/api/resource/PositionResource.java b/src/main/java/org/traccar/api/resource/PositionResource.java
index 998d59706..53157197b 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 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 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.
@@ -55,6 +55,7 @@ public class PositionResource extends BaseResource {
} else {
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
if (from != null && to != null) {
+ Context.getPermissionsManager().checkDisableReports(getUserId());
return Context.getDataManager().getPositions(deviceId, from, to);
} else {
return Collections.singleton(Context.getDeviceManager().getLastPosition(deviceId));
diff --git a/src/main/java/org/traccar/api/resource/ReportResource.java b/src/main/java/org/traccar/api/resource/ReportResource.java
index 7347bfd64..23ffaf54c 100644
--- a/src/main/java/org/traccar/api/resource/ReportResource.java
+++ b/src/main/java/org/traccar/api/resource/ReportResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2016 - 2018 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -99,6 +99,7 @@ public class ReportResource extends BaseResource {
public Collection<Position> getRoute(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ Context.getPermissionsManager().checkDisableReports(getUserId());
LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds);
return Route.getObjects(getUserId(), deviceIds, groupIds, from, to);
}
@@ -110,6 +111,7 @@ public class ReportResource extends BaseResource {
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
throws SQLException, IOException {
+ Context.getPermissionsManager().checkDisableReports(getUserId());
return executeReport(getUserId(), mail, stream -> {
LogAction.logReport(getUserId(), "route", from, to, deviceIds, groupIds);
Route.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
@@ -122,6 +124,7 @@ public class ReportResource extends BaseResource {
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("type") final List<String> types,
@QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ Context.getPermissionsManager().checkDisableReports(getUserId());
LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds);
return Events.getObjects(getUserId(), deviceIds, groupIds, types, from, to);
}
@@ -134,6 +137,7 @@ public class ReportResource extends BaseResource {
@QueryParam("type") final List<String> types,
@QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
throws SQLException, IOException {
+ Context.getPermissionsManager().checkDisableReports(getUserId());
return executeReport(getUserId(), mail, stream -> {
LogAction.logReport(getUserId(), "events", from, to, deviceIds, groupIds);
Events.getExcel(stream, getUserId(), deviceIds, groupIds, types, from, to);
@@ -146,6 +150,7 @@ public class ReportResource extends BaseResource {
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily)
throws SQLException {
+ Context.getPermissionsManager().checkDisableReports(getUserId());
LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds);
return Summary.getObjects(getUserId(), deviceIds, groupIds, from, to, daily);
}
@@ -158,6 +163,7 @@ public class ReportResource extends BaseResource {
@QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("daily") boolean daily,
@QueryParam("mail") boolean mail)
throws SQLException, IOException {
+ Context.getPermissionsManager().checkDisableReports(getUserId());
return executeReport(getUserId(), mail, stream -> {
LogAction.logReport(getUserId(), "summary", from, to, deviceIds, groupIds);
Summary.getExcel(stream, getUserId(), deviceIds, groupIds, from, to, daily);
@@ -170,6 +176,7 @@ public class ReportResource extends BaseResource {
public Collection<TripReport> getTrips(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ Context.getPermissionsManager().checkDisableReports(getUserId());
LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds);
return Trips.getObjects(getUserId(), deviceIds, groupIds, from, to);
}
@@ -181,6 +188,7 @@ public class ReportResource extends BaseResource {
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
throws SQLException, IOException {
+ Context.getPermissionsManager().checkDisableReports(getUserId());
return executeReport(getUserId(), mail, stream -> {
LogAction.logReport(getUserId(), "trips", from, to, deviceIds, groupIds);
Trips.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
@@ -193,6 +201,7 @@ public class ReportResource extends BaseResource {
public Collection<StopReport> getStops(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("from") Date from, @QueryParam("to") Date to) throws SQLException {
+ Context.getPermissionsManager().checkDisableReports(getUserId());
LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds);
return Stops.getObjects(getUserId(), deviceIds, groupIds, from, to);
}
@@ -204,6 +213,7 @@ public class ReportResource extends BaseResource {
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("from") Date from, @QueryParam("to") Date to, @QueryParam("mail") boolean mail)
throws SQLException, IOException {
+ Context.getPermissionsManager().checkDisableReports(getUserId());
return executeReport(getUserId(), mail, stream -> {
LogAction.logReport(getUserId(), "stops", from, to, deviceIds, groupIds);
Stops.getExcel(stream, getUserId(), deviceIds, groupIds, from, to);
diff --git a/src/main/java/org/traccar/config/Keys.java b/src/main/java/org/traccar/config/Keys.java
index e8e0ff207..cb3bd4de8 100644
--- a/src/main/java/org/traccar/config/Keys.java
+++ b/src/main/java/org/traccar/config/Keys.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,6 +38,28 @@ public final class Keys {
Collections.singletonList(KeyType.GLOBAL));
/**
+ * List of devices for polling protocols. List should contain unique ids separated by commas. Used only for polling
+ * protocols.
+ */
+ public static final ConfigSuffix<String> PROTOCOL_DEVICES = new ConfigSuffix<>(
+ ".devices",
+ Collections.singletonList(KeyType.GLOBAL));
+
+ /**
+ * Polling interval in seconds. Used only for polling protocols.
+ */
+ public static final ConfigSuffix<Long> PROTOCOL_INTERVAL = new ConfigSuffix<>(
+ ".interval",
+ Collections.singletonList(KeyType.GLOBAL));
+
+ /**
+ * Enable SSL support for the protocol. Not all protocols support this.
+ */
+ public static final ConfigSuffix<Boolean> PROTOCOL_SSL = new ConfigSuffix<>(
+ ".ssl",
+ Collections.singletonList(KeyType.GLOBAL));
+
+ /**
* Connection timeout value in seconds. Because sometimes there is no way to detect lost TCP connection old
* connections stay in open state. On most systems there is a limit on number of open connection, so this leads to
* problems with establishing new connections when number of devices is high or devices data connections are
@@ -176,6 +198,20 @@ public final class Keys {
Collections.singletonList(KeyType.GLOBAL));
/**
+ * ORBCOMM API access id.
+ */
+ public static final ConfigKey<String> ORBCOMM_ACCESS_ID = new ConfigKey<>(
+ "orbcomm.accessId",
+ Collections.singletonList(KeyType.GLOBAL));
+
+ /**
+ * ORBCOMM API password.
+ */
+ public static final ConfigKey<String> ORBCOMM_PASSWORD = new ConfigKey<>(
+ "orbcomm.password",
+ Collections.singletonList(KeyType.GLOBAL));
+
+ /**
* Skip device connection session cache. Global configuration.
*/
public static final ConfigKey<Boolean> DECODER_IGNORE_SESSIONS_CACHE = new ConfigKey<>(
diff --git a/src/main/java/org/traccar/database/PermissionsManager.java b/src/main/java/org/traccar/database/PermissionsManager.java
index 32464cf90..ab841a521 100644
--- a/src/main/java/org/traccar/database/PermissionsManager.java
+++ b/src/main/java/org/traccar/database/PermissionsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 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.
@@ -274,6 +274,11 @@ public class PermissionsManager {
return user != null && user.getLimitCommands();
}
+ public boolean getUserDisableReport(long userId) {
+ User user = getUser(userId);
+ return user != null && user.getDisableReports();
+ }
+
public void checkReadonly(long userId) throws SecurityException {
if (!getUserAdmin(userId) && (server.getReadonly() || getUserReadonly(userId))) {
throw new SecurityException("Account is readonly");
@@ -292,6 +297,12 @@ public class PermissionsManager {
}
}
+ public void checkDisableReports(long userId) throws SecurityException {
+ if (!getUserAdmin(userId) && (server.getDisableReports() || getUserDisableReport(userId))) {
+ throw new SecurityException("Account has reports disabled");
+ }
+ }
+
public void checkUserDeviceCommand(long userId, long deviceId, long commandId) throws SecurityException {
if (!getUserAdmin(userId) && Context.getCommandsManager().checkDeviceCommand(deviceId, commandId)) {
throw new SecurityException("Command can not be sent to this device");
@@ -326,7 +337,8 @@ public class PermissionsManager {
if (before.getReadonly() != after.getReadonly()
|| before.getDeviceReadonly() != after.getDeviceReadonly()
|| before.getDisabled() != after.getDisabled()
- || before.getLimitCommands() != after.getLimitCommands()) {
+ || before.getLimitCommands() != after.getLimitCommands()
+ || before.getDisableReports() != after.getDisableReports()) {
if (userId == after.getId()) {
checkAdmin(userId);
}
diff --git a/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java b/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java
new file mode 100644
index 000000000..ef0e4c8bd
--- /dev/null
+++ b/src/main/java/org/traccar/geocoder/GeoapifyGeocoder.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.geocoder;
+
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+
+public class GeoapifyGeocoder extends JsonGeocoder {
+
+ private static String formatUrl(String key, String language) {
+ String url = "https://api.geoapify.com/v1/geocode/reverse?format=json&lat=%f&lon=%f";
+ if (key != null) {
+ url += "&apiKey=" + key;
+ }
+ if (language != null) {
+ url += "&lang=" + language;
+ }
+ return url;
+ }
+
+ public GeoapifyGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(formatUrl(key, language), cacheSize, addressFormat);
+ }
+
+ @Override
+ public Address parseAddress(JsonObject json) {
+ JsonArray results = json.getJsonArray("results");
+ if (results.size() > 0) {
+ JsonObject result = results.getJsonObject(0);
+
+ Address address = new Address();
+
+ if (json.containsKey("formatted")) {
+ address.setFormattedAddress(json.getString("formatted"));
+ }
+
+ if (result.containsKey("housenumber")) {
+ address.setHouse(result.getString("housenumber"));
+ }
+ if (result.containsKey("street")) {
+ address.setStreet(result.getString("street"));
+ }
+ if (result.containsKey("suburb")) {
+ address.setSuburb(result.getString("suburb"));
+ }
+ if (result.containsKey("city")) {
+ address.setSettlement(result.getString("city"));
+ }
+ if (result.containsKey("district")) {
+ address.setDistrict(result.getString("district"));
+ }
+ if (result.containsKey("state")) {
+ address.setState(result.getString("state"));
+ }
+ if (result.containsKey("country_code")) {
+ address.setCountry(result.getString("country_code").toUpperCase());
+ }
+ if (result.containsKey("postcode")) {
+ address.setPostcode(result.getString("postcode"));
+ }
+
+ return address;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/handler/FilterHandler.java b/src/main/java/org/traccar/handler/FilterHandler.java
index 049512765..2b850c755 100644
--- a/src/main/java/org/traccar/handler/FilterHandler.java
+++ b/src/main/java/org/traccar/handler/FilterHandler.java
@@ -181,7 +181,7 @@ public class FilterHandler extends BaseDataHandler {
} else {
preceding = getLastReceivedPosition(deviceId);
}
- if (filterDuplicate(position, preceding)) {
+ if (filterDuplicate(position, preceding) && !skipLimit(position, preceding) && !skipAttributes(position)) {
filterType.append("Duplicate ");
}
if (filterStatic(position) && !skipLimit(position, preceding) && !skipAttributes(position)) {
diff --git a/src/main/java/org/traccar/handler/OpenChannelHandler.java b/src/main/java/org/traccar/handler/OpenChannelHandler.java
index d09d617ab..e416f35ae 100644
--- a/src/main/java/org/traccar/handler/OpenChannelHandler.java
+++ b/src/main/java/org/traccar/handler/OpenChannelHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,26 +17,26 @@ package org.traccar.handler;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
-import org.traccar.TrackerServer;
+import org.traccar.TrackerConnector;
public class OpenChannelHandler extends ChannelDuplexHandler {
- private final TrackerServer server;
+ private final TrackerConnector connector;
- public OpenChannelHandler(TrackerServer server) {
- this.server = server;
+ public OpenChannelHandler(TrackerConnector connector) {
+ this.connector = connector;
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
- server.getChannelGroup().add(ctx.channel());
+ connector.getChannelGroup().add(ctx.channel());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
- server.getChannelGroup().remove(ctx.channel());
+ connector.getChannelGroup().remove(ctx.channel());
}
}
diff --git a/src/main/java/org/traccar/model/Calendar.java b/src/main/java/org/traccar/model/Calendar.java
index 1010325b6..0b45ca6c8 100644
--- a/src/main/java/org/traccar/model/Calendar.java
+++ b/src/main/java/org/traccar/model/Calendar.java
@@ -20,7 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.data.ParserException;
import net.fortuna.ical4j.filter.Filter;
-import net.fortuna.ical4j.filter.PeriodRule;
+import net.fortuna.ical4j.filter.predicate.PeriodRule;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Period;
import net.fortuna.ical4j.model.component.CalendarComponent;
diff --git a/src/main/java/org/traccar/model/Server.java b/src/main/java/org/traccar/model/Server.java
index 7bdb53b22..03d087cac 100644
--- a/src/main/java/org/traccar/model/Server.java
+++ b/src/main/java/org/traccar/model/Server.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 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.
@@ -152,6 +152,16 @@ public class Server extends ExtendedModel {
this.limitCommands = limitCommands;
}
+ private boolean disableReports;
+
+ public boolean getDisableReports() {
+ return disableReports;
+ }
+
+ public void setDisableReports(boolean disableReports) {
+ this.disableReports = disableReports;
+ }
+
private String poiLayer;
public String getPoiLayer() {
diff --git a/src/main/java/org/traccar/model/User.java b/src/main/java/org/traccar/model/User.java
index 976b6aac0..359bdc2c2 100644
--- a/src/main/java/org/traccar/model/User.java
+++ b/src/main/java/org/traccar/model/User.java
@@ -224,6 +224,16 @@ public class User extends ExtendedModel {
private String poiLayer;
+ private boolean disableReports;
+
+ public boolean getDisableReports() {
+ return disableReports;
+ }
+
+ public void setDisableReports(boolean disableReports) {
+ this.disableReports = disableReports;
+ }
+
public String getPoiLayer() {
return poiLayer;
}
diff --git a/src/main/java/org/traccar/protocol/ArmoliProtocol.java b/src/main/java/org/traccar/protocol/ArmoliProtocol.java
new file mode 100644
index 000000000..5f36012af
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArmoliProtocol.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class ArmoliProtocol extends BaseProtocol {
+
+ public ArmoliProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ";;", ";\r", ";"));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new ArmoliProtocolDecoder(ArmoliProtocol.this));
+ pipeline.addLast(new ArmoliProtocolPoller(ArmoliProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java
new file mode 100644
index 000000000..50af039d6
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArmoliProtocolDecoder.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.ObdDecoder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.regex.Pattern;
+
+public class ArmoliProtocolDecoder extends BaseProtocolDecoder {
+
+ public ArmoliProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("[M") // start
+ .number("(d{15})") // imei
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .number("(dd)(dd)(dd)") // time (hhmmss)
+ .number("([NS])(dd.d{6})") // latitude
+ .number("([EW])(ddd.d{6})") // longitude
+ .number("(d)") // valid
+ .number("(x)") // satellites
+ .number("(xx)") // speed
+ .number("(ddd)") // course
+ .number("(xxx)") // adc 1
+ .number("(xxx)") // adc 2
+ .number("(xx)") // status
+ .number("(xx)") // max speed
+ .number("(x{6})") // distance
+ .number("(dd)?") // hdop
+ .number("x{4}") // idle
+ .number(":(x+)").optional() // alarms
+ .number("G(x{6})").optional() // g-sensor
+ .number("H(x{3})").optional() // power
+ .number("E(x{3})").optional() // battery
+ .number("!(x+)").optional() // driver
+ .expression("@A([>0-9A-F]+)").optional() // obd
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ char type = sentence.charAt(1);
+
+ Position position = new Position(getProtocolName());
+ DeviceSession deviceSession;
+
+ if (type != 'M') {
+ if (type == 'W') {
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, null);
+ position.set(
+ Position.KEY_RESULT,
+ sentence.substring(sentence.indexOf(',') + 1, sentence.length() - 1));
+ return position;
+ }
+ } else if (channel != null && (type == 'Q' || type == 'L')) {
+ channel.writeAndFlush(new NetworkMessage("[TX,];;", remoteAddress));
+ }
+ return null;
+ }
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
+ position.setValid(parser.nextInt() > 0);
+
+ position.set(Position.KEY_SATELLITES, parser.nextHexInt());
+
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextHexInt()));
+ position.setCourse(parser.nextInt());
+
+ position.set(Position.PREFIX_ADC + 1, parser.nextHexInt() / 27.0 * 1000);
+ position.set(Position.PREFIX_ADC + 1, parser.nextHexInt() / 27.0 * 1000);
+ position.set(Position.KEY_STATUS, parser.nextHexInt());
+ position.set("maxSpeed", parser.nextHexInt());
+ position.set(Position.KEY_ODOMETER, parser.nextHexInt());
+
+ if (parser.hasNext()) {
+ position.set(Position.KEY_HDOP, parser.nextInt() * 0.1);
+ }
+ if (parser.hasNext()) {
+ position.set("alarms", parser.next());
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_G_SENSOR, parser.next());
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_POWER, parser.nextHexInt() * 0.01);
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_BATTERY, parser.nextHexInt() * 0.01);
+ }
+ if (parser.hasNext()) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+ }
+ if (parser.hasNext()) {
+ String[] values = parser.next().split(">");
+ for (int i = 1; i < values.length; i++) {
+ String value = values[i];
+ position.add(ObdDecoder.decodeData(
+ Integer.parseInt(value.substring(4, 6), 16),
+ Long.parseLong(value.substring(6), 16), true));
+ }
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ArmoliProtocolPoller.java b/src/main/java/org/traccar/protocol/ArmoliProtocolPoller.java
new file mode 100644
index 000000000..f7bb9f593
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArmoliProtocolPoller.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolPoller;
+import org.traccar.Protocol;
+
+import java.net.SocketAddress;
+
+public class ArmoliProtocolPoller extends BaseProtocolPoller {
+
+ public ArmoliProtocolPoller(Protocol protocol) {
+ super(180000);
+ }
+
+ @Override
+ protected void sendRequest(Channel channel, SocketAddress remoteAddress) {
+ channel.writeAndFlush("[TX,];;");
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java
index d5a9df7bc..3ef960f12 100644
--- a/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Dsf22ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 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,6 @@
package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
@@ -46,7 +45,7 @@ public class Dsf22ProtocolDecoder extends BaseProtocolDecoder {
buf.skipBytes(2); // header
- String id = ByteBufUtil.hexDump(buf.readSlice(2));
+ String id = String.valueOf(buf.readUnsignedShortLE());
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
if (deviceSession == null) {
return null;
@@ -61,12 +60,12 @@ public class Dsf22ProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
position.setValid(true);
- position.setLatitude(buf.readInt());
- position.setLongitude(buf.readInt());
- position.setTime(new Date(946684800000L + buf.readUnsignedInt()));
+ position.setLatitude(buf.readIntLE() / 10000000.0);
+ position.setLongitude(buf.readIntLE() / 10000000.0);
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
- position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.001);
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShortLE() * 0.001);
int status = buf.readUnsignedByte();
position.set(Position.KEY_IGNITION, BitUtil.check(status, 0));
diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
index 8fe12fe69..9856ad999 100644
--- a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2022 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.
@@ -218,6 +218,24 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
buf.skipBytes(7); // bss2
}
+ if (BitUtil.check(flags, 7)) {
+ buf.readUnsignedByte(); // radio access technology
+ int count = buf.readUnsignedByte();
+ if (count > 0) {
+ buf.readUnsignedShort(); // mcc
+ buf.readUnsignedShort(); // mnc
+ buf.readUnsignedShort(); // lac
+ buf.readUnsignedShort(); // tac
+ buf.readUnsignedInt(); // cid
+ buf.readUnsignedShort(); // ta
+ }
+ for (int i = 0; i < count; i++) {
+ buf.readUnsignedShort(); // physical cid
+ buf.readUnsignedShort(); // e-arfcn
+ buf.readUnsignedByte(); // rssi
+ }
+ }
+
if (type == MSG_WARNING) {
position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
diff --git a/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java
index d4d539a9e..20ff78c21 100644
--- a/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FlexApiProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 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,8 @@ import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.Protocol;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
import org.traccar.model.Position;
import javax.json.Json;
@@ -52,7 +54,7 @@ public class FlexApiProtocolDecoder extends BaseProtocolDecoder {
JsonObject payload = root.getJsonObject("payload");
- if (topic.contains("gnss")) {
+ if (topic.contains("/gnss/")) {
position.setValid(true);
@@ -72,7 +74,25 @@ public class FlexApiProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_SATELLITES, payload.getInt("gnss.num_sv"));
- } else if (topic.contains("obd")) {
+ } else if (topic.contains("/cellular1/")) {
+
+ getLastLocation(position, new Date(payload.getInt("modem1.ts") * 1000L));
+
+ position.set("imei", payload.getString("modem1.imei"));
+ position.set("imsi", payload.getString("modem1.imsi"));
+ position.set(Position.KEY_ICCID, payload.getString("modem1.iccid"));
+
+ String operator = payload.getString("modem1.operator");
+ if (!operator.isEmpty()) {
+ position.setNetwork(new Network(CellTower.from(
+ Integer.parseInt(operator.substring(0, 3)),
+ Integer.parseInt(operator.substring(3)),
+ Integer.parseInt(payload.getString("modem1.lac"), 16),
+ Integer.parseInt(payload.getString("modem1.cell_id"), 16),
+ payload.getInt("modem1.rssi"))));
+ }
+
+ } else if (topic.contains("/obd/")) {
getLastLocation(position, new Date(payload.getInt("obd.ts") * 1000L));
@@ -89,6 +109,42 @@ public class FlexApiProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_VIN, payload.getString("obd.vin"));
}
+ } else if (topic.contains("/motion/")) {
+
+ getLastLocation(position, new Date(payload.getInt("motion.ts") * 1000L));
+
+ position.set("ax", payload.getJsonNumber("motion.ax").doubleValue());
+ position.set("ay", payload.getJsonNumber("motion.ay").doubleValue());
+ position.set("az", payload.getJsonNumber("motion.az").doubleValue());
+ position.set("gx", payload.getJsonNumber("motion.gx").doubleValue());
+ position.set("gy", payload.getJsonNumber("motion.gy").doubleValue());
+ position.set("gz", payload.getJsonNumber("motion.gz").doubleValue());
+
+ } else if (topic.contains("/io/")) {
+
+ getLastLocation(position, new Date(payload.getInt("io.ts") * 1000L));
+
+ if (payload.containsKey("io.IGN")) {
+ position.set(Position.KEY_IGNITION, payload.getInt("io.IGN") > 0);
+ }
+
+ for (String key : payload.keySet()) {
+ if (key.startsWith("io.AI")) {
+ position.set(Position.PREFIX_ADC + key.substring(5), payload.getJsonNumber(key).doubleValue());
+ } else if (key.startsWith("io.DI") && !key.endsWith("_pullup")) {
+ position.set(Position.PREFIX_IN + key.substring(5), payload.getInt(key) > 0);
+ } else if (key.startsWith("io.DO")) {
+ position.set(Position.PREFIX_OUT + key.substring(5), payload.getInt(key) > 0);
+ }
+ }
+
+ } else if (topic.contains("/sysinfo/")) {
+
+ getLastLocation(position, new Date(payload.getInt("sysinfo.ts") * 1000L));
+
+ position.set("serial", payload.getString("sysinfo.serial_number"));
+ position.set(Position.KEY_VERSION_FW, payload.getString("sysinfo.firmware_version"));
+
} else {
return null;
diff --git a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
index 683ba476e..7ce0c425d 100644
--- a/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -139,7 +139,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(x+)?,") // lac
.number("(x+)?,") // cid
.groupEnd()
- .number("(?:d+|(d+.d))?,") // odometer
+ .number("(?:d+|(d+.d))?,") // rssi / odometer
.compile();
private static final Pattern PATTERN_OBD = new PatternBuilder()
@@ -184,7 +184,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.expression("(?:([0-9A-Z]{17}),)?") // vin
.expression("[^,]*,") // device name
.number("(d+)?,") // power
- .number("d{1,2},").optional() // report type
+ .number("(d{1,2}),").optional() // report type
.number("d{1,2},").optional() // count
.number("d*,").optional() // reserved
.number("(d+),").optional() // battery
@@ -208,11 +208,11 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption
.number("(d+)?,") // fuel level
.or()
- .number("(d{1,7}.d)?,").optional() // odometer
- .number("(d{1,3})?,") // battery
- .or()
.number("(-?d),") // rssi
.number("(d{1,3}),") // battery
+ .or()
+ .number("(d{1,7}.d)?,").optional() // odometer
+ .number("(d{1,3})?,") // battery
.groupEnd()
.any()
.number("(dddd)(dd)(dd)") // date (yyyymmdd)
@@ -835,6 +835,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
String vin = parser.next();
Integer power = parser.nextInt();
+ Integer reportType = parser.nextInt();
Integer battery = parser.nextInt();
Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
@@ -877,12 +878,18 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RPM, parser.nextInt());
position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+ if (parser.hasNext(2)) {
+ if (reportType != null) {
+ position.set(Position.KEY_MOTION, BitUtil.check(reportType, 0));
+ position.set(Position.KEY_CHARGE, BitUtil.check(reportType, 1));
+ }
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
+ }
if (parser.hasNext()) {
position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
}
position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
- position.set(Position.KEY_RSSI, parser.nextInt());
- position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt());
decodeDeviceTime(position, parser);
if (ignoreFixTime) {
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
index 0dcdab892..ec09f371b 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -70,6 +70,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_GPS_LBS_STATUS_3 = 0x27;
public static final int MSG_LBS_MULTIPLE_1 = 0x28;
public static final int MSG_LBS_MULTIPLE_2 = 0x2E;
+ public static final int MSG_LBS_MULTIPLE_3 = 0x24;
public static final int MSG_LBS_WIFI = 0x2C;
public static final int MSG_LBS_EXTEND = 0x18;
public static final int MSG_LBS_STATUS = 0x19;
@@ -699,8 +700,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return null; // space10x multi-lbs message
- } else if (type == MSG_LBS_MULTIPLE_1 || type == MSG_LBS_MULTIPLE_2 || type == MSG_LBS_EXTEND
- || type == MSG_LBS_WIFI || type == MSG_LBS_2 || type == MSG_WIFI_3 || type == MSG_WIFI_5) {
+ } else if (type == MSG_LBS_MULTIPLE_1 || type == MSG_LBS_MULTIPLE_2 || type == MSG_LBS_MULTIPLE_3
+ || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI || type == MSG_LBS_2
+ || type == MSG_WIFI_3 || type == MSG_WIFI_5) {
boolean longFormat = type == MSG_LBS_2 || type == MSG_WIFI_3 || type == MSG_WIFI_5;
@@ -726,7 +728,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // time leads
- if (type != MSG_LBS_MULTIPLE_1 && type != MSG_LBS_MULTIPLE_2 && type != MSG_LBS_2) {
+ if (type != MSG_LBS_MULTIPLE_1 && type != MSG_LBS_MULTIPLE_2 && type != MSG_LBS_MULTIPLE_3
+ && type != MSG_LBS_2) {
int wifiCount = buf.readUnsignedByte();
for (int i = 0; i < wifiCount; i++) {
String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
diff --git a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
index dd7141a2c..10a272bff 100644
--- a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 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.
@@ -176,17 +176,19 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // coding scheme
.groupEnd()
.groupBegin()
- .number("-(d+)-(d+.d+),") // latitude
+ .number("-(d+)-(d+.d+),([NS]),") // latitude
.or()
- .number("(d+)(dd.d+),") // latitude
+ .number("(d+)(dd.d+),([NS]),") // latitude
+ .or()
+ .number("(d+)(dd)(d{4}),([NS]),") // latitude
.groupEnd()
- .expression("([NS]),")
.groupBegin()
- .number("-(d+)-(d+.d+),") // longitude
+ .number("-(d+)-(d+.d+),([EW]),") // longitude
.or()
- .number("(d+)(dd.d+),") // longitude
+ .number("(d+)(dd.d+),([EW]),") // longitude
+ .or()
+ .number("(d+)(dd)(d{4}),([EW]),") // longitude
.groupEnd()
- .expression("([EW]),")
.number(" *(d+.?d*),") // speed
.number("(d+.?d*)?,") // course
.number("(?:d+,)?") // battery
@@ -349,19 +351,25 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.setValid(true);
}
- if (parser.hasNext(2)) {
- position.setLatitude(-parser.nextCoordinate());
+ if (parser.hasNext(3)) {
+ position.setLatitude(parser.nextCoordinate());
}
- if (parser.hasNext(2)) {
+ if (parser.hasNext(3)) {
position.setLatitude(parser.nextCoordinate());
}
+ if (parser.hasNext(4)) {
+ position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN_HEM));
+ }
- if (parser.hasNext(2)) {
- position.setLongitude(-parser.nextCoordinate());
+ if (parser.hasNext(3)) {
+ position.setLongitude(parser.nextCoordinate());
}
- if (parser.hasNext(2)) {
+ if (parser.hasNext(3)) {
position.setLongitude(parser.nextCoordinate());
}
+ if (parser.hasNext(4)) {
+ position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_MIN_MIN_HEM));
+ }
position.setSpeed(parser.nextDouble(0));
position.setCourse(parser.nextDouble(0));
diff --git a/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java b/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java
index 7a765332c..8f1a8c042 100644
--- a/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/H02ProtocolEncoder.java
@@ -6,7 +6,7 @@
* 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
+ * 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,
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
index aa85ea061..0ae08af37 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -34,7 +34,10 @@ import org.traccar.model.Position;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.Calendar;
+import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TimeZone;
@@ -149,7 +152,19 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
ByteBuf buf = (ByteBuf) msg;
if (buf.getByte(buf.readerIndex()) == '(') {
- return decodeResult(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII));
+ String sentence = buf.toString(StandardCharsets.US_ASCII);
+ if (sentence.contains("BASE,2")) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ String response = sentence.replace("TIME", dateFormat.format(new Date()));
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(
+ Unpooled.copiedBuffer(response, StandardCharsets.US_ASCII), remoteAddress));
+ }
+ return null;
+ } else {
+ return decodeResult(channel, remoteAddress, sentence);
+ }
}
buf.readUnsignedByte(); // start marker
diff --git a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
index 652ba3f6a..c72a742b9 100644
--- a/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MictrackProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 Anton Tananaev (anton@traccar.org)
* Copyright 2020 Roeland Boeters (roeland@geodelta.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -114,14 +114,18 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeWifi(Network network, String data) {
+ private void decodeWifi(Network network, String data, boolean hasSsid) {
String[] values = data.split(",");
- for (int i = 0; i < values.length / 2; i++) {
- network.addWifiAccessPoint(WifiAccessPoint.from(values[i * 2], Integer.parseInt(values[i * 2 + 1])));
+ int step = hasSsid ? 3 : 2;
+ int offset = hasSsid ? 1 : 0;
+ for (int i = 0; i < values.length / step; i++) {
+ network.addWifiAccessPoint(WifiAccessPoint.from(
+ values[i * step + offset], Integer.parseInt(values[i * step + offset + 1])));
}
}
- private void decodeNetwork(Position position, String data, boolean hasWifi, boolean hasCell) throws ParseException {
+ private void decodeNetwork(
+ Position position, String data, boolean hasWifi, boolean hasSsid, boolean hasCell) throws ParseException {
int index = 0;
String[] values = data.split("\\+");
@@ -130,7 +134,7 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder {
Network network = new Network();
if (hasWifi) {
- decodeWifi(network, values[index++]);
+ decodeWifi(network, values[index++], hasSsid);
}
if (hasCell) {
@@ -231,19 +235,22 @@ public class MictrackProtocolDecoder extends BaseProtocolDecoder {
decodeLocation(position, fragments[4]);
break;
case "R1":
- decodeNetwork(position, fragments[4], true, false);
+ decodeNetwork(position, fragments[4], true, false, false);
break;
case "R2":
case "R3":
- decodeNetwork(position, fragments[4], false, true);
+ decodeNetwork(position, fragments[4], false, false, true);
break;
case "R12":
case "R13":
- decodeNetwork(position, fragments[4], true, true);
+ decodeNetwork(position, fragments[4], true, false, true);
break;
case "RH":
decodeStatus(position, fragments[4]);
break;
+ case "Y1":
+ decodeNetwork(position, fragments[4], true, true, false);
+ break;
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocol.java b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java
index 82534ecd8..0cc9598ed 100644
--- a/src/main/java/org/traccar/protocol/MiniFinderProtocol.java
+++ b/src/main/java/org/traccar/protocol/MiniFinderProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2022 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.
@@ -41,7 +41,7 @@ public class MiniFinderProtocol extends BaseProtocol {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ';'));
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, ";\0", ";"));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
pipeline.addLast(new MiniFinderProtocolEncoder(MiniFinderProtocol.this));
diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocol.java b/src/main/java/org/traccar/protocol/OrbcommProtocol.java
new file mode 100644
index 000000000..bdfce3b1e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OrbcommProtocol.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpRequestEncoder;
+import io.netty.handler.codec.http.HttpResponseDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerClient;
+
+public class OrbcommProtocol extends BaseProtocol {
+
+ public OrbcommProtocol() {
+ addClient(new TrackerClient(getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpRequestEncoder());
+ pipeline.addLast(new HttpResponseDecoder());
+ pipeline.addLast(new HttpObjectAggregator(65535));
+ pipeline.addLast(new OrbcommProtocolDecoder(OrbcommProtocol.this));
+ pipeline.addLast(new OrbcommProtocolPoller(OrbcommProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java
new file mode 100644
index 000000000..aff722c46
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OrbcommProtocolDecoder.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2022 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.FullHttpResponse;
+import org.traccar.BasePipelineFactory;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+import javax.json.JsonValue;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.LinkedList;
+import java.util.TimeZone;
+
+public class OrbcommProtocolDecoder extends BaseProtocolDecoder {
+
+ public OrbcommProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpResponse response = (FullHttpResponse) msg;
+ String content = response.content().toString(StandardCharsets.UTF_8);
+ JsonObject json = Json.createReader(new StringReader(content)).readObject();
+
+ if (channel != null && !json.getString("NextStartUTC").isEmpty()) {
+ OrbcommProtocolPoller poller =
+ BasePipelineFactory.getHandler(channel.pipeline(), OrbcommProtocolPoller.class);
+ if (poller != null) {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ poller.setStartTime(dateFormat.parse(json.getString("NextStartUTC")));
+ }
+ }
+
+ if (json.get("Messages").getValueType() == JsonValue.ValueType.NULL) {
+ return null;
+ }
+
+ LinkedList<Position> positions = new LinkedList<>();
+
+ JsonArray messages = json.getJsonArray("Messages");
+ for (int i = 0; i < messages.size(); i++) {
+ JsonObject message = messages.getJsonObject(i);
+ DeviceSession deviceSession = getDeviceSession(
+ channel, remoteAddress, true, message.getJsonNumber("ID").toString());
+ if (deviceSession != null) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setDeviceTime(dateFormat.parse(message.getString("MessageUTC")));
+
+ JsonArray fields = message.getJsonObject("Payload").getJsonArray("Fields");
+ for (int j = 0; j < fields.size(); j++) {
+ JsonObject field = fields.getJsonObject(j);
+ String value = field.getString("Value");
+ switch (field.getString("Name")) {
+ case "latitude":
+ position.setLatitude(Integer.parseInt(value) / 60000.0);
+ break;
+ case "longitude":
+ position.setLongitude(Integer.parseInt(value) / 60000.0);
+ break;
+ case "speed":
+ position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(value)));
+ break;
+ case "heading":
+ position.setCourse(Integer.parseInt(value) * 0.1);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (position.getLatitude() != 0 && position.getLongitude() != 0) {
+ position.setValid(true);
+ position.setFixTime(position.getDeviceTime());
+ } else {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ positions.add(position);
+
+ }
+ }
+
+ return positions.isEmpty() ? null : positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java b/src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java
new file mode 100644
index 000000000..6a2d7a92d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OrbcommProtocolPoller.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2022 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.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http.QueryStringEncoder;
+import org.traccar.BaseProtocolPoller;
+import org.traccar.Context;
+import org.traccar.Protocol;
+import org.traccar.config.Keys;
+
+import java.net.SocketAddress;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class OrbcommProtocolPoller extends BaseProtocolPoller {
+
+ private final String accessId;
+ private final String password;
+ private final String host;
+
+ private Date startTime = new Date();
+
+ public void setStartTime(Date startTime) {
+ this.startTime = startTime;
+ }
+
+ public OrbcommProtocolPoller(Protocol protocol) {
+ super(Context.getConfig().getLong(Keys.PROTOCOL_INTERVAL.withPrefix(protocol.getName())));
+ accessId = Context.getConfig().getString(Keys.ORBCOMM_ACCESS_ID);
+ password = Context.getConfig().getString(Keys.ORBCOMM_PASSWORD);
+ host = Context.getConfig().getString(Keys.PROTOCOL_ADDRESS.withPrefix(protocol.getName()));
+ }
+
+ @Override
+ protected void sendRequest(Channel channel, SocketAddress remoteAddress) {
+
+ QueryStringEncoder encoder = new QueryStringEncoder("/GLGW/2/RestMessages.svc/JSON/get_return_messages/");
+ encoder.addParam("access_id", accessId);
+ encoder.addParam("password", password);
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ encoder.addParam("start_utc", dateFormat.format(startTime));
+
+ HttpRequest request = new DefaultFullHttpRequest(
+ HttpVersion.HTTP_1_1, HttpMethod.GET, encoder.toString(), Unpooled.buffer());
+ request.headers().add(HttpHeaderNames.HOST, host);
+ request.headers().add(HttpHeaderNames.CONTENT_LENGTH, 0);
+ channel.writeAndFlush(request);
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java b/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java
index 71aff1a65..685364483 100644
--- a/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2022 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.
@@ -51,6 +51,9 @@ public class S168ProtocolDecoder extends BaseProtocolDecoder {
String content = values[4];
String[] fragments = content.split(";");
for (String fragment : fragments) {
+ if (fragment.isEmpty()) {
+ continue;
+ }
int dataIndex = fragment.indexOf(':');
String type = fragment.substring(0, dataIndex);
diff --git a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
index 042518cb2..c1869def2 100644
--- a/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StartekProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2021 - 2022 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.
@@ -70,14 +70,23 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
.number("(x+),") // outputs
.number("(x+)|") // power
.number("(x+)") // battery
- .groupBegin()
- .text("|")
- .expression("([^,]+)").optional() // adc
+ .expression("([^,]+)?") // adc
.groupBegin()
.text(",")
.number("d,") // extended
.expression("([^,]+)?,") // fuel
- .expression("([^,]+)?,?") // temperature
+ .expression("([^,]+)?") // temperature
+ .groupBegin()
+ .text(",")
+ .number("(d+)|") // rpm
+ .number("(d+)|") // engine load
+ .number("d+|") // maf flow
+ .number("d+|") // intake pressure
+ .number("d+|") // intake temperature
+ .number("(d+)|") // throttle
+ .number("(d+)|") // coolant temperature
+ .number("(d+)|") // instant fuel
+ .number("(d+)") // fuel level
.groupEnd("?")
.groupEnd("?")
.any()
@@ -181,7 +190,7 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext()) {
String[] adc = parser.next().split("\\|");
- for (int i = 0; i < adc.length; i++) {
+ for (int i = 1; i < adc.length; i++) {
position.set(Position.PREFIX_ADC + (i + 1), Integer.parseInt(adc[i], 16) * 0.01);
}
}
@@ -208,6 +217,15 @@ public class StartekProtocolDecoder extends BaseProtocolDecoder {
}
}
+ if (parser.hasNext(6)) {
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set(Position.KEY_ENGINE_LOAD, parser.nextInt());
+ position.set(Position.KEY_THROTTLE, parser.nextInt());
+ position.set(Position.KEY_COOLANT_TEMP, parser.nextInt() - 40);
+ position.set(Position.KEY_FUEL_CONSUMPTION, parser.nextInt() * 0.1);
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextInt());
+ }
+
return position;
}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
index 2d00ea81e..2f8e50c5e 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2022 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ package org.traccar.protocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
@@ -34,6 +35,11 @@ import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
import java.util.TimeZone;
public class SuntechProtocolDecoder extends BaseProtocolDecoder {
@@ -46,6 +52,8 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
private boolean includeRpm;
private boolean includeTemp;
+ private ByteBuf crash;
+
public SuntechProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -732,6 +740,89 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Collection<Position> decodeCrashReport(Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ if (buf.getByte(buf.readerIndex() + 3) != ';') {
+ return null;
+ }
+
+ String[] values = buf.readCharSequence(23, StandardCharsets.US_ASCII).toString().split(";");
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[1]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int currentIndex = Integer.parseInt(values[2]);
+ int totalIndex = Integer.parseInt(values[3]);
+
+ if (crash == null) {
+ crash = Unpooled.buffer();
+ }
+
+ crash.writeBytes(buf.readSlice(buf.readableBytes() - 3));
+
+ if (currentIndex == totalIndex) {
+
+ LinkedList<Position> positions = new LinkedList<>();
+
+ Date crashTime = new DateBuilder()
+ .setDate(crash.readUnsignedByte(), crash.readUnsignedByte(), crash.readUnsignedByte())
+ .setTime(crash.readUnsignedByte(), crash.readUnsignedByte(), crash.readUnsignedByte())
+ .getDate();
+
+ List<Date> times = Arrays.asList(
+ new Date(crashTime.getTime() - 3000),
+ new Date(crashTime.getTime() - 2000),
+ new Date(crashTime.getTime() - 1000),
+ new Date(crashTime.getTime() + 1000));
+
+ for (Date time : times) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setValid(true);
+ position.setTime(time);
+ position.setLatitude(crash.readIntLE() * 0.0000001);
+ position.setLongitude(crash.readIntLE() * 0.0000001);
+ position.setSpeed(UnitsConverter.knotsFromKph(crash.readUnsignedShort() * 0.01));
+ position.setCourse(crash.readUnsignedShort() * 0.01);
+
+ StringBuilder value = new StringBuilder("[");
+ for (int i = 0; i < 100; i++) {
+ if (value.length() > 1) {
+ value.append(",");
+ }
+ value.append("[");
+ value.append(crash.readShortLE());
+ value.append(",");
+ value.append(crash.readShortLE());
+ value.append(",");
+ value.append(crash.readShortLE());
+ value.append("]");
+ }
+ value.append("]");
+
+ position.set(Position.KEY_G_SENSOR, value.toString());
+
+ positions.add(position);
+
+ }
+
+ crash.release();
+ crash = null;
+
+ return positions;
+
+ } else {
+
+ return null;
+
+ }
+
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -747,7 +838,9 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
String[] values = buf.toString(StandardCharsets.US_ASCII).split(";");
prefix = values[0];
- if (prefix.length() < 5) {
+ if (prefix.equals("CRR")) {
+ return decodeCrashReport(channel, remoteAddress, buf);
+ } else if (prefix.length() < 5) {
return decodeUniversal(channel, remoteAddress, values);
} else if (prefix.endsWith("HTE")) {
return decodeTravelReport(channel, remoteAddress, values);
diff --git a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
index b15688df0..d554c2999 100644
--- a/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T800xProtocolDecoder.java
@@ -411,7 +411,7 @@ public class T800xProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(status, 6)) {
- position.setValid(!BitUtil.check(status, 7));
+ position.setValid(true);
position.setTime(readDate(buf));
position.setAltitude(buf.readFloatLE());
position.setLongitude(buf.readFloatLE());
diff --git a/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java b/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
index 6792d61a5..4b22ade03 100644
--- a/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
@@ -6,7 +6,7 @@
* 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
+ * 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,
diff --git a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
index 4990cfd65..cf58b0fed 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
@@ -89,6 +89,8 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_GEOFENCE_EXIT;
} else if (BitUtil.check(status, 2)) {
return Position.ALARM_GEOFENCE_ENTER;
+ } else if (BitUtil.check(status, 14)) {
+ return Position.ALARM_POWER_CUT;
} else if (BitUtil.check(status, 16)) {
return Position.ALARM_SOS;
} else if (BitUtil.check(status, 17)) {
diff --git a/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java b/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
index 21f1ee321..fb213dc40 100644
--- a/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
@@ -5,7 +5,7 @@
* 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
+ * 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,
diff --git a/src/test/java/org/traccar/ProtocolTest.java b/src/test/java/org/traccar/ProtocolTest.java
index c40a15dcc..353593c29 100644
--- a/src/test/java/org/traccar/ProtocolTest.java
+++ b/src/test/java/org/traccar/ProtocolTest.java
@@ -4,9 +4,11 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import org.traccar.helper.DataConverter;
import org.traccar.model.CellTower;
@@ -89,6 +91,10 @@ public class ProtocolTest extends BaseTest {
return new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, method, url, Unpooled.buffer(), headers, new DefaultHttpHeaders());
}
+ protected DefaultFullHttpResponse response(ByteBuf data) {
+ return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, data);
+ }
+
protected void verifyNotNull(BaseProtocolDecoder decoder, Object object) throws Exception {
assertNotNull(decoder.decode(null, null, object));
}
diff --git a/src/test/java/org/traccar/geocoder/GeocoderTest.java b/src/test/java/org/traccar/geocoder/GeocoderTest.java
index 380980d2b..9134194f2 100644
--- a/src/test/java/org/traccar/geocoder/GeocoderTest.java
+++ b/src/test/java/org/traccar/geocoder/GeocoderTest.java
@@ -105,9 +105,17 @@ public class GeocoderTest {
@Ignore
@Test
public void testMapTiler() {
- Geocoder geocoder = new MapTilerGeocoder("mnbnwLErpdspq13f0kC6", 0, new AddressFormat());
+ Geocoder geocoder = new MapTilerGeocoder("", 0, new AddressFormat());
String address = geocoder.getAddress(40.733, -73.989, null);
assertEquals("East 13th Street, New York City, New York, United States", address);
}
+ @Ignore
+ @Test
+ public void testGeoapify() {
+ Geocoder geocoder = new GeoapifyGeocoder("", null, 0, new AddressFormat());
+ String address = geocoder.getAddress(40.733, -73.989, null);
+ assertEquals("114 East 13th Street, New York, New York, US", address);
+ }
+
}
diff --git a/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java
new file mode 100644
index 000000000..da2542b34
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/ArmoliProtocolDecoderTest.java
@@ -0,0 +1,36 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+import org.traccar.model.Position;
+
+public class ArmoliProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new ArmoliProtocolDecoder(null);
+
+ verifyAttribute(decoder, text(
+ "[M869867039550712160821153237N41.033508E029.2697032F00036000000410006B336FFFFG458563@A6D>04410C2482>03410F56>03412F19>0441210000>034130FF>0441313A7>03410D30>04411F01B6>0341048C>04410C1C98];"),
+ Position.KEY_RPM, 1830L);
+
+ verifyPosition(decoder, text(
+ "[M869867038698074210122125205N38.735641E035.4727751E003340000000C00000E9E07FF:106AG505283H60E]"));
+
+ verifyAttribute(decoder, text(
+ "[W869867038698074,O,1234,2657,1]"),
+ Position.KEY_RESULT, "O,1234,2657,1");
+
+ verifyNull(decoder, text(
+ "[Q010001088610010024363698990011101070608200,05XXXXXXXXX,10.49.182.53,C,1,20,19,0]"));
+
+ verifyPosition(decoder, text(
+ "[M860906041293587100122061310N40.792751E029.4313092801143000000010003513209FFGC18080H8DA#E209C4]"));
+
+ verifyNull(decoder, text(
+ "[L866104027971681]"));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java
index 96cd78f03..4089c208c 100644
--- a/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Dsf22ProtocolDecoderTest.java
@@ -1,10 +1,8 @@
package org.traccar.protocol;
-import org.junit.Ignore;
import org.junit.Test;
import org.traccar.ProtocolTest;
-@Ignore
public class Dsf22ProtocolDecoderTest extends ProtocolTest {
@Test
@@ -13,6 +11,9 @@ public class Dsf22ProtocolDecoderTest extends ProtocolTest {
var decoder = new Dsf22ProtocolDecoder(null);
verifyPositions(decoder, binary(
+ "4642a82d01c8f6aa1af1792c0c1411eb61001e0000"));
+
+ verifyPositions(decoder, binary(
"4642000101A8EE5F0ECA5FF421B33F524E32610401"));
verifyPositions(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
index d0c683abf..c1cc3c39a 100644
--- a/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/EelinkProtocolDecoderTest.java
@@ -14,6 +14,9 @@ public class EelinkProtocolDecoderTest extends ProtocolTest {
verifyPositions(decoder, binary(
"454c029249a50354679090044671676712004321315f3cf43503fc94d3760c79328a0129000000000a01f9000190330905580d2e046f118a04ec00000000ccc7086c02fe000000000000000000000000000000000000676712004321325f3cf43e03fc94d3760c79328a0129000000000901f9000190330905580d2e046f117b04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321335f3cf44703fc94d3760c79328a0129000000000901f9000190330905580d2e046f117f04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321345f3cf45303fc94d3760c79328a0129000000000901f9000190330905580d2e046f119d04ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321355f3cf45c03fc94d3760c79328a0129000000000801f9000190330905580d2e046f11a304ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321365f3cf46603fc94d3760c79328a0129000000000801f9000190330905580d2e046f118804df00000000ccc7086d02ff000000000000000000000000000000000000676712004321375f3cf47103fc94d3760c79328a0129000000000901f9000190330905580d2e046f119704ec00000000ccc7086d02ff000000000000000000000000000000000000676712004321385f3cf47a03fc94d3760c79328a0129000000000901f9000190330905580d2e046f118204ec00000000ccc7086e0300000000000000000000000000000000000000676712004321395f3cf48303fc94d3760c79328a0129000000000901f9000190330905580d2e046f117604df00000000ccc7086e0300000000000000000000000000000000000000"));
+ verifyPosition(decoder, binary(
+ "6767120056096661d38e0091fbf0aa3a0f8fa08500060051015f09002542e50e7ea6080101f90001304e304e0818390d000000c524c2ae0699102b00000000000115b0040504050000000014000000000000000000000000000002"));
+
verifyAttribute(decoder, binary(
"676714001500035f74a2940201360104591100a7160122250400"),
Position.KEY_ALARM, Position.ALARM_REMOVING);
@@ -42,9 +45,6 @@ public class EelinkProtocolDecoderTest extends ProtocolTest {
verifyPosition(decoder, binary(
"676714002414B05AD43A7D03026B92B10C395499FFD7000000000701CC00002495000014203604067B"));
- verifyNotNull(decoder, binary(
- "676714004F14B0E68CAFE58AA8E68AA5E8ADA621E5B9BFE4B89CE79C81E6B7B1E59CB3E5B882E58D97E5B1B1E58CBAE696B0E8A5BFE8B7AF3138EFBC88E8B79DE5AE87E998B3E5A4A7E58EA63230E7B1B3EFBC89"));
-
verifyPosition(decoder, binary(
"676780005a000001000000004c61743a4e33312e38333935352c4c6f6e3a5738322e36313334362c436f757273653a302e30302c53706565643a302e30306b6d2f682c4461746554696d653a323031372d31322d30322031313a32393a3433"));
diff --git a/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java
index a276a01e9..c819cd8bf 100644
--- a/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/FlexApiProtocolDecoderTest.java
@@ -11,9 +11,12 @@ public class FlexApiProtocolDecoderTest extends ProtocolTest {
var decoder = new FlexApiProtocolDecoder(null);
verifyAttributes(decoder, text(
+ "${\"topic\":\"v1/VF3102021113001/motion/info\",\"payload\":{\"motion.ts\":1641885877,\"motion.ax\":0.006344,\"motion.ay\":0.289384,\"motion.az\":-0.939156,\"motion.gx\":0.420000,\"motion.gy\":0.420000,\"motion.gz\":-0.280000}}xx"));
+
+ verifyAttributes(decoder, text(
"${\"topic\":\"v1/VF3102021113001/gnss/info\",\"payload\":{\"gnss.ts\":1639713510,\"gnss.latitude\":30.587509,\"gnss.longitude\":104.053650,\"gnss.altitude\":391,\"gnss.speed\":0,\"gnss.heading\":0,\"gnss.hdop\":1.100000,\"gnss.fix\":4,\"gnss.num_sv\":10}}xx"));
- verifyNull(decoder, text(
+ verifyAttributes(decoder, text(
"${\"topic\":\"v1/VF3102021113001/cellular1/info\",\"payload\":{\"modem1.ts\":1639713510,\"modem1.imei\":\"863674047326655\",\"modem1.imsi\":\"\",\"modem1.iccid\":\"\",\"modem1.phone_num\":\"\",\"modem1.signal_lvl\":0,\"modem1.reg_status\":0,\"modem1.operator\":\"\",\"modem1.network\":0,\"modem1.lac\":\"\",\"modem1.cell_id\":\"\",\"modem1.rssi\":0,\"modem1.rsrp\":0,\"modem1.rsrq\":0,\"cellular1.status\":2,\"cellular1.ip\":\"0.0.0.0\",\"cellular1.netmask\":\"255.255.255.255\",\"cellular1.gateway\":\"0.0.0.0\",\"cellular1.dns1\":\"0.0.0.0\",\"cellular1.up_at\":602}}xx"));
verifyAttributes(decoder, text(
@@ -25,16 +28,16 @@ public class FlexApiProtocolDecoderTest extends ProtocolTest {
verifyPosition(decoder, text(
"${\"topic\":\"v1/VF3102021111601/gnss/info\",\"payload\":{\"time\":1637225390,\"lat\":30.587942,\"log\":104.053543,\"gnss.altitude\":480.399994,\"gnss.speed\":0,\"gnss.heading\":0,\"gnss.hdop\":0.900000,\"gnss.fix\":4,\"gnss.num_sv\":11}}xx"));
- verifyNull(decoder, text(
+ verifyAttributes(decoder, text(
"${\"topic\":\"v1/VF3102021111601/motion/info\",\"payload\":{\"motion.ts\":1637225450,\"motion.ax\":0.009272,\"motion.ay\":0.278404,\"motion.az\":-0.941596,\"motion.gx\":0.420000,\"motion.gy\":-0.490000,\"motion.gz\":0.140000}}xx"));
- verifyNull(decoder, text(
+ verifyAttributes(decoder, text(
"${\"topic\":\"v1/VF3102021111601/sysinfo/info\",\"payload\":{\"sysinfo.ts\":1637224740,\"sysinfo.model_name\":\"310\",\"sysinfo.oem_name\":\"inhand\",\"sysinfo.serial_number\":\"VF3102021111601\",\"sysinfo.firmware_version\":\"VT3_V1.1.32\",\"sysinfo.product_number\":\"FQ58\",\"sysinfo.description\":\"www.inhand.com.cn\"}}xx"));
- verifyNull(decoder, text(
+ verifyAttributes(decoder, text(
"${\"topic\":\"v1/VF3102021111601/io/info\",\"payload\":{\"io.ts\":1637227722,\"io.AI1\":0,\"io.DI1\":1,\"io.DI2\":0,\"io.DI3\":0,\"io.DI4\":0,\"io.DI1_pullup\":0,\"io.DI2_pullup\":0,\"io.DI3_pullup\":0,\"io.DI4_pullup\":0,\"io.DO1\":0,\"io.DO2\":0,\"io.DO3\":0,\"io.IGT\":1}}xx"));
- verifyNull(decoder, text(
+ verifyAttributes(decoder, text(
"${\"topic\":\"v1/VF3102021111601/cellular1/info\",\"payload\":{\"modem1.ts\":1637225330,\"modem1.imei\":\"863674047324999\",\"modem1.imsi\":\"460111150414721\",\"modem1.iccid\":\"89860319482086580401\",\"modem1.phone_num\":\"\",\"modem1.signal_lvl\":25,\"modem1.reg_status\":1,\"modem1.operator\":\"46011\",\"modem1.network\":3,\"modem1.lac\":\"EA00\",\"modem1.cell_id\":\"E779B81\",\"modem1.rssi\":0,\"modem1.rsrp\":0,\"modem1.rsrq\":0,\"cellular1.status\":3,\"cellular1.ip\":\"10.136.143.193\",\"cellular1.netmask\":\"255.255.255.255\",\"cellular1.gateway\":\"10.64.64.64\",\"cellular1.dns1\":\"223.5.5.5\",\"cellular1.up_at\":450}}xx"));
}
diff --git a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
index 8978f64e5..9fab7e010 100644
--- a/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
@@ -11,6 +11,10 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
var decoder = new Gl200TextProtocolDecoder(null);
+ verifyAttribute(decoder, buffer(
+ "+RESP:GTFRI,423031,866873025895726,,0,1,1,0,1,16,0.0,351,51.6,121.391063,31.164633,20181212072535,460,00,1877,DAE,00,3,85,20181212072535,002C$"),
+ Position.KEY_BATTERY_LEVEL, 85);
+
verifyAttributes(decoder, buffer(
"+RESP:GTINF,DC0103,865284049247079,gv600mg,21,89883070000007211665,22,0,11,12913,12917,4.26,0,1,,,20210216154607,1,79,,01,00,,,20210216104606,1EBE$"));
diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java
index d2d090c04..8aadb7fe3 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"));
verifyNotNull(decoder, binary(
+ "78783B2810010D02020201CC00287D001F713E287D001F7231287D001E232D287D001F4018000000000000000000000000000000000000FF00020005B14B0D0A"));
+
+ verifyNotNull(decoder, binary(
"78782111150b0b022c30c804b7af7808810cb0003c00012e02d075df0084890c000679950d0a"));
verifyNotNull(decoder, binary(
diff --git a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
index a0462e675..ad5f82176 100644
--- a/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/H02ProtocolDecoderTest.java
@@ -12,6 +12,9 @@ public class H02ProtocolDecoderTest extends ProtocolTest {
var decoder = new H02ProtocolDecoder(null);
verifyPosition(decoder, buffer(
+ "*HQ,5905101893,V1,105759,A,37573392,S,145037022,E,000.00,173,280122,FF7FFBFF,,,9059e2c,8232,4#"));
+
+ verifyPosition(decoder, buffer(
"*HQ,4970105243,V1,104000,A,2235.1777,N,11357.8913,E,000.27,235,130721,FFFFFBFF,460,11,d18e105,7752,6#"));
verifyAttribute(decoder, buffer(
diff --git a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
index 238799fac..7aaec33e7 100644
--- a/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/HuabaoProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class HuabaoProtocolDecoderTest extends ProtocolTest {
var decoder = new HuabaoProtocolDecoder(null);
+ verifyNull(decoder, buffer(
+ "(794104004140,1,001,BASE,2,TIME)"));
+
verifyNull(decoder, binary(
"7E01000021013345678906000F002C012F373031313142534A2D4D3742203030303030303001D4C1423838383838B47E"));
diff --git a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
index 4c17bf1f8..ca8b67a46 100644
--- a/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/MictrackProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class MictrackProtocolDecoderTest extends ProtocolTest {
var decoder = new MictrackProtocolDecoder(null);
+ verifyAttributes(decoder, text(
+ "MT;5;867035041396795;Y1;220111085741+test,8c:53:c3:db:e7:26,-58,jiuide-842,80:26:89:f0:5e:4f,-74,jiu2ide 403,94:e4:4b:0a:31:08,-75,jiu3ide,7a:91:e9:50:26:0b,-85,CNet-9rNe,78:91:e9:40:26:0b,-87+0+4092+1"));
+
verifyAttribute(decoder, text(
"867035041390699 netlock=Success!"),
Position.KEY_RESULT, "netlock=Success");
diff --git a/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java
new file mode 100644
index 000000000..af35505d5
--- /dev/null
+++ b/src/test/java/org/traccar/protocol/OrbcommProtocolDecoderTest.java
@@ -0,0 +1,24 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OrbcommProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ var decoder = new OrbcommProtocolDecoder(null);
+
+ verifyNull(decoder, response(
+ buffer("{\"ErrorID\":0,\"NextStartUTC\":\"\",\"Messages\":null}")));
+
+ verifyPositions(decoder, response(
+ buffer("{\"ErrorID\":0,\"NextStartUTC\":\"2022-01-20 21:17:19\",\"Messages\":[{\"ID\":21545955455454,\"MessageUTC\":\"2022-01-20 21:17:19\",\"ReceiveUTC\":\"2022-01-20 21:17:19\",\"SIN\":19,\"MobileID\":\"01097623SKY2C68\",\"Payload\":{\"Name\":\"simpleReport\",\"SIN\":19,\"MIN\":1,\"Fields\":[{\"Name\":\"latitude\",\"Value\":\"2717900\",\"Type\":\"signedint\"},{\"Name\":\"longitude\",\"Value\":\"-4555211\",\"Type\":\"signedint\"},{\"Name\":\"speed\",\"Value\":\"0\",\"Type\":\"unsignedint\"},{\"Name\":\"heading\",\"Value\":\"1439\",\"Type\":\"unsignedint\"}]},\"RegionName\":\"\",\"OTAMessageSize\":17,\"CustomerID\":0,\"Transport\":1,\"MobileOwnerID\":60000934}]}")));
+
+ verifyPositions(decoder, false, response(
+ buffer("{\"ErrorID\":0,\"NextStartUTC\":\"2016-10-13 15:19:59\",\"Messages\":[{\"ID\":120213064,\"MessageUTC\":\"2016-10-12 12:42:01\",\"ReceiveUTC\":\"2016-10-12 12:42:01\",\"SIN\":0,\"MobileID\":\"01173096SKY0E45\",\"Payload\":{\"Name\":\"modemRegistration\",\"SIN\":0,\"MIN\":0,\"Fields\":[{\"Name\":\"hardwareMajorVersion\",\"Value\":\"4\"},{\"Name\":\"hardwareMinorVersion\",\"Value\":\"2\"},{\"Name\":\"softwareMajorVersion\",\"Value\":\"13\"},{\"Name\":\"softwareMinorVersion\",\"Value\":\"1\"},{\"Name\":\"product\",\"Value\":\"4\"},{\"Name\":\"wakeupPeriod\",\"Value\":\"None\"},{\"Name\":\"lastResetReason\",\"Value\":\"Software\"},{\"Name\":\"virtualCarrier\",\"Value\":\"6\"},{\"Name\":\"beam\",\"Value\":\"1\"},{\"Name\":\"vain\",\"Value\":\"0\"},{\"Name\":\"reserved\",\"Value\":\"0\"},{\"Name\":\"operatorTxState\",\"Value\":\"0\"},{\"Name\":\"userTxState\",\"Value\":\"0\"},{\"Name\":\"broadcastIDCount\",\"Value\":\"0\"}],\"RegionName\":\"AMERRB11\",\"OTAMessageSize\":15,\"CustomerID\":0}}]}")));
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java
index d4918e121..6bdf19182 100644
--- a/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/S168ProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class S168ProtocolDecoderTest extends ProtocolTest {
var decoder = new S168ProtocolDecoder(null);
+ verifyPosition(decoder, text(
+ "S168#358511039001705#003a#01ca#LOCA:G;CELL:6,1cc,0,2479,de11150,2e,2479,d6e4546,31,2479,d6e4547,39,778c,787cc30,39,778c,787cc31,40,253f,6195502,32;GDATA:A,5,220117220950,22.779583,113.820633,5,296,35;ALERT:0080;STATUS:98,73;;FARM:0,0009,0000,010a,20220117220950;WIFI:10,CC-08-FB-A5-49-B3,-28,08-40-F3-7F-6C-A9,-59,A4-29-40-65-2C-42,-74,80-89-17-A5-6F-7B,-82,80-EA-07-82-93-C6,-82,FC-37-2B-34-D6-A1,-83,34-6B-5B-A9-49-15,-83,BC-46-99-B3-51-10,-84,BC-54-FC-53-0A-D1,-84,3C-CD-57-67-D1-32,-85"));
+
verifyNull(decoder, text(
"S168#358511139046180#00c9#0009#SYNC:0000"));
diff --git a/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java
index 5d22344fa..6c2d39940 100644
--- a/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/StartekProtocolDecoderTest.java
@@ -12,6 +12,9 @@ public class StartekProtocolDecoderTest extends ProtocolTest {
var decoder = new StartekProtocolDecoder(null);
verifyPosition(decoder, text(
+ "&&R187,860294046453690,000,0,,220105160656,A,22.994986,72.499711,15,0.9,2,222,55,121135784,404|98|147B|0000376A,24,0000001F,02,00,052E|01A3|0000|0000,1,010000|020000,,853|6|10|105|73|41|125|34|52"));
+
+ verifyPosition(decoder, text(
"&&o142,860262050066062,000,27,,211111070826,V,28.653435,-106.077455,0,0.0,0,151,1412,918,0|0|4708|01402D19,6,0000001A,02,00,04C0|016C|0000|0000,1,,,BB"));
verifyPosition(decoder, text(
diff --git a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
index a9720f437..098758728 100644
--- a/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/SuntechProtocolDecoderTest.java
@@ -1,5 +1,6 @@
package org.traccar.protocol;
+import org.junit.Ignore;
import org.junit.Test;
import org.traccar.ProtocolTest;
import org.traccar.model.Position;
@@ -257,4 +258,16 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
}
+ @Ignore
+ @Test
+ public void testDecodeCrash() throws Exception {
+
+ var decoder = new SuntechProtocolDecoder(null);
+
+ verifyAttribute(decoder, binary(
+ "4352523b303931303030303036333b313b313b303135303b16011c150f0ad82f6c0000000000ae037085fbff7700fd00faff6300f30000006800fb000d007100fa00f32f6c00000000005e044a80fcff6f000301e1ff5d00e900e1ff6400e600f4ff5b00ec000a306c00000000002104248306006c000501fcff5b00e00001006e000101eeff4e00e10022306c00000000005c041a7e00006a00010100005d00f800b5ff7cffdf0050009300fc003b44350d"),
+ Position.KEY_G_SENSOR, "");
+
+ }
+
}