aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/org/traccar/Main.java58
-rw-r--r--src/main/java/org/traccar/MainModule.java4
-rw-r--r--src/main/java/org/traccar/StringProtocolEncoder.java30
-rw-r--r--src/main/java/org/traccar/api/ExtendedObjectResource.java2
-rw-r--r--src/main/java/org/traccar/api/HealthCheckService.java91
-rw-r--r--src/main/java/org/traccar/api/resource/SessionResource.java2
-rw-r--r--src/main/java/org/traccar/database/BaseObjectManager.java60
-rw-r--r--src/main/java/org/traccar/database/CommandsManager.java29
-rw-r--r--src/main/java/org/traccar/database/DeviceManager.java118
-rw-r--r--src/main/java/org/traccar/database/DriversManager.java51
-rw-r--r--src/main/java/org/traccar/database/ExtendedObjectManager.java78
-rw-r--r--src/main/java/org/traccar/database/GroupsManager.java5
-rw-r--r--src/main/java/org/traccar/database/SimpleObjectManager.java30
-rw-r--r--src/main/java/org/traccar/database/UsersManager.java6
-rw-r--r--src/main/java/org/traccar/geocoder/FactualGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geocoder/HereGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geocoder/MapQuestGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geocoder/OpenCageGeocoder.java12
-rw-r--r--src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java7
-rw-r--r--src/main/java/org/traccar/helper/Checksum.java8
-rw-r--r--src/main/java/org/traccar/helper/LogAction.java12
-rw-r--r--src/main/java/org/traccar/helper/ServletHelper.java45
-rw-r--r--src/main/java/org/traccar/model/Command.java1
-rw-r--r--src/main/java/org/traccar/protocol/AdmProtocolEncoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java179
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java82
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocol.java9
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java80
-rw-r--r--src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java108
-rw-r--r--src/main/java/org/traccar/protocol/BceProtocolDecoder.java104
-rw-r--r--src/main/java/org/traccar/protocol/BlueProtocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/BlueProtocolDecoder.java170
-rw-r--r--src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/CastelProtocolDecoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/DmtProtocolDecoder.java13
-rw-r--r--src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java3
-rw-r--r--src/main/java/org/traccar/protocol/EsealProtocolEncoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/EskyProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/EskyProtocolDecoder.java11
-rw-r--r--src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/GatorProtocolDecoder.java27
-rw-r--r--src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java10
-rw-r--r--src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java51
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java18
-rw-r--r--src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java87
-rw-r--r--src/main/java/org/traccar/protocol/H02ProtocolDecoder.java17
-rw-r--r--src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java29
-rw-r--r--src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java80
-rw-r--r--src/main/java/org/traccar/protocol/ItsProtocolDecoder.java37
-rw-r--r--src/main/java/org/traccar/protocol/Jt600FrameDecoder.java2
-rw-r--r--src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java6
-rw-r--r--src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java28
-rw-r--r--src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java22
-rw-r--r--src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java17
-rw-r--r--src/main/java/org/traccar/protocol/MotorProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/MotorProtocolDecoder.java85
-rw-r--r--src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java58
-rw-r--r--src/main/java/org/traccar/protocol/OmnicommProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java142
-rw-r--r--src/main/java/org/traccar/protocol/OutsafeProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java69
-rw-r--r--src/main/java/org/traccar/protocol/PacificTrackProtocol.java33
-rw-r--r--src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java152
-rw-r--r--src/main/java/org/traccar/protocol/PluginProtocolDecoder.java62
-rw-r--r--src/main/java/org/traccar/protocol/PstFrameDecoder.java51
-rw-r--r--src/main/java/org/traccar/protocol/PstProtocol.java40
-rw-r--r--src/main/java/org/traccar/protocol/PstProtocolDecoder.java116
-rw-r--r--src/main/java/org/traccar/protocol/Pt215Protocol.java35
-rw-r--r--src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java118
-rw-r--r--src/main/java/org/traccar/protocol/Pt502ProtocolEncoder.java8
-rw-r--r--src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java171
-rw-r--r--src/main/java/org/traccar/protocol/RstProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/RstProtocolDecoder.java132
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java40
-rw-r--r--src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java13
-rw-r--r--src/main/java/org/traccar/protocol/S168Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/S168ProtocolDecoder.java81
-rw-r--r--src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java188
-rw-r--r--src/main/java/org/traccar/protocol/SolarPoweredProtocol.java34
-rw-r--r--src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java97
-rw-r--r--src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java7
-rw-r--r--src/main/java/org/traccar/protocol/SuntechFrameDecoder.java23
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocol.java2
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java168
-rw-r--r--src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java37
-rw-r--r--src/main/java/org/traccar/protocol/SviasProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/T55ProtocolDecoder.java33
-rw-r--r--src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java17
-rw-r--r--src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java30
-rw-r--r--src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java34
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocol.java33
-rw-r--r--src/main/java/org/traccar/protocol/TopinProtocolDecoder.java182
-rw-r--r--src/main/java/org/traccar/protocol/TotemProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java59
-rw-r--r--src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java5
-rw-r--r--src/main/java/org/traccar/protocol/UproProtocolDecoder.java23
-rw-r--r--src/main/java/org/traccar/protocol/VnetProtocol.java37
-rw-r--r--src/main/java/org/traccar/protocol/VnetProtocolDecoder.java105
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocolDecoder.java15
-rw-r--r--src/main/java/org/traccar/protocol/WatchProtocolEncoder.java22
-rw-r--r--src/main/java/org/traccar/protocol/WialonProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/WondexProtocolEncoder.java12
-rw-r--r--src/main/java/org/traccar/protocol/XexunProtocolEncoder.java4
-rw-r--r--src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java2
112 files changed, 4243 insertions, 549 deletions
diff --git a/src/main/java/org/traccar/Main.java b/src/main/java/org/traccar/Main.java
index 6ebd1d399..47d6e91df 100644
--- a/src/main/java/org/traccar/Main.java
+++ b/src/main/java/org/traccar/Main.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import com.google.inject.Guice;
import com.google.inject.Injector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.traccar.api.HealthCheckService;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
@@ -105,6 +106,27 @@ public final class Main {
}
}
+ private static void scheduleHealthCheck() {
+ HealthCheckService service = new HealthCheckService();
+ if (service.isEnabled()) {
+ new Timer().scheduleAtFixedRate(
+ service.createTask(), service.getPeriod(), service.getPeriod());
+ }
+ }
+
+ private static void scheduleDatabaseCleanup() {
+ new Timer().scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ Context.getDataManager().clearHistory();
+ } catch (SQLException error) {
+ LOGGER.warn("Clear history error", error);
+ }
+ }
+ }, 0, CLEAN_PERIOD);
+ }
+
public static void run(String configFile) {
try {
Context.init(configFile);
@@ -118,35 +140,19 @@ public final class Main {
Context.getWebServer().start();
}
- new Timer().scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- try {
- Context.getDataManager().clearHistory();
- } catch (SQLException error) {
- LOGGER.warn("Clear history error", error);
- }
- }
- }, 0, CLEAN_PERIOD);
+ scheduleHealthCheck();
+ scheduleDatabaseCleanup();
- Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
- @Override
- public void uncaughtException(Thread t, Throwable e) {
- LOGGER.error("Thread exception", e);
- }
- });
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> LOGGER.error("Thread exception", e));
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- LOGGER.info("Shutting down server...");
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ LOGGER.info("Shutting down server...");
- if (Context.getWebServer() != null) {
- Context.getWebServer().stop();
- }
- Context.getServerManager().stop();
+ if (Context.getWebServer() != null) {
+ Context.getWebServer().stop();
}
- });
+ Context.getServerManager().stop();
+ }));
} catch (Exception e) {
LOGGER.error("Main method error", e);
throw new RuntimeException(e);
diff --git a/src/main/java/org/traccar/MainModule.java b/src/main/java/org/traccar/MainModule.java
index 1d5508f5a..c3c0182d7 100644
--- a/src/main/java/org/traccar/MainModule.java
+++ b/src/main/java/org/traccar/MainModule.java
@@ -171,7 +171,7 @@ public class MainModule extends AbstractModule {
case "ban":
return new BanGeocoder(cacheSize, addressFormat);
case "here":
- return new HereGeocoder(id, key, language, cacheSize, addressFormat);
+ return new HereGeocoder(url, id, key, language, cacheSize, addressFormat);
case "mapmyindia":
return new MapmyIndiaGeocoder(url, key, cacheSize, addressFormat);
default:
@@ -192,7 +192,7 @@ public class MainModule extends AbstractModule {
case "google":
return new GoogleGeolocationProvider(key);
case "opencellid":
- return new OpenCellIdGeolocationProvider(key);
+ return new OpenCellIdGeolocationProvider(url, key);
case "unwired":
return new UnwiredGeolocationProvider(url, key);
default:
diff --git a/src/main/java/org/traccar/StringProtocolEncoder.java b/src/main/java/org/traccar/StringProtocolEncoder.java
index d9acce7f0..e9fb65500 100644
--- a/src/main/java/org/traccar/StringProtocolEncoder.java
+++ b/src/main/java/org/traccar/StringProtocolEncoder.java
@@ -17,8 +17,6 @@ package org.traccar;
import org.traccar.model.Command;
-import java.util.Map;
-
public abstract class StringProtocolEncoder extends BaseProtocolEncoder {
public StringProtocolEncoder(Protocol protocol) {
@@ -31,21 +29,27 @@ public abstract class StringProtocolEncoder extends BaseProtocolEncoder {
protected String formatCommand(Command command, String format, ValueFormatter valueFormatter, String... keys) {
- String result = String.format(format, (Object[]) keys);
-
- result = result.replaceAll("\\{" + Command.KEY_UNIQUE_ID + "}", getUniqueId(command.getDeviceId()));
- for (Map.Entry<String, Object> entry : command.getAttributes().entrySet()) {
+ Object[] values = new String[keys.length];
+ for (int i = 0; i < keys.length; i++) {
String value = null;
- if (valueFormatter != null) {
- value = valueFormatter.formatValue(entry.getKey(), entry.getValue());
- }
- if (value == null) {
- value = entry.getValue().toString();
+ if (keys[i].equals(Command.KEY_UNIQUE_ID)) {
+ value = getUniqueId(command.getDeviceId());
+ } else {
+ Object object = command.getAttributes().get(keys[i]);
+ if (valueFormatter != null) {
+ value = valueFormatter.formatValue(keys[i], object);
+ }
+ if (value == null && object != null) {
+ value = object.toString();
+ }
+ if (value == null) {
+ value = "";
+ }
}
- result = result.replaceAll("\\{" + entry.getKey() + "}", value);
+ values[i] = value;
}
- return result;
+ return String.format(format, values);
}
protected String formatCommand(Command command, String format, String... keys) {
diff --git a/src/main/java/org/traccar/api/ExtendedObjectResource.java b/src/main/java/org/traccar/api/ExtendedObjectResource.java
index 007a7b1bd..9e554217e 100644
--- a/src/main/java/org/traccar/api/ExtendedObjectResource.java
+++ b/src/main/java/org/traccar/api/ExtendedObjectResource.java
@@ -55,8 +55,8 @@ public class ExtendedObjectResource<T extends BaseModel> extends BaseObjectResou
Context.getPermissionsManager().checkDevice(getUserId(), deviceId);
result.retainAll(manager.getDeviceItems(deviceId));
}
- return manager.getItems(result);
+ return manager.getItems(result);
}
}
diff --git a/src/main/java/org/traccar/api/HealthCheckService.java b/src/main/java/org/traccar/api/HealthCheckService.java
new file mode 100644
index 000000000..1e8f0d731
--- /dev/null
+++ b/src/main/java/org/traccar/api/HealthCheckService.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.api;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.traccar.Context;
+
+import java.util.TimerTask;
+
+public class HealthCheckService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(HealthCheckService.class);
+
+ private SystemD systemD;
+
+ private boolean enabled;
+ private long period;
+
+ public HealthCheckService() {
+ if (Context.getConfig().getBoolean("web.healthCheck")
+ && System.getProperty("os.name").toLowerCase().startsWith("linux")) {
+ try {
+ systemD = Native.load("systemd", SystemD.class);
+ String watchdogTimer = System.getenv("WATCHDOG_USEC");
+ if (watchdogTimer != null && !watchdogTimer.isEmpty()) {
+ period = Long.parseLong(watchdogTimer) / 1000 * 4 / 5;
+ }
+ if (period > 0) {
+ LOGGER.info("Health check enabled with period {}", period);
+ enabled = true;
+ }
+ } catch (UnsatisfiedLinkError e) {
+ LOGGER.warn("No systemd support", e);
+ }
+ }
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public long getPeriod() {
+ return period;
+ }
+
+ private String getUrl() {
+ String address = Context.getConfig().getString("web.address", "localhost");
+ int port = Context.getConfig().getInteger("web.port", 8082);
+ return "http://" + address + ":" + port + "/api/server";
+ }
+
+ public TimerTask createTask() {
+ return new TimerTask() {
+ @Override
+ public void run() {
+ LOGGER.debug("Health check running");
+ int status = Context.getClient().target(getUrl()).request().get().getStatus();
+ if (status == 200) {
+ int result = systemD.sd_notify(0, "WATCHDOG=1");
+ if (result < 0) {
+ LOGGER.warn("Health check notify error {}", result);
+ }
+ } else {
+ LOGGER.warn("Health check failed with status {}", status);
+ }
+ }
+ };
+ }
+
+ interface SystemD extends Library {
+ @SuppressWarnings("checkstyle:MethodName")
+ int sd_notify(@SuppressWarnings("checkstyle:ParameterName") int unset_environment, String state);
+ }
+
+}
diff --git a/src/main/java/org/traccar/api/resource/SessionResource.java b/src/main/java/org/traccar/api/resource/SessionResource.java
index fd331c766..e3c5d457f 100644
--- a/src/main/java/org/traccar/api/resource/SessionResource.java
+++ b/src/main/java/org/traccar/api/resource/SessionResource.java
@@ -18,6 +18,7 @@ package org.traccar.api.resource;
import org.traccar.Context;
import org.traccar.api.BaseResource;
import org.traccar.helper.DataConverter;
+import org.traccar.helper.ServletHelper;
import org.traccar.helper.LogAction;
import org.traccar.model.User;
@@ -106,6 +107,7 @@ public class SessionResource extends BaseResource {
LogAction.login(user.getId());
return user;
} else {
+ LogAction.failedLogin(ServletHelper.retrieveRemoteAddress(request));
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
diff --git a/src/main/java/org/traccar/database/BaseObjectManager.java b/src/main/java/org/traccar/database/BaseObjectManager.java
index 8bf9ef860..e274e5aba 100644
--- a/src/main/java/org/traccar/database/BaseObjectManager.java
+++ b/src/main/java/org/traccar/database/BaseObjectManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,6 +23,8 @@ import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,6 +34,8 @@ public class BaseObjectManager<T extends BaseModel> {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseObjectManager.class);
+ private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
private final DataManager dataManager;
private Map<Long, T> items;
@@ -43,6 +47,22 @@ public class BaseObjectManager<T extends BaseModel> {
refreshItems();
}
+ protected final void readLock() {
+ lock.readLock().lock();
+ }
+
+ protected final void readUnlock() {
+ lock.readLock().unlock();
+ }
+
+ protected final void writeLock() {
+ lock.writeLock().lock();
+ }
+
+ protected final void writeUnlock() {
+ lock.writeLock().unlock();
+ }
+
protected final DataManager getDataManager() {
return dataManager;
}
@@ -52,12 +72,18 @@ public class BaseObjectManager<T extends BaseModel> {
}
public T getById(long itemId) {
- return items.get(itemId);
+ try {
+ readLock();
+ return items.get(itemId);
+ } finally {
+ readUnlock();
+ }
}
public void refreshItems() {
if (dataManager != null) {
try {
+ writeLock();
Collection<T> databaseItems = dataManager.getObjects(baseClass);
if (items == null) {
items = new ConcurrentHashMap<>(databaseItems.size());
@@ -78,12 +104,19 @@ public class BaseObjectManager<T extends BaseModel> {
}
} catch (SQLException error) {
LOGGER.warn("Error refreshing items", error);
+ } finally {
+ writeUnlock();
}
}
}
protected void addNewItem(T item) {
- items.put(item.getId(), item);
+ try {
+ writeLock();
+ items.put(item.getId(), item);
+ } finally {
+ writeUnlock();
+ }
}
public void addItem(T item) throws SQLException {
@@ -92,7 +125,12 @@ public class BaseObjectManager<T extends BaseModel> {
}
protected void updateCachedItem(T item) {
- items.put(item.getId(), item);
+ try {
+ writeLock();
+ items.put(item.getId(), item);
+ } finally {
+ writeUnlock();
+ }
}
public void updateItem(T item) throws SQLException {
@@ -101,7 +139,12 @@ public class BaseObjectManager<T extends BaseModel> {
}
protected void removeCachedItem(long itemId) {
- items.remove(itemId);
+ try {
+ writeLock();
+ items.remove(itemId);
+ } finally {
+ writeUnlock();
+ }
}
public void removeItem(long itemId) throws SQLException {
@@ -121,7 +164,12 @@ public class BaseObjectManager<T extends BaseModel> {
}
public Set<Long> getAllItems() {
- return items.keySet();
+ try {
+ readLock();
+ return items.keySet();
+ } finally {
+ readUnlock();
+ }
}
}
diff --git a/src/main/java/org/traccar/database/CommandsManager.java b/src/main/java/org/traccar/database/CommandsManager.java
index dc9512d9e..de6eeeba8 100644
--- a/src/main/java/org/traccar/database/CommandsManager.java
+++ b/src/main/java/org/traccar/database/CommandsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -142,14 +142,33 @@ public class CommandsManager extends ExtendedObjectManager<Command> {
}
private Queue<Command> getDeviceQueue(long deviceId) {
- if (!deviceQueues.containsKey(deviceId)) {
- deviceQueues.put(deviceId, new ConcurrentLinkedQueue<Command>());
+ Queue<Command> deviceQueue;
+ try {
+ readLock();
+ deviceQueue = deviceQueues.get(deviceId);
+ } finally {
+ readUnlock();
+ }
+ if (deviceQueue != null) {
+ return deviceQueue;
+ } else {
+ try {
+ writeLock();
+ return deviceQueues.computeIfAbsent(deviceId, key -> new ConcurrentLinkedQueue<>());
+ } finally {
+ writeUnlock();
+ }
}
- return deviceQueues.get(deviceId);
}
public void sendQueuedCommands(ActiveDevice activeDevice) {
- Queue<Command> deviceQueue = deviceQueues.get(activeDevice.getDeviceId());
+ Queue<Command> deviceQueue;
+ try {
+ readLock();
+ deviceQueue = deviceQueues.get(activeDevice.getDeviceId());
+ } finally {
+ readUnlock();
+ }
if (deviceQueue != null) {
Command command = deviceQueue.poll();
while (command != null) {
diff --git a/src/main/java/org/traccar/database/DeviceManager.java b/src/main/java/org/traccar/database/DeviceManager.java
index fa95adeb2..fe17f7ced 100644
--- a/src/main/java/org/traccar/database/DeviceManager.java
+++ b/src/main/java/org/traccar/database/DeviceManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -58,11 +58,16 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
public DeviceManager(DataManager dataManager) {
super(dataManager, Device.class);
this.config = Context.getConfig();
- if (devicesByPhone == null) {
- devicesByPhone = new ConcurrentHashMap<>();
- }
- if (devicesByUniqueId == null) {
- devicesByUniqueId = new ConcurrentHashMap<>();
+ try {
+ writeLock();
+ if (devicesByPhone == null) {
+ devicesByPhone = new ConcurrentHashMap<>();
+ }
+ if (devicesByUniqueId == null) {
+ devicesByUniqueId = new ConcurrentHashMap<>();
+ }
+ } finally {
+ writeUnlock();
}
dataRefreshDelay = config.getLong("database.refreshDelay", DEFAULT_REFRESH_DELAY) * 1000;
lookupGroupsAttribute = config.getBoolean("deviceManager.lookupGroupsAttribute");
@@ -108,11 +113,20 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
@Override
public Device getByUniqueId(String uniqueId) throws SQLException {
- boolean forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean("database.ignoreUnknown");
-
+ boolean forceUpdate;
+ try {
+ readLock();
+ forceUpdate = !devicesByUniqueId.containsKey(uniqueId) && !config.getBoolean("database.ignoreUnknown");
+ } finally {
+ readUnlock();
+ }
updateDeviceCache(forceUpdate);
-
- return devicesByUniqueId.get(uniqueId);
+ try {
+ readLock();
+ return devicesByUniqueId.get(uniqueId);
+ } finally {
+ readUnlock();
+ }
}
@Override
@@ -134,7 +148,12 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
}
public Device getDeviceByPhone(String phone) {
- return devicesByPhone.get(phone);
+ try {
+ readLock();
+ return devicesByPhone.get(phone);
+ } finally {
+ readUnlock();
+ }
}
@Override
@@ -176,8 +195,7 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
}
public Set<Long> getAllManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getAllUserItems(userId));
+ Set<Long> result = new HashSet<>(getAllUserItems(userId));
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getAllUserItems(managedUserId));
}
@@ -186,34 +204,68 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = new HashSet<>(getUserItems(userId));
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getUserItems(managedUserId));
}
return result;
}
- private void putUniqueDeviceId(Device device) {
- if (devicesByUniqueId == null) {
- devicesByUniqueId = new ConcurrentHashMap<>(getAllItems().size());
+ private void addByUniqueId(Device device) {
+ try {
+ writeLock();
+ if (devicesByUniqueId == null) {
+ devicesByUniqueId = new ConcurrentHashMap<>();
+ }
+ devicesByUniqueId.put(device.getUniqueId(), device);
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ private void removeByUniqueId(String deviceUniqueId) {
+ try {
+ writeLock();
+ if (devicesByUniqueId != null) {
+ devicesByUniqueId.remove(deviceUniqueId);
+ }
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ private void addByPhone(Device device) {
+ try {
+ writeLock();
+ if (devicesByPhone == null) {
+ devicesByPhone = new ConcurrentHashMap<>();
+ }
+ devicesByPhone.put(device.getPhone(), device);
+ } finally {
+ writeUnlock();
}
- devicesByUniqueId.put(device.getUniqueId(), device);
}
- private void putPhone(Device device) {
- if (devicesByPhone == null) {
- devicesByPhone = new ConcurrentHashMap<>(getAllItems().size());
+ private void removeByPhone(String phone) {
+ if (phone == null || phone.isEmpty()) {
+ return;
+ }
+ try {
+ writeLock();
+ if (devicesByPhone != null) {
+ devicesByPhone.remove(phone);
+ }
+ } finally {
+ writeUnlock();
}
- devicesByPhone.put(device.getPhone(), device);
}
@Override
protected void addNewItem(Device device) {
super.addNewItem(device);
- putUniqueDeviceId(device);
+ addByUniqueId(device);
if (device.getPhone() != null && !device.getPhone().isEmpty()) {
- putPhone(device);
+ addByPhone(device);
}
if (Context.getGeofenceManager() != null) {
Position lastPosition = getLastPosition(device.getId());
@@ -234,18 +286,16 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
cachedDevice.setDisabled(device.getDisabled());
cachedDevice.setAttributes(device.getAttributes());
if (!device.getUniqueId().equals(cachedDevice.getUniqueId())) {
- devicesByUniqueId.remove(cachedDevice.getUniqueId());
+ removeByUniqueId(cachedDevice.getUniqueId());
cachedDevice.setUniqueId(device.getUniqueId());
- putUniqueDeviceId(cachedDevice);
+ addByUniqueId(cachedDevice);
}
if (device.getPhone() != null && !device.getPhone().isEmpty()
&& !device.getPhone().equals(cachedDevice.getPhone())) {
String phone = cachedDevice.getPhone();
- if (phone != null && !phone.isEmpty()) {
- devicesByPhone.remove(phone);
- }
+ removeByPhone(phone);
cachedDevice.setPhone(device.getPhone());
- putPhone(cachedDevice);
+ addByPhone(cachedDevice);
}
}
@@ -256,10 +306,8 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
String deviceUniqueId = cachedDevice.getUniqueId();
String phone = cachedDevice.getPhone();
super.removeCachedItem(deviceId);
- devicesByUniqueId.remove(deviceUniqueId);
- if (phone != null && !phone.isEmpty()) {
- devicesByPhone.remove(phone);
- }
+ removeByUniqueId(deviceUniqueId);
+ removeByPhone(phone);
}
positions.remove(deviceId);
}
diff --git a/src/main/java/org/traccar/database/DriversManager.java b/src/main/java/org/traccar/database/DriversManager.java
index 930951460..d111cd643 100644
--- a/src/main/java/org/traccar/database/DriversManager.java
+++ b/src/main/java/org/traccar/database/DriversManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,22 +27,44 @@ public class DriversManager extends ExtendedObjectManager<Driver> {
public DriversManager(DataManager dataManager) {
super(dataManager, Driver.class);
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>();
+ try {
+ writeLock();
+ if (driversByUniqueId == null) {
+ driversByUniqueId = new ConcurrentHashMap<>();
+ }
+ } finally {
+ writeUnlock();
}
}
- private void putUniqueDriverId(Driver driver) {
- if (driversByUniqueId == null) {
- driversByUniqueId = new ConcurrentHashMap<>(getAllItems().size());
+ private void addByUniqueId(Driver driver) {
+ try {
+ writeLock();
+ if (driversByUniqueId == null) {
+ driversByUniqueId = new ConcurrentHashMap<>();
+ }
+ driversByUniqueId.put(driver.getUniqueId(), driver);
+ } finally {
+ writeUnlock();
+ }
+ }
+
+ private void removeByUniqueId(String driverUniqueId) {
+ try {
+ writeLock();
+ if (driversByUniqueId == null) {
+ driversByUniqueId = new ConcurrentHashMap<>();
+ }
+ driversByUniqueId.remove(driverUniqueId);
+ } finally {
+ writeUnlock();
}
- driversByUniqueId.put(driver.getUniqueId(), driver);
}
@Override
protected void addNewItem(Driver driver) {
super.addNewItem(driver);
- putUniqueDriverId(driver);
+ addByUniqueId(driver);
}
@Override
@@ -50,9 +72,9 @@ public class DriversManager extends ExtendedObjectManager<Driver> {
Driver cachedDriver = getById(driver.getId());
cachedDriver.setName(driver.getName());
if (!driver.getUniqueId().equals(cachedDriver.getUniqueId())) {
- driversByUniqueId.remove(cachedDriver.getUniqueId());
+ removeByUniqueId(cachedDriver.getUniqueId());
cachedDriver.setUniqueId(driver.getUniqueId());
- putUniqueDriverId(cachedDriver);
+ addByUniqueId(cachedDriver);
}
cachedDriver.setAttributes(driver.getAttributes());
}
@@ -63,11 +85,16 @@ public class DriversManager extends ExtendedObjectManager<Driver> {
if (cachedDriver != null) {
String driverUniqueId = cachedDriver.getUniqueId();
super.removeCachedItem(driverId);
- driversByUniqueId.remove(driverUniqueId);
+ removeByUniqueId(driverUniqueId);
}
}
public Driver getDriverByUniqueId(String uniqueId) {
- return driversByUniqueId.get(uniqueId);
+ try {
+ readLock();
+ return driversByUniqueId.get(uniqueId);
+ } finally {
+ readUnlock();
+ }
}
}
diff --git a/src/main/java/org/traccar/database/ExtendedObjectManager.java b/src/main/java/org/traccar/database/ExtendedObjectManager.java
index ceb85b537..93e5820fb 100644
--- a/src/main/java/org/traccar/database/ExtendedObjectManager.java
+++ b/src/main/java/org/traccar/database/ExtendedObjectManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -45,24 +45,45 @@ public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleO
}
public final Set<Long> getGroupItems(long groupId) {
- if (!groupItems.containsKey(groupId)) {
- groupItems.put(groupId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = groupItems.get(groupId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return groupItems.get(groupId);
}
public final Set<Long> getDeviceItems(long deviceId) {
- if (!deviceItems.containsKey(deviceId)) {
- deviceItems.put(deviceId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = deviceItems.get(deviceId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return deviceItems.get(deviceId);
}
public Set<Long> getAllDeviceItems(long deviceId) {
- if (!deviceItemsWithGroups.containsKey(deviceId)) {
- deviceItemsWithGroups.put(deviceId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = deviceItemsWithGroups.get(deviceId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return deviceItemsWithGroups.get(deviceId);
}
@Override
@@ -74,41 +95,48 @@ public abstract class ExtendedObjectManager<T extends BaseModel> extends SimpleO
public void refreshExtendedPermissions() {
if (getDataManager() != null) {
try {
-
Collection<Permission> databaseGroupPermissions =
getDataManager().getPermissions(Group.class, getBaseClass());
- groupItems.clear();
- for (Permission groupPermission : databaseGroupPermissions) {
- getGroupItems(groupPermission.getOwnerId()).add(groupPermission.getPropertyId());
- }
-
Collection<Permission> databaseDevicePermissions =
getDataManager().getPermissions(Device.class, getBaseClass());
+ writeLock();
+
+ groupItems.clear();
deviceItems.clear();
deviceItemsWithGroups.clear();
+ for (Permission groupPermission : databaseGroupPermissions) {
+ groupItems
+ .computeIfAbsent(groupPermission.getOwnerId(), key -> new HashSet<>())
+ .add(groupPermission.getPropertyId());
+ }
+
for (Permission devicePermission : databaseDevicePermissions) {
- getDeviceItems(devicePermission.getOwnerId()).add(devicePermission.getPropertyId());
- getAllDeviceItems(devicePermission.getOwnerId()).add(devicePermission.getPropertyId());
+ deviceItems
+ .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>())
+ .add(devicePermission.getPropertyId());
+ deviceItemsWithGroups
+ .computeIfAbsent(devicePermission.getOwnerId(), key -> new HashSet<>())
+ .add(devicePermission.getPropertyId());
}
for (Device device : Context.getDeviceManager().getAllDevices()) {
long groupId = device.getGroupId();
- while (groupId != 0) {
- getAllDeviceItems(device.getId()).addAll(getGroupItems(groupId));
+ while (groupId > 0) {
+ deviceItemsWithGroups
+ .computeIfAbsent(device.getId(), key -> new HashSet<>())
+ .addAll(groupItems.getOrDefault(groupId, new HashSet<>()));
Group group = Context.getGroupsManager().getById(groupId);
- if (group != null) {
- groupId = group.getGroupId();
- } else {
- groupId = 0;
- }
+ groupId = group != null ? group.getGroupId() : 0;
}
}
} catch (SQLException | ClassNotFoundException error) {
LOGGER.warn("Refresh permissions error", error);
+ } finally {
+ writeUnlock();
}
}
}
diff --git a/src/main/java/org/traccar/database/GroupsManager.java b/src/main/java/org/traccar/database/GroupsManager.java
index d8404c614..81f1968aa 100644
--- a/src/main/java/org/traccar/database/GroupsManager.java
+++ b/src/main/java/org/traccar/database/GroupsManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -95,8 +95,7 @@ public class GroupsManager extends BaseObjectManager<Group> implements Managable
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = getUserItems(userId);
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getUserItems(managedUserId));
}
diff --git a/src/main/java/org/traccar/database/SimpleObjectManager.java b/src/main/java/org/traccar/database/SimpleObjectManager.java
index 15dda4520..eb8284d4e 100644
--- a/src/main/java/org/traccar/database/SimpleObjectManager.java
+++ b/src/main/java/org/traccar/database/SimpleObjectManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -42,16 +42,22 @@ public abstract class SimpleObjectManager<T extends BaseModel> extends BaseObjec
@Override
public final Set<Long> getUserItems(long userId) {
- if (!userItems.containsKey(userId)) {
- userItems.put(userId, new HashSet<Long>());
+ try {
+ readLock();
+ Set<Long> result = userItems.get(userId);
+ if (result != null) {
+ return new HashSet<>(result);
+ } else {
+ return new HashSet<>();
+ }
+ } finally {
+ readUnlock();
}
- return userItems.get(userId);
}
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = getUserItems(userId);
for (long managedUserId : Context.getUsersManager().getUserItems(userId)) {
result.addAll(getUserItems(managedUserId));
}
@@ -71,16 +77,16 @@ public abstract class SimpleObjectManager<T extends BaseModel> extends BaseObjec
public final void refreshUserItems() {
if (getDataManager() != null) {
try {
- if (userItems != null) {
- userItems.clear();
- } else {
- userItems = new ConcurrentHashMap<>();
- }
+ writeLock();
+ userItems = new ConcurrentHashMap<>();
for (Permission permission : getDataManager().getPermissions(User.class, getBaseClass())) {
- getUserItems(permission.getOwnerId()).add(permission.getPropertyId());
+ Set<Long> items = userItems.computeIfAbsent(permission.getOwnerId(), key -> new HashSet<>());
+ items.add(permission.getPropertyId());
}
} catch (SQLException | ClassNotFoundException error) {
LOGGER.warn("Error getting permissions", error);
+ } finally {
+ writeUnlock();
}
}
}
diff --git a/src/main/java/org/traccar/database/UsersManager.java b/src/main/java/org/traccar/database/UsersManager.java
index 576a9e6c7..b741a85b6 100644
--- a/src/main/java/org/traccar/database/UsersManager.java
+++ b/src/main/java/org/traccar/database/UsersManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,6 @@
*/
package org.traccar.database;
-import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -73,8 +72,7 @@ public class UsersManager extends SimpleObjectManager<User> {
@Override
public Set<Long> getManagedItems(long userId) {
- Set<Long> result = new HashSet<>();
- result.addAll(getUserItems(userId));
+ Set<Long> result = getUserItems(userId);
result.add(userId);
return result;
}
diff --git a/src/main/java/org/traccar/geocoder/FactualGeocoder.java b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
index c7a68c293..f540eb8fe 100644
--- a/src/main/java/org/traccar/geocoder/FactualGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/FactualGeocoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,8 +20,16 @@ import javax.json.JsonObject;
public class FactualGeocoder extends JsonGeocoder {
+ private static String formatUrl(String url, String key) {
+ if (url == null) {
+ url = "https://api.factual.com/geotag";
+ }
+ url += "?latitude=%f&longitude=%f&KEY=" + key;
+ return url;
+ }
+
public FactualGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(url + "?latitude=%f&longitude=%f&KEY=" + key, cacheSize, addressFormat);
+ super(formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
index 3a173f985..b4881a006 100644
--- a/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/GisgraphyGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,12 +19,16 @@ import javax.json.JsonObject;
public class GisgraphyGeocoder extends JsonGeocoder {
- public GisgraphyGeocoder(AddressFormat addressFormat) {
- this("http://services.gisgraphy.com/reversegeocoding/search", 0, addressFormat);
+ private static String formatUrl(String url) {
+ if (url == null) {
+ url = "http://services.gisgraphy.com/reversegeocoding/search";
+ }
+ url += "?format=json&lat=%f&lng=%f&from=1&to=1";
+ return url;
}
public GisgraphyGeocoder(String url, int cacheSize, AddressFormat addressFormat) {
- super(url + "?format=json&lat=%f&lng=%f&from=1&to=1", cacheSize, addressFormat);
+ super(formatUrl(url), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/HereGeocoder.java b/src/main/java/org/traccar/geocoder/HereGeocoder.java
index 756260b52..aaf11d74d 100644
--- a/src/main/java/org/traccar/geocoder/HereGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/HereGeocoder.java
@@ -19,20 +19,24 @@ import javax.json.JsonObject;
public class HereGeocoder extends JsonGeocoder {
- private static String formatUrl(String id, String key, String language) {
- String url = "https://reverse.geocoder.api.here.com/6.2/reversegeocode.json";
+ private static String formatUrl(String url, String id, String key, String language) {
+ if (url == null) {
+ url = "https://reverse.geocoder.ls.hereapi.com/6.2/reversegeocode.json";
+ }
url += "?mode=retrieveAddresses&maxresults=1";
url += "&prox=%f,%f,0";
url += "&app_id=" + id;
url += "&app_code=" + key;
+ url += "&apiKey=" + key;
if (language != null) {
url += "&language=" + language;
}
return url;
}
- public HereGeocoder(String id, String key, String language, int cacheSize, AddressFormat addressFormat) {
- super(formatUrl(id, key, language), cacheSize, addressFormat);
+ public HereGeocoder(
+ String url, String id, String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(formatUrl(url, id, key, language), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
index 4029e3f07..8dc3f76f0 100644
--- a/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/MapQuestGeocoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,16 @@ import javax.json.JsonObject;
public class MapQuestGeocoder extends JsonGeocoder {
+ private static String formatUrl(String url, String key) {
+ if (url == null) {
+ url = "http://www.mapquestapi.com/geocoding/v1/reverse";
+ }
+ url += "?key=" + key + "&location=%f,%f";
+ return url;
+ }
+
public MapQuestGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(url + "?key=" + key + "&location=%f,%f", cacheSize, addressFormat);
+ super(formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
index 822b6e91e..56161e52c 100644
--- a/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
+++ b/src/main/java/org/traccar/geocoder/OpenCageGeocoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,16 @@ import javax.json.JsonObject;
public class OpenCageGeocoder extends JsonGeocoder {
+ private static String formatUrl(String url, String key) {
+ if (url == null) {
+ url = "https://api.opencagedata.com/geocode/v1";
+ }
+ url += "/json?q=%f,%f&no_annotations=1&key=" + key;
+ return url;
+ }
+
public OpenCageGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
- super(url + "/json?q=%f,%f&no_annotations=1&key=" + key, cacheSize, addressFormat);
+ super(formatUrl(url, key), cacheSize, addressFormat);
}
@Override
diff --git a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
index 768aaf6a2..cb3094e16 100644
--- a/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
+++ b/src/main/java/org/traccar/geolocation/OpenCellIdGeolocationProvider.java
@@ -26,11 +26,10 @@ public class OpenCellIdGeolocationProvider implements GeolocationProvider {
private String url;
- public OpenCellIdGeolocationProvider(String key) {
- this("http://opencellid.org/cell/get", key);
- }
-
public OpenCellIdGeolocationProvider(String url, String key) {
+ if (url == null) {
+ url = "http://opencellid.org/cell/get";
+ }
this.url = url + "?format=json&mcc=%d&mnc=%d&lac=%d&cellid=%d&key=" + key;
}
diff --git a/src/main/java/org/traccar/helper/Checksum.java b/src/main/java/org/traccar/helper/Checksum.java
index adfa697c5..d41dc2992 100644
--- a/src/main/java/org/traccar/helper/Checksum.java
+++ b/src/main/java/org/traccar/helper/Checksum.java
@@ -168,6 +168,14 @@ public final class Checksum {
return checksum;
}
+ public static int modulo256(ByteBuffer buf) {
+ int checksum = 0;
+ while (buf.hasRemaining()) {
+ checksum = (checksum + buf.get()) & 0xFF;
+ }
+ return checksum;
+ }
+
public static String sum(String msg) {
byte checksum = 0;
for (byte b : msg.getBytes(StandardCharsets.US_ASCII)) {
diff --git a/src/main/java/org/traccar/helper/LogAction.java b/src/main/java/org/traccar/helper/LogAction.java
index db13337b8..16d55ec60 100644
--- a/src/main/java/org/traccar/helper/LogAction.java
+++ b/src/main/java/org/traccar/helper/LogAction.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
* Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,6 +44,7 @@ public final class LogAction {
private static final String PATTERN_OBJECT = "user: %d, action: %s, object: %s, id: %d";
private static final String PATTERN_LINK = "user: %d, action: %s, owner: %s, id: %d, property: %s, id: %d";
private static final String PATTERN_LOGIN = "user: %d, action: %s";
+ private static final String PATTERN_LOGIN_FAILED = "login failed from: %s";
private static final String PATTERN_DEVICE_ACCUMULATORS = "user: %d, action: %s, deviceId: %d";
public static void create(long userId, BaseModel object) {
@@ -74,6 +75,13 @@ public final class LogAction {
logLoginAction(ACTION_LOGOUT, userId);
}
+ public static void failedLogin(String remoteAddress) {
+ if (remoteAddress == null || remoteAddress.isEmpty()) {
+ remoteAddress = "unknown";
+ }
+ LOGGER.info(String.format(PATTERN_LOGIN_FAILED, remoteAddress));
+ }
+
public static void resetDeviceAccumulators(long userId, long deviceId) {
LOGGER.info(String.format(
PATTERN_DEVICE_ACCUMULATORS, userId, ACTION_DEVICE_ACCUMULATORS, deviceId));
@@ -85,7 +93,7 @@ public final class LogAction {
}
private static void logLinkAction(String action, long userId,
- Class<?> owner, long ownerId, Class<?> property, long propertyId) {
+ Class<?> owner, long ownerId, Class<?> property, long propertyId) {
LOGGER.info(String.format(
PATTERN_LINK, userId, action,
Introspector.decapitalize(owner.getSimpleName()), ownerId,
diff --git a/src/main/java/org/traccar/helper/ServletHelper.java b/src/main/java/org/traccar/helper/ServletHelper.java
new file mode 100644
index 000000000..b6c587ec3
--- /dev/null
+++ b/src/main/java/org/traccar/helper/ServletHelper.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.helper;
+
+import javax.servlet.http.HttpServletRequest;
+
+public final class ServletHelper {
+
+ private ServletHelper() {
+ }
+
+ public static String retrieveRemoteAddress(HttpServletRequest request) {
+
+ if (request != null) {
+ String remoteAddress = request.getHeader("X-FORWARDED-FOR");
+
+ if (remoteAddress != null && !remoteAddress.isEmpty()) {
+ int separatorIndex = remoteAddress.indexOf(",");
+ if (separatorIndex > 0) {
+ return remoteAddress.substring(0, separatorIndex); // remove the additional data
+ } else {
+ return remoteAddress;
+ }
+ } else {
+ return request.getRemoteAddr();
+ }
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/model/Command.java b/src/main/java/org/traccar/model/Command.java
index 336fc61f4..abe538a10 100644
--- a/src/main/java/org/traccar/model/Command.java
+++ b/src/main/java/org/traccar/model/Command.java
@@ -68,6 +68,7 @@ public class Command extends Message implements Cloneable {
public static final String KEY_UNIQUE_ID = "uniqueId";
public static final String KEY_FREQUENCY = "frequency";
+ public static final String KEY_LANGUAGE = "language";
public static final String KEY_TIMEZONE = "timezone";
public static final String KEY_DEVICE_PASSWORD = "devicePassword";
public static final String KEY_RADIUS = "radius";
diff --git a/src/main/java/org/traccar/protocol/AdmProtocolEncoder.java b/src/main/java/org/traccar/protocol/AdmProtocolEncoder.java
index 1c3dfc156..c02fa4112 100644
--- a/src/main/java/org/traccar/protocol/AdmProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/AdmProtocolEncoder.java
@@ -34,7 +34,7 @@ public class AdmProtocolEncoder extends StringProtocolEncoder {
return formatCommand(command, "STATUS\r\n");
case Command.TYPE_CUSTOM:
- return formatCommand(command, "{%s}\r\n", Command.KEY_DATA);
+ return formatCommand(command, "%s\r\n", Command.KEY_DATA);
default:
return null;
diff --git a/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java
new file mode 100644
index 000000000..e957a6911
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArnaviBinaryProtocolDecoder.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Ivan Muratov (binakot@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ArnaviBinaryProtocolDecoder extends BaseProtocolDecoder {
+
+ private static final byte HEADER_START_SIGN = (byte) 0xff;
+ private static final byte HEADER_VERSION_1 = 0x22;
+ private static final byte HEADER_VERSION_2 = 0x23;
+
+ private static final byte RECORD_PING = 0x00;
+ private static final byte RECORD_DATA = 0x01;
+ private static final byte RECORD_TEXT = 0x03;
+ private static final byte RECORD_FILE = 0x04;
+ private static final byte RECORD_BINARY = 0x06;
+
+ private static final byte TAG_LATITUDE = 3;
+ private static final byte TAG_LONGITUDE = 4;
+ private static final byte TAG_COORD_PARAMS = 5;
+
+ public ArnaviBinaryProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private void sendResponse(Channel channel, byte version, int index) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0x7b);
+ if (version == HEADER_VERSION_1) {
+ response.writeByte(0x00);
+ response.writeByte((byte) index);
+ } else if (version == HEADER_VERSION_2) {
+ response.writeByte(0x04);
+ response.writeByte(0x00);
+ ByteBuffer time = ByteBuffer.allocate(4).putInt((int) (System.currentTimeMillis() / 1000));
+ ((Buffer) time).position(0);
+ response.writeByte(Checksum.modulo256(time.slice()));
+ response.writeBytes(time);
+ }
+ response.writeByte(0x7d);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private Position decodePosition(DeviceSession deviceSession, ByteBuf buf, int length, Date time) {
+
+ final Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(time);
+
+ int readBytes = 0;
+ while (readBytes < length) {
+ short tag = buf.readUnsignedByte();
+ switch (tag) {
+ case TAG_LATITUDE:
+ position.setLatitude(buf.readFloatLE());
+ position.setValid(true);
+ break;
+
+ case TAG_LONGITUDE:
+ position.setLongitude(buf.readFloatLE());
+ position.setValid(true);
+ break;
+
+ case TAG_COORD_PARAMS:
+ position.setCourse(buf.readUnsignedByte() * 2);
+ position.setAltitude(buf.readUnsignedByte() * 10);
+ byte satellites = buf.readByte();
+ position.set(Position.KEY_SATELLITES, satellites & 0x0F + (satellites >> 4) & 0x0F);
+ position.setSpeed(buf.readUnsignedByte());
+ break;
+
+ default:
+ buf.skipBytes(4);
+ break;
+ }
+
+ readBytes += 1 + 4;
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ byte startSign = buf.readByte();
+
+ if (startSign == HEADER_START_SIGN) {
+
+ byte version = buf.readByte();
+
+ String imei = String.valueOf(buf.readLongLE());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+
+ if (deviceSession != null) {
+ sendResponse(channel, version, 0);
+ }
+
+ return null;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ int index = buf.readUnsignedByte();
+
+ byte recordType = buf.readByte();
+ while (buf.readableBytes() > 0) {
+ switch (recordType) {
+ case RECORD_PING:
+ case RECORD_DATA:
+ case RECORD_TEXT:
+ case RECORD_FILE:
+ case RECORD_BINARY:
+ int length = buf.readUnsignedShortLE();
+ Date time = new Date(buf.readUnsignedIntLE() * 1000);
+
+ if (recordType == RECORD_DATA) {
+ positions.add(decodePosition(deviceSession, buf, length, time));
+ } else {
+ buf.readBytes(length);
+ }
+
+ buf.readUnsignedByte(); // checksum
+ break;
+
+ default:
+ return null;
+ }
+
+ recordType = buf.readByte();
+ }
+
+ sendResponse(channel, HEADER_VERSION_1, index);
+
+ return positions;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java b/src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java
new file mode 100644
index 000000000..473e8b2c7
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArnaviFrameDecoder.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Ivan Muratov (binakot@gmail.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+import org.traccar.helper.BufferUtil;
+
+public class ArnaviFrameDecoder extends BaseFrameDecoder {
+
+ private static final int HEADER_LENGTH = 10;
+ private static final int PACKET_WRAPPER_LENGTH = 8;
+ private static final int RESULT_TYPE = 0xfd;
+ private static final byte PACKAGE_END_SIGN = 0x5d;
+
+ private boolean firstPacket = true;
+
+ @Override
+ protected Object decode(ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 4) {
+ return null;
+ }
+
+ if (buf.getByte(buf.readerIndex()) == '$') {
+
+ int index = BufferUtil.indexOf("\r\n", buf);
+ if (index > 0) {
+ ByteBuf frame = buf.readRetainedSlice(index - buf.readerIndex());
+ buf.skipBytes(2);
+ return frame;
+ }
+
+ } else {
+
+ int length;
+ if (firstPacket) {
+ firstPacket = false;
+ length = HEADER_LENGTH;
+ } else {
+ int type = buf.getUnsignedByte(1);
+ if (type == RESULT_TYPE) {
+ length = 4;
+ } else {
+ int index = 2;
+ while (index + PACKET_WRAPPER_LENGTH < buf.readableBytes()
+ && buf.getByte(index) != PACKAGE_END_SIGN) {
+ index += PACKET_WRAPPER_LENGTH + buf.getUnsignedShortLE(index + 1);
+ }
+ if (buf.getByte(index) != PACKAGE_END_SIGN) {
+ return null;
+ }
+ length = index + 1;
+ }
+ }
+
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocol.java b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
index afe491865..aecb42c8c 100644
--- a/src/main/java/org/traccar/protocol/ArnaviProtocol.java
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,6 @@
*/
package org.traccar.protocol;
-import io.netty.handler.codec.LineBasedFrameDecoder;
-import io.netty.handler.codec.string.StringDecoder;
-import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
@@ -28,9 +25,7 @@ public class ArnaviProtocol extends BaseProtocol {
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
- pipeline.addLast(new LineBasedFrameDecoder(1024));
- pipeline.addLast(new StringDecoder());
- pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new ArnaviFrameDecoder());
pipeline.addLast(new ArnaviProtocolDecoder(ArnaviProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
index 7996cf429..68a70c944 100644
--- a/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ArnaviProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,91 +15,35 @@
*/
package org.traccar.protocol;
+import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
-import org.traccar.DeviceSession;
import org.traccar.Protocol;
-import org.traccar.helper.DateBuilder;
-import org.traccar.helper.Parser;
-import org.traccar.helper.PatternBuilder;
-import org.traccar.model.Position;
import java.net.SocketAddress;
-import java.util.regex.Pattern;
public class ArnaviProtocolDecoder extends BaseProtocolDecoder {
+ private final ArnaviTextProtocolDecoder textProtocolDecoder;
+ private final ArnaviBinaryProtocolDecoder binaryProtocolDecoder;
+
public ArnaviProtocolDecoder(Protocol protocol) {
super(protocol);
+ textProtocolDecoder = new ArnaviTextProtocolDecoder(protocol);
+ binaryProtocolDecoder = new ArnaviBinaryProtocolDecoder(protocol);
}
- private static final Pattern PATTERN = new PatternBuilder()
- .text("$AV,")
- .number("Vd,") // type
- .number("(d+),") // device id
- .number("(d+),") // index
- .number("(d+),") // power
- .number("(d+),") // battery
- .number("-?d+,")
- .expression("[01],") // movement
- .expression("([01]),") // ignition
- .number("(d+),") // input
- .number("d+,d+,") // input 1
- .number("d+,d+,").optional() // input 2
- .expression("[01],") // fix type
- .number("(d+),") // satellites
- .groupBegin()
- .number("(d+.d+)?,") // altitude
- .number("(?:d+.d+)?,") // geoid height
- .groupEnd("?")
- .number("(dd)(dd)(dd),") // time (hhmmss)
- .number("(dd)(dd.d+)([NS]),") // latitude
- .number("(ddd)(dd.d+)([EW]),") // longitude
- .number("(d+.d+),") // speed
- .number("(d+.d+),") // course
- .number("(dd)(dd)(dd)") // date (ddmmyy)
- .any()
- .compile();
-
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- Parser parser = new Parser(PATTERN, (String) msg);
- if (!parser.matches()) {
- return null;
- }
+ ByteBuf buf = (ByteBuf) msg;
- Position position = new Position(getProtocolName());
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
- if (deviceSession == null) {
- return null;
+ if (buf.getByte(buf.readerIndex()) == '$') {
+ return textProtocolDecoder.decode(channel, remoteAddress, msg);
+ } else {
+ return binaryProtocolDecoder.decode(channel, remoteAddress, msg);
}
- position.setDeviceId(deviceSession.getDeviceId());
-
- position.set(Position.KEY_INDEX, parser.nextInt());
- position.set(Position.KEY_POWER, parser.nextInt() * 0.01);
- position.set(Position.KEY_BATTERY, parser.nextInt() * 0.01);
- position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
- position.set(Position.KEY_INPUT, parser.nextInt());
- position.set(Position.KEY_SATELLITES, parser.nextInt());
-
- position.setAltitude(parser.nextDouble(0));
-
- DateBuilder dateBuilder = new DateBuilder()
- .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
-
- position.setValid(true);
- position.setLatitude(parser.nextCoordinate());
- position.setLongitude(parser.nextCoordinate());
- position.setSpeed(parser.nextDouble());
- position.setCourse(parser.nextDouble());
-
- dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
- position.setTime(dateBuilder.getDate());
-
- return position;
}
}
diff --git a/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java b/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java
new file mode 100644
index 000000000..b99869e6e
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/ArnaviTextProtocolDecoder.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Pattern;
+
+public class ArnaviTextProtocolDecoder extends BaseProtocolDecoder {
+
+ public ArnaviTextProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("$AV,")
+ .number("Vd,") // type
+ .number("(d+),") // device id
+ .number("(d+),") // index
+ .number("(d+),") // power
+ .number("(d+),") // battery
+ .number("-?d+,")
+ .expression("[01],") // movement
+ .expression("([01]),") // ignition
+ .number("(d+),") // input
+ .number("d+,d+,") // input 1
+ .number("d+,d+,").optional() // input 2
+ .expression("[01],") // fix type
+ .number("(d+),") // satellites
+ .groupBegin()
+ .number("(d+.d+)?,") // altitude
+ .number("(?:d+.d+)?,") // geoid height
+ .groupEnd("?")
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("(dd)(dd.d+)([NS]),") // latitude
+ .number("(ddd)(dd.d+)([EW]),") // longitude
+ .number("(d+.d+),") // speed
+ .number("(d+.d+),") // course
+ .number("(dd)(dd)(dd)") // date (ddmmyy)
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+ Parser parser = new Parser(PATTERN, buf.toString(StandardCharsets.US_ASCII));
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.set(Position.KEY_INDEX, parser.nextInt());
+ position.set(Position.KEY_POWER, parser.nextInt() * 0.01);
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.01);
+ position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
+ position.set(Position.KEY_INPUT, parser.nextInt());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+
+ position.setAltitude(parser.nextDouble(0));
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+
+ position.setValid(true);
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/BceProtocolDecoder.java b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
index f07338937..54136382c 100644
--- a/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/BceProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
@@ -82,10 +83,10 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
buf.skipBytes(4);
}
if (BitUtil.check(mask, 12)) {
- buf.skipBytes(2);
+ position.set("fuel1", buf.readUnsignedShort());
}
if (BitUtil.check(mask, 13)) {
- buf.skipBytes(2);
+ position.set("fuel2", buf.readUnsignedShort());
}
if (BitUtil.check(mask, 14)) {
@@ -97,7 +98,7 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
}
}
- private void decodeMask2(ByteBuf buf, int mask) {
+ private void decodeMask2(ByteBuf buf, int mask, Position position) {
if (BitUtil.check(mask, 0)) {
buf.readUnsignedShortLE(); // wheel speed
@@ -106,31 +107,31 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // acceleration pedal
}
if (BitUtil.check(mask, 2)) {
- buf.readUnsignedIntLE(); // total fuel used
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedIntLE());
}
if (BitUtil.check(mask, 3)) {
- buf.readUnsignedByte(); // fuel level
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte());
}
if (BitUtil.check(mask, 4)) {
- buf.readUnsignedShortLE(); // engine speed
+ position.set(Position.KEY_RPM, buf.readUnsignedShortLE() * 0.0125);
}
if (BitUtil.check(mask, 5)) {
- buf.readUnsignedIntLE(); // total hours
+ position.set(Position.KEY_HOURS, buf.readUnsignedIntLE());
}
if (BitUtil.check(mask, 6)) {
- buf.readUnsignedIntLE(); // total distance
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
}
if (BitUtil.check(mask, 7)) {
- buf.readUnsignedByte(); // engine coolant
+ position.set(Position.KEY_COOLANT_TEMP, buf.readByte() - 40);
}
if (BitUtil.check(mask, 8)) {
- buf.readUnsignedByte(); // fuel level 2
+ position.set("fuel2", buf.readUnsignedByte());
}
if (BitUtil.check(mask, 9)) {
- buf.readUnsignedByte(); // engine load
+ position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte());
}
if (BitUtil.check(mask, 10)) {
- buf.readUnsignedShortLE(); // service distance
+ position.set(Position.KEY_ODOMETER_SERVICE, buf.readUnsignedShortLE());
}
if (BitUtil.check(mask, 11)) {
buf.skipBytes(8); // sensors
@@ -142,7 +143,7 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
buf.skipBytes(8); // trailer id
}
if (BitUtil.check(mask, 14)) {
- buf.readUnsignedShortLE(); // fuel rate
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShortLE());
}
}
@@ -152,10 +153,10 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedShortLE(); // fuel economy
}
if (BitUtil.check(mask, 1)) {
- buf.readUnsignedIntLE(); // fuel consumption
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedIntLE());
}
if (BitUtil.check(mask, 2)) {
- buf.readUnsignedMediumLE(); // axle weight
+ position.set(Position.KEY_AXLE_WEIGHT, buf.readUnsignedMediumLE());
}
if (BitUtil.check(mask, 3)) {
buf.readUnsignedByte(); // mil status
@@ -169,6 +170,70 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(mask, 6)) {
position.set(Position.KEY_DRIVER_UNIQUE_ID, String.valueOf(buf.readLongLE()));
}
+ if (BitUtil.check(mask, 7)) {
+ buf.readUnsignedShortLE(); // dallas temperature
+ }
+ if (BitUtil.check(mask, 8)) {
+ buf.readUnsignedShortLE(); // dallas humidity
+ }
+ if (BitUtil.check(mask, 9)) {
+ buf.skipBytes(6); // lls group 1
+ }
+ if (BitUtil.check(mask, 10)) {
+ buf.skipBytes(6); // lls group 2
+ }
+ if (BitUtil.check(mask, 11)) {
+ buf.skipBytes(21); // j1979 group 1
+ }
+ if (BitUtil.check(mask, 12)) {
+ buf.skipBytes(20); // j1979 dtc
+ }
+ if (BitUtil.check(mask, 13)) {
+ buf.skipBytes(9); // j1708 group 1
+ }
+ if (BitUtil.check(mask, 14)) {
+ buf.skipBytes(21); // driving quality
+ }
+ }
+
+ private void decodeMask4(ByteBuf buf, int mask, Position position) {
+
+ if (BitUtil.check(mask, 0)) {
+ buf.readUnsignedIntLE();
+ }
+ if (BitUtil.check(mask, 1)) {
+ buf.skipBytes(30); // lls group 3
+ }
+ if (BitUtil.check(mask, 2)) {
+ buf.readUnsignedIntLE(); // instant fuel consumption
+ }
+ if (BitUtil.check(mask, 3)) {
+ buf.skipBytes(10); // axle weight group
+ }
+ if (BitUtil.check(mask, 4)) {
+ buf.readUnsignedByte();
+ }
+ if (BitUtil.check(mask, 5)) {
+ buf.readUnsignedShortLE();
+ }
+ if (BitUtil.check(mask, 6)) {
+ position.set("maxAcceleration", buf.readUnsignedByte());
+ position.set("maxBraking", buf.readUnsignedByte());
+ position.set("maxCornering", buf.readUnsignedByte());
+ }
+ if (BitUtil.check(mask, 7)) {
+ buf.skipBytes(16);
+ }
+ if (BitUtil.check(mask, 8)) {
+ buf.skipBytes(40); // temperature sensors
+ }
+ if (BitUtil.check(mask, 9)) {
+ position.set("driver1", buf.readCharSequence(16, StandardCharsets.US_ASCII).toString().trim());
+ position.set("driver2", buf.readCharSequence(16, StandardCharsets.US_ASCII).toString().trim());
+ }
+ if (BitUtil.check(mask, 10)) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ }
}
@Override
@@ -223,13 +288,18 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
if (masks.size() >= 2) {
mask = masks.get(1);
- decodeMask2(buf, mask);
+ decodeMask2(buf, mask, position);
}
if (masks.size() >= 3) {
mask = masks.get(2);
decodeMask3(buf, mask, position);
}
+
+ if (masks.size() >= 4) {
+ mask = masks.get(3);
+ decodeMask4(buf, mask, position);
+ }
}
buf.readerIndex(structEnd);
diff --git a/src/main/java/org/traccar/protocol/BlueProtocol.java b/src/main/java/org/traccar/protocol/BlueProtocol.java
new file mode 100644
index 000000000..d5dc5c421
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BlueProtocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 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.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class BlueProtocol extends BaseProtocol {
+
+ public BlueProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 1, 2, -2, 0));
+ pipeline.addLast(new BlueProtocolDecoder(BlueProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java b/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java
new file mode 100644
index 000000000..f35ac6fbe
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/BlueProtocolDecoder.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class BlueProtocolDecoder extends BaseProtocolDecoder {
+
+ public BlueProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private double readCoordinate(ByteBuf buf, boolean negative) {
+
+ int value = buf.readUnsignedShort();
+ int degrees = value / 100;
+ double minutes = value % 100 + buf.readUnsignedShort() * 0.0001;
+ double coordinate = degrees + minutes / 60;
+ return negative ? -coordinate : coordinate;
+ }
+
+ private void sendResponse(Channel channel, int deviceIndex) {
+ if (channel != null) {
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0xaa);
+ response.writeShort(2 + 1 + 1 + 6 + 1);
+ response.writeByte(0x86); // version
+ response.writeByte(0);
+
+ response.writeByte(6); // data length
+ response.writeByte(0xa4); // type
+ response.writeByte(0); // server index
+ response.writeByte(deviceIndex);
+ response.writeByte(0);
+ response.writeByte(0);
+
+ response.writeByte(Checksum.xor(response.nioBuffer(1, response.writerIndex() - 1)));
+
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private String decodeAlarm(int value) {
+ switch (value) {
+ case 1:
+ return Position.ALARM_SOS;
+ case 8:
+ return Position.ALARM_OVERSPEED;
+ case 19:
+ return Position.ALARM_LOW_POWER;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // header
+ buf.readUnsignedShort(); // length
+ buf.readUnsignedByte(); // version
+ buf.readUnsignedByte();
+
+ String id = String.valueOf(buf.readUnsignedInt());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ while (buf.readableBytes() > 1) {
+
+ int frameEnd = buf.readerIndex() + buf.readUnsignedByte();
+
+ int type = buf.readUnsignedByte();
+ int index = buf.readUnsignedByte();
+ buf.readUnsignedByte();
+ buf.readUnsignedByte(); // flags
+
+ if (type == 0x01) {
+
+ buf.readUnsignedByte(); // reserved
+ int flags = buf.readUnsignedByte();
+
+ position.setValid(BitUtil.check(flags, 7));
+ position.setLatitude(readCoordinate(buf, BitUtil.check(flags, 6)));
+ position.setLongitude(readCoordinate(buf, BitUtil.check(flags, 5)));
+ position.setSpeed(buf.readUnsignedShort() + buf.readUnsignedShort() * 0.001);
+ position.setCourse(buf.readUnsignedShort() + buf.readUnsignedByte() * 0.01);
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+
+ buf.readUnsignedShort(); // lac
+ buf.readUnsignedShort(); // cid
+
+ } else if (type == 0x12) {
+
+ int status;
+
+ status = buf.readUnsignedByte(); // status 1
+ position.set(Position.KEY_ALARM, BitUtil.check(status, 1) ? Position.ALARM_VIBRATION : null);
+
+ buf.readUnsignedByte(); // status 2
+ buf.readUnsignedByte(); // status 3
+
+ status = buf.readUnsignedByte(); // status 4
+ int ignition = BitUtil.between(status, 2, 4);
+ if (ignition == 0b01) {
+ position.set(Position.KEY_IGNITION, false);
+ }
+ if (ignition == 0b10) {
+ position.set(Position.KEY_IGNITION, true);
+ }
+
+ buf.readUnsignedByte(); // status 5
+ buf.readUnsignedByte(); // status 6
+
+ position.set(Position.KEY_STATUS, buf.readUnsignedShort());
+
+ } else if (type == 0x81) {
+
+ position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte()));
+
+ } else if (type == 0x84) {
+
+ sendResponse(channel, index);
+
+ }
+
+ buf.readerIndex(frameEnd);
+ }
+
+ return position.getFixTime() != null ? position : null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java b/src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java
index 083fe9a9e..78dbe7e91 100644
--- a/src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/CarcellProtocolEncoder.java
@@ -30,9 +30,9 @@ public class CarcellProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "$SRVCMD,{%s},BA#\r\n", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "$SRVCMD,%s,BA#\r\n", Command.KEY_UNIQUE_ID);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "$SRVCMD,{%s},BD#\r\n", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "$SRVCMD,%s,BD#\r\n", Command.KEY_UNIQUE_ID);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
index 03e4b25fd..23401b5ee 100644
--- a/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CastelProtocolDecoder.java
@@ -23,6 +23,7 @@ import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.ObdDecoder;
@@ -185,7 +186,14 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedIntLE());
position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedIntLE());
buf.readUnsignedShortLE(); // current fuel consumption
- position.set(Position.KEY_STATUS, buf.readUnsignedIntLE());
+
+ long state = buf.readUnsignedIntLE();
+ position.set(Position.KEY_ALARM, BitUtil.check(state, 4) ? Position.ALARM_ACCELERATION : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(state, 5) ? Position.ALARM_BRAKING : null);
+ position.set(Position.KEY_ALARM, BitUtil.check(state, 6) ? Position.ALARM_IDLE : null);
+ position.set(Position.KEY_IGNITION, BitUtil.check(state, 2 * 8 + 2));
+ position.set(Position.KEY_STATUS, state);
+
buf.skipBytes(8);
}
diff --git a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
index ea4975d3e..aa13a0aa2 100644
--- a/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/CellocatorProtocolDecoder.java
@@ -88,7 +88,7 @@ public class CellocatorProtocolDecoder extends BaseProtocolDecoder {
content.writeByte(0); // ack
content.writeShortLE(0); // reserved
- ByteBuf reply = encodeContent(MSG_SERVER_ACKNOWLEDGE, (int) deviceId, packetNumber, content);
+ ByteBuf reply = encodeContent(MSG_CLIENT_MODULAR_EXT, (int) deviceId, packetNumber, content);
channel.writeAndFlush(new NetworkMessage(reply, remoteAddress));
}
}
diff --git a/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java b/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
index c04e90f1d..58a5a88e3 100644
--- a/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/DmtProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2019 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.
@@ -208,10 +208,21 @@ public class DmtProtocolDecoder extends BaseProtocolDecoder {
position.set("solarPower", buf.readUnsignedShortLE() * 0.001);
break;
default:
+ buf.readUnsignedShortLE(); // other
break;
}
}
+ } else if (fieldId == 26) {
+
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedIntLE());
+ position.set("tripHours", buf.readUnsignedIntLE() * 1000);
+
+ } else if (fieldId == 27) {
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ position.set(Position.KEY_HOURS, buf.readUnsignedIntLE() * 1000);
+
}
buf.readerIndex(fieldEnd);
diff --git a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
index a6fd94b83..41d76f37f 100644
--- a/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EelinkProtocolDecoder.java
@@ -412,7 +412,8 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder {
return decodeNew(deviceSession, buf, type, index);
- } else if (type == MSG_HEARTBEAT && buf.readableBytes() >= 2) {
+ } else if (type == MSG_HEARTBEAT && buf.readableBytes() >= 2
+ || type == MSG_OBD && buf.readableBytes() == 4) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
diff --git a/src/main/java/org/traccar/protocol/EsealProtocolEncoder.java b/src/main/java/org/traccar/protocol/EsealProtocolEncoder.java
index 6ee305ed8..74f9e22ab 100644
--- a/src/main/java/org/traccar/protocol/EsealProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/EsealProtocolEncoder.java
@@ -31,13 +31,13 @@ public class EsealProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_CUSTOM:
return formatCommand(
- command, "##S,eSeal,{%s},256,3.0.8,{%s},E##", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
+ command, "##S,eSeal,%s,256,3.0.8,%s,E##", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
case Command.TYPE_ALARM_ARM:
return formatCommand(
- command, "##S,eSeal,{%s},256,3.0.8,RC-Power Control,Power OFF,E##", Command.KEY_UNIQUE_ID);
+ command, "##S,eSeal,%s,256,3.0.8,RC-Power Control,Power OFF,E##", Command.KEY_UNIQUE_ID);
case Command.TYPE_ALARM_DISARM:
return formatCommand(
- command, "##S,eSeal,{%s},256,3.0.8,RC-Unlock,E##", Command.KEY_UNIQUE_ID);
+ command, "##S,eSeal,%s,256,3.0.8,RC-Unlock,E##", Command.KEY_UNIQUE_ID);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/EskyProtocol.java b/src/main/java/org/traccar/protocol/EskyProtocol.java
index aaa92da58..fb047c207 100644
--- a/src/main/java/org/traccar/protocol/EskyProtocol.java
+++ b/src/main/java/org/traccar/protocol/EskyProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,14 @@ public class EskyProtocol extends BaseProtocol {
pipeline.addLast(new EskyProtocolDecoder(EskyProtocol.this));
}
});
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new EskyProtocolDecoder(EskyProtocol.this));
+ }
+ });
}
}
diff --git a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
index 641b2e28f..d9de110f4 100644
--- a/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/EskyProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,10 @@
package org.traccar.protocol;
import io.netty.channel.Channel;
+import io.netty.channel.socket.DatagramChannel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -35,7 +37,7 @@ public class EskyProtocolDecoder extends BaseProtocolDecoder {
private static final Pattern PATTERN = new PatternBuilder()
.expression("..;") // header
- .number("d+;") // index
+ .number("(d+);") // index
.number("(d+);") // imei
.text("R;") // data type
.number("(d+)[+;]") // satellites
@@ -63,6 +65,11 @@ public class EskyProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ int index = parser.nextInt();
+ if (channel instanceof DatagramChannel) {
+ channel.writeAndFlush(new NetworkMessage("ACK," + index + "#", remoteAddress));
+ }
+
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
index 458e92065..40e146e0b 100644
--- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java
@@ -60,7 +60,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
.number("(-?d+),") // altitude
.number("(d+),") // odometer
.number("d+,") // runtime
- .number("(xxxx),") // status
+ .number("(x{4,8}),") // status
.number("(x+)?,") // input
.number("(x+)?,") // output
.number("(d+)|") // mcc
@@ -151,7 +151,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(parser.nextInt());
position.set(Position.KEY_ODOMETER, parser.nextLong());
- position.set(Position.KEY_STATUS, parser.nextHexInt());
+ position.set(Position.KEY_STATUS, parser.nextHexLong());
position.set(Position.KEY_INPUT, parser.nextHexInt());
position.set(Position.KEY_OUTPUT, parser.nextHexInt());
diff --git a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
index 5b843324c..dfaedd695 100644
--- a/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GalileoProtocolDecoder.java
@@ -318,7 +318,9 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(); // part number
photo.writeBytes(buf, length - 1);
- } else {
+ sendResponse(channel, 0x07, buf.readUnsignedShortLE());
+
+ } else if (photo != null) {
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
String uniqueId = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getUniqueId();
@@ -334,8 +336,6 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
}
- sendResponse(channel, 0x07, buf.readUnsignedShortLE());
-
return position;
}
diff --git a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
index 31500bae6..087861635 100644
--- a/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GatorProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.BcdUtil;
+import org.traccar.helper.Checksum;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
@@ -58,16 +59,16 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
return String.format("%02d%02d%02d%02d%02d", d1, d2, d3, d4, d5);
}
- private void sendResponse(Channel channel, SocketAddress remoteAddress, byte calibration) {
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, int type, int checksum) {
if (channel != null) {
ByteBuf response = Unpooled.buffer();
- response.writeByte(0x24); response.writeByte(0x24); // header
- response.writeByte(MSG_HEARTBEAT); // size
- response.writeShort(5);
- response.writeByte(calibration);
- response.writeByte(0); // main order
- response.writeByte(0); // slave order
- response.writeByte(1); // calibration
+ response.writeShort(0x2424); // header
+ response.writeByte(MSG_HEARTBEAT);
+ response.writeShort(5); // length
+ response.writeByte(checksum);
+ response.writeByte(type);
+ response.writeByte(0); // subtype
+ response.writeByte(Checksum.xor(response.nioBuffer(2, response.writerIndex())));
response.writeByte(0x0D);
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
@@ -87,7 +88,7 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
buf.readUnsignedByte(), buf.readUnsignedByte(),
buf.readUnsignedByte(), buf.readUnsignedByte());
- sendResponse(channel, remoteAddress, buf.getByte(buf.writerIndex() - 2));
+ sendResponse(channel, remoteAddress, type, buf.getByte(buf.writerIndex() - 2));
if (type == MSG_POSITION_DATA || type == MSG_ROLLCALL_RESPONSE
|| type == MSG_ALARM_DATA || type == MSG_BLIND_AREA) {
@@ -120,8 +121,10 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_STATUS, buf.readUnsignedByte());
position.set("key", buf.readUnsignedByte());
- position.set("oil", buf.readUnsignedShort() / 10.0);
- position.set(Position.KEY_POWER, buf.readUnsignedByte() + buf.readUnsignedByte() * 0.01);
+
+ position.set(Position.PREFIX_ADC + 1, buf.readUnsignedByte() + buf.readUnsignedByte() * 0.01);
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedByte() + buf.readUnsignedByte() * 0.01);
+
position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
return position;
diff --git a/src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java
index 32307446b..dd0672c23 100644
--- a/src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Gl200ProtocolEncoder.java
@@ -32,17 +32,17 @@ public class Gl200ProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "AT+GTRTO={%s},1,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "AT+GTRTO=%s,1,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "AT+GTOUT={%s},1,,,0,0,0,0,0,0,0,,,,,,,FFFF$",
+ return formatCommand(command, "AT+GTOUT=%s,1,,,0,0,0,0,0,0,0,,,,,,,FFFF$",
Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "AT+GTOUT={%s},0,,,0,0,0,0,0,0,0,,,,,,,FFFF$",
+ return formatCommand(command, "AT+GTOUT=%s,0,,,0,0,0,0,0,0,0,,,,,,,FFFF$",
Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_IDENTIFICATION:
- return formatCommand(command, "AT+GTRTO={%s},8,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "AT+GTRTO=%s,8,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "AT+GTRTO={%s},3,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "AT+GTRTO=%s,3,,,,,,FFFF$", Command.KEY_DEVICE_PASSWORD);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
index 5739a1224..382509793 100644
--- a/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/GlobalstarProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,28 +17,41 @@ package org.traccar.protocol;
import com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream;
import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
+import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
+import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpVersion;
import org.traccar.BaseHttpProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
import org.traccar.Protocol;
import org.traccar.helper.DataConverter;
import org.traccar.model.Position;
import org.w3c.dom.Document;
+import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.net.SocketAddress;
+import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
@@ -67,6 +80,40 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
}
}
+ private void sendResponse(Channel channel, String messageId) throws TransformerException {
+
+ Document document = documentBuilder.newDocument();
+ Element rootElement = document.createElement("stuResponseMsg");
+ rootElement.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
+ rootElement.setAttribute(
+ "xsi:noNamespaceSchemaLocation", "http://cody.glpconnect.com/XSD/StuResponse_Rev1_0.xsd");
+ rootElement.setAttribute("deliveryTimeStamp", new SimpleDateFormat("dd/MM/yyyy hh:mm:ss z").format(new Date()));
+ rootElement.setAttribute("messageID", "00000000000000000000000000000000");
+ rootElement.setAttribute("correlationID", messageId);
+ document.appendChild(rootElement);
+
+ Element state = document.createElement("state");
+ state.appendChild(document.createTextNode("pass"));
+ rootElement.appendChild(state);
+
+ Element stateMessage = document.createElement("stateMessage");
+ stateMessage.appendChild(document.createTextNode("Messages received and stored successfully"));
+ rootElement.appendChild(stateMessage);
+
+ Transformer transformer = TransformerFactory.newInstance().newTransformer();
+ ByteBuf content = Unpooled.buffer();
+ transformer.transform(new DOMSource(document), new StreamResult(new ByteBufOutputStream(content)));
+
+ if (channel != null) {
+ FullHttpResponse response = new DefaultFullHttpResponse(
+ HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
+ response.headers()
+ .add(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes())
+ .add(HttpHeaderNames.CONTENT_TYPE, "text/xml");
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -112,7 +159,7 @@ public class GlobalstarProtocolDecoder extends BaseHttpProtocolDecoder {
}
}
- sendResponse(channel, HttpResponseStatus.OK);
+ sendResponse(channel, document.getFirstChild().getAttributes().getNamedItem("messageID").getNodeValue());
return positions;
}
diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
index aa02e8ad4..e00d83061 100644
--- a/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -77,7 +77,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
.expression("([EW])?,").optional()
.number("(d+.?d*)?").optional() // speed
.number(",(d+.?d*)?").optional() // course
- .number(",(d+.?d*)?").optional() // altitude
+ .number(",(-?d+.?d*)?").optional() // altitude
.number(",([01])?").optional() // ignition
.number(",([01])?").optional() // door
.groupBegin()
diff --git a/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java
index 7128823ed..e662e9b04 100644
--- a/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Gps103ProtocolEncoder.java
@@ -47,24 +47,24 @@ public class Gps103ProtocolEncoder extends StringProtocolEncoder implements Stri
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return formatCommand(command, "**,imei:{%s},{%s}", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
+ return formatCommand(command, "**,imei:%s,%s", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
case Command.TYPE_POSITION_STOP:
- return formatCommand(command, "**,imei:{%s},A", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,A", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "**,imei:{%s},B", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,B", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_PERIODIC:
return formatCommand(
- command, "**,imei:{%s},C,{%s}", this, Command.KEY_UNIQUE_ID, Command.KEY_FREQUENCY);
+ command, "**,imei:%s,C,%s", this, Command.KEY_UNIQUE_ID, Command.KEY_FREQUENCY);
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "**,imei:{%s},J", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,J", Command.KEY_UNIQUE_ID);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "**,imei:{%s},K", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,K", Command.KEY_UNIQUE_ID);
case Command.TYPE_ALARM_ARM:
- return formatCommand(command, "**,imei:{%s},L", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,L", Command.KEY_UNIQUE_ID);
case Command.TYPE_ALARM_DISARM:
- return formatCommand(command, "**,imei:{%s},M", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,M", Command.KEY_UNIQUE_ID);
case Command.TYPE_REQUEST_PHOTO:
- return formatCommand(command, "**,imei:{%s},160", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "**,imei:%s,160", Command.KEY_UNIQUE_ID);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java b/src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java
index 7dd4b2d77..be0ab5130 100644
--- a/src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java
+++ b/src/main/java/org/traccar/protocol/GranitProtocolSmsEncoder.java
@@ -32,7 +32,7 @@ public class GranitProtocolSmsEncoder extends StringProtocolEncoder {
case Command.TYPE_REBOOT_DEVICE:
return "BB+RESET";
case Command.TYPE_POSITION_PERIODIC:
- return formatCommand(command, "BB+BBMD={%s}", Command.KEY_FREQUENCY);
+ return formatCommand(command, "BB+BBMD=%s", Command.KEY_FREQUENCY);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
index dc7a95955..946652b03 100644
--- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java
@@ -103,6 +103,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
public static final int MSG_MULTIMEDIA = 0x21;
public static final int MSG_BMS_2 = 0x40;
public static final int MSG_MULTIMEDIA_2 = 0x41;
+ public static final int MSG_ALARM = 0x95;
private static boolean isSupported(int type) {
return hasGps(type) || hasLbs(type) || hasStatus(type);
@@ -208,7 +209,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
sendResponse(channel, false, MSG_X1_PHOTO_DATA, 0, content);
}
- private boolean decodeGps(Position position, ByteBuf buf, boolean hasLength, TimeZone timezone) {
+ public static boolean decodeGps(Position position, ByteBuf buf, boolean hasLength, TimeZone timezone) {
DateBuilder dateBuilder = new DateBuilder(timezone)
.setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
@@ -547,6 +548,15 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01);
+ long portInfo = buf.readUnsignedInt();
+
+ position.set(Position.KEY_INPUT, buf.readUnsignedByte());
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
+
+ for (int i = 1; i <= BitUtil.between(portInfo, 20, 24); i++) {
+ position.set(Position.PREFIX_ADC + i, buf.readUnsignedShort() * 0.01);
+ }
+
return position;
} else if (type == MSG_X1_PHOTO_INFO) {
@@ -720,6 +730,14 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
decodeStatus(position, buf);
}
+ if (type == MSG_GPS_LBS_1 && buf.readableBytes() > 75 + 6) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString();
+ buf.readUnsignedByte(); // alarm
+ buf.readUnsignedByte(); // swiped
+ position.set("driverLicense", data.trim());
+ }
+
if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 2 + 6) {
int mask = buf.readUnsignedShort();
position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7));
@@ -750,6 +768,39 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ARCHIVE, buf.readUnsignedByte() > 0);
}
+ } else if (type == MSG_ALARM) {
+
+ DateBuilder dateBuilder = new DateBuilder(deviceSession.getTimeZone())
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+
+ getLastLocation(position, dateBuilder.getDate());
+
+ short alarmType = buf.readUnsignedByte();
+
+ switch (alarmType) {
+ case 0x80:
+ position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
+ break;
+ case 0x87:
+ position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ break;
+ case 0x90:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 0x91:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ case 0x92:
+ position.set(Position.KEY_ALARM, Position.ALARM_CORNERING);
+ break;
+ default:
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ break;
+ }
+
+ position.set("alarmValue", buf.readShort());
+
} else {
if (dataLength > 0) {
@@ -958,7 +1009,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
} else if (type == MSG_GPS_MODULAR) {
- return decodeExtendedModular(channel, buf, deviceSession, type);
+ return decodeExtendedModular(buf, deviceSession);
} else {
@@ -969,7 +1020,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
return null;
}
- private Object decodeExtendedModular(Channel channel, ByteBuf buf, DeviceSession deviceSession, int type) {
+ private Object decodeExtendedModular(ByteBuf buf, DeviceSession deviceSession) {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -977,7 +1028,28 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
while (buf.readableBytes() > 6) {
int moduleType = buf.readUnsignedShort();
int moduleLength = buf.readUnsignedShort();
+
switch (moduleType) {
+ case 0x03:
+ position.set(Position.KEY_ICCID, ByteBufUtil.hexDump(buf.readSlice(10)));
+ break;
+ case 0x09:
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ break;
+ case 0x0a:
+ position.set(Position.KEY_SATELLITES_VISIBLE, buf.readUnsignedByte());
+ break;
+ case 0x11:
+ CellTower cellTower = CellTower.from(
+ buf.readUnsignedShort(),
+ buf.readUnsignedShort(),
+ buf.readUnsignedShort(),
+ buf.readUnsignedMedium(),
+ buf.readUnsignedByte());
+ if (cellTower.getCellId() > 0) {
+ position.setNetwork(new Network(cellTower));
+ }
+ break;
case 0x18:
position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.01);
break;
@@ -988,7 +1060,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_INDEX, buf.readUnsignedInt());
break;
case 0x2a:
- position.set(Position.KEY_INPUT, buf.readUnsignedByte());
+ int input = buf.readUnsignedByte();
+ position.set(Position.KEY_DOOR, BitUtil.to(input, 4) > 0);
+ position.set("tamper", BitUtil.from(input, 4) > 0);
break;
case 0x2b:
int event = buf.readUnsignedByte();
@@ -1036,6 +1110,11 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder {
position.setLatitude(latitude);
position.setLongitude(longitude);
break;
+ case 0x34:
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+ buf.readUnsignedIntLE(); // time
+ buf.skipBytes(buf.readUnsignedByte()); // content
+ break;
default:
buf.skipBytes(moduleLength);
break;
diff --git a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
index 137689a67..320fe991d 100644
--- a/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/H02ProtocolDecoder.java
@@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -185,7 +186,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+)(dd.d+),") // longitude
.groupEnd()
.expression("([EW]),")
- .number("(d+.?d*),") // speed
+ .number(" *(d+.?d*),") // speed
.number("(d+.?d*)?,") // course
.number("(?:d+,)?") // battery
.number("(?:(dd)(dd)(dd))?") // date (ddmmyy)
@@ -223,7 +224,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.number("(d+),") // mnc
.number("d+,") // gsm delay time
.number("d+,") // count
- .number("((?:d+,d+,d+,)+)") // cells
+ .number("((?:d+,d+,-?d+,)+)") // cells
.number("(dd)(dd)(dd),") // date (ddmmyy)
.number("(x{8})") // status
.any()
@@ -287,9 +288,15 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
private void sendResponse(Channel channel, SocketAddress remoteAddress, String id, String type) {
if (channel != null && id != null) {
- DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ String response;
+ DateFormat dateFormat = new SimpleDateFormat(type.equals("R12") ? "HHmmss" : "yyyyMMddHHmmss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- String response = String.format("*HQ,%s,V4,%s,%s#", id, type, dateFormat.format(new Date()));
+ String time = dateFormat.format(new Date());
+ if (type.equals("R12")) {
+ response = String.format("*HQ,%s,%s,%s#", id, type, time);
+ } else {
+ response = String.format("*HQ,%s,V4,%s,%s#", id, type, time);
+ }
channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
}
@@ -316,6 +323,8 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
if (parser.hasNext() && parser.next().equals("V1")) {
sendResponse(channel, remoteAddress, id, "V1");
+ } else if (Context.getConfig().getBoolean(getProtocolName() + ".ack")) {
+ sendResponse(channel, remoteAddress, id, "R12");
}
DateBuilder dateBuilder = new DateBuilder();
diff --git a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
index 9449e2d5c..eac06bbfe 100644
--- a/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuaShengProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,10 @@ import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
@@ -137,6 +140,8 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, buf.readUnsignedShort() * 1000);
+ Network network = new Network();
+
while (buf.readableBytes() > 4) {
int subtype = buf.readUnsignedShort();
int length = buf.readUnsignedShort() - 4;
@@ -161,12 +166,34 @@ public class HuaShengProtocolDecoder extends BaseProtocolDecoder {
position.set(
Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString());
break;
+ case 0x0020:
+ String[] cells = buf.readCharSequence(
+ length, StandardCharsets.US_ASCII).toString().split("\\+");
+ for (String cell : cells) {
+ String[] values = cell.split("@");
+ network.addCellTower(CellTower.from(
+ Integer.parseInt(values[0]), Integer.parseInt(values[1]),
+ Integer.parseInt(values[2], 16), Integer.parseInt(values[3], 16)));
+ }
+ break;
+ case 0x0021:
+ String[] points = buf.readCharSequence(
+ length, StandardCharsets.US_ASCII).toString().split("\\+");
+ for (String point : points) {
+ String[] values = point.split("@");
+ network.addWifiAccessPoint(WifiAccessPoint.from(values[0], Integer.parseInt(values[1])));
+ }
+ break;
default:
buf.skipBytes(length);
break;
}
}
+ if (network.getCellTowers() != null || network.getWifiAccessPoints() != null) {
+ position.setNetwork(network);
+ }
+
sendResponse(channel, MSG_POSITION_RSP, index, null);
return position;
diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
index 6e2e1377b..bf5b6d89a 100644
--- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java
@@ -58,7 +58,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
buf.writeShort(type);
buf.writeShort(data.readableBytes());
buf.writeBytes(id);
- buf.writeShort(1); // index
+ buf.writeShort(0); // index
buf.writeBytes(data);
data.release();
buf.writeByte(Checksum.xor(buf.nioBuffer(1, buf.readableBytes() - 1)));
@@ -98,6 +98,9 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
if (BitUtil.check(value, 20)) {
return Position.ALARM_GEOFENCE;
}
+ if (BitUtil.check(value, 28)) {
+ return Position.ALARM_MOVEMENT;
+ }
if (BitUtil.check(value, 29)) {
return Position.ALARM_ACCIDENT;
}
@@ -131,7 +134,7 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
ByteBuf response = Unpooled.buffer();
response.writeShort(index);
response.writeByte(RESULT_SUCCESS);
- response.writeBytes("authentication".getBytes(StandardCharsets.US_ASCII));
+ response.writeBytes(ByteBufUtil.hexDump(id).getBytes(StandardCharsets.US_ASCII));
channel.writeAndFlush(new NetworkMessage(
formatMessage(MSG_TERMINAL_REGISTER_RESPONSE, id, response), remoteAddress));
}
@@ -160,22 +163,23 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedInt()));
- int flags = buf.readInt();
+ int status = buf.readInt();
- position.set(Position.KEY_IGNITION, BitUtil.check(flags, 0));
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 0));
+ position.set(Position.KEY_BLOCKED, BitUtil.check(status, 10));
- position.setValid(BitUtil.check(flags, 1));
+ position.setValid(BitUtil.check(status, 1));
double lat = buf.readUnsignedInt() * 0.000001;
double lon = buf.readUnsignedInt() * 0.000001;
- if (BitUtil.check(flags, 2)) {
+ if (BitUtil.check(status, 2)) {
position.setLatitude(-lat);
} else {
position.setLatitude(lat);
}
- if (BitUtil.check(flags, 3)) {
+ if (BitUtil.check(status, 3)) {
position.setLongitude(-lon);
} else {
position.setLongitude(lon);
@@ -197,20 +201,80 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder {
while (buf.readableBytes() > 2) {
int subtype = buf.readUnsignedByte();
int length = buf.readUnsignedByte();
+ int endIndex = buf.readerIndex() + length;
switch (subtype) {
case 0x01:
position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100);
break;
+ case 0x02:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedShort() * 0.1);
+ break;
case 0x30:
position.set(Position.KEY_RSSI, buf.readUnsignedByte());
break;
case 0x31:
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
break;
+ case 0x33:
+ String sentence = buf.readCharSequence(length, StandardCharsets.US_ASCII).toString();
+ if (sentence.startsWith("*M00")) {
+ String lockStatus = sentence.substring(8, 8 + 7);
+ position.set(Position.KEY_BATTERY, Integer.parseInt(lockStatus.substring(2, 5)) * 0.01);
+ }
+ break;
+ case 0x91:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort() * 0.1);
+ position.set(Position.KEY_RPM, buf.readUnsignedShort());
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte());
+ position.set(Position.KEY_THROTTLE, buf.readUnsignedByte() * 100 / 255);
+ position.set(Position.KEY_ENGINE_LOAD, buf.readUnsignedByte() * 100 / 255);
+ position.set(Position.KEY_COOLANT_TEMP, buf.readUnsignedByte() - 40);
+ buf.readUnsignedShort();
+ position.set(Position.KEY_FUEL_CONSUMPTION, buf.readUnsignedShort() * 0.01);
+ buf.readUnsignedShort();
+ buf.readUnsignedInt();
+ buf.readUnsignedShort();
+ position.set(Position.KEY_FUEL_USED, buf.readUnsignedShort() * 0.01);
+ break;
+ case 0x94:
+ if (length > 0) {
+ position.set(
+ Position.KEY_VIN, buf.readCharSequence(length, StandardCharsets.US_ASCII).toString());
+ }
+ break;
+ case 0xD0:
+ long userStatus = buf.readUnsignedInt();
+ if (BitUtil.check(userStatus, 3)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_VIBRATION);
+ }
+ break;
+ case 0xD3:
+ position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.1);
+ break;
+ case 0xEB:
+ while (buf.readerIndex() < endIndex) {
+ int tenetLength = buf.readUnsignedShort();
+ int tenetType = buf.readUnsignedShort();
+ switch (tenetType) {
+ case 0x0001:
+ position.set("fuel1", buf.readUnsignedShort() * 0.1);
+ buf.readUnsignedByte(); // unused
+ break;
+ case 0x0023:
+ buf.skipBytes(4); // unused
+ position.set("fuel2", Double.parseDouble(
+ buf.readCharSequence(2, StandardCharsets.US_ASCII).toString()));
+ break;
+ default:
+ buf.skipBytes(tenetLength - 2);
+ break;
+ }
+ }
+ break;
default:
- buf.skipBytes(length);
break;
}
+ buf.readerIndex(endIndex);
}
return position;
diff --git a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
index d76d9c92e..e8d77f1a8 100644
--- a/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/ItsProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2018 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,7 +41,7 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
.text("$")
.expression(",?[^,]+,") // event
.groupBegin()
- .expression("[^,]+,") // vendor
+ .expression("[^,]*,") // vendor
.expression("[^,]+,") // firmware version
.expression("(..),") // status
.number("(d+),").optional() // event
@@ -81,8 +81,8 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
.number("([01]{2}),") // outputs
.groupBegin()
.number("d+,") // index
- .number("(d+.d+),") // adc1
- .number("(d+.d+),") // adc2
+ .number("(d+.?d*),") // adc1
+ .number("(d+.?d*),") // adc2
.groupEnd("?")
.groupEnd("?")
.or()
@@ -195,22 +195,25 @@ public class ItsProtocolDecoder extends BaseProtocolDecoder {
position.set("emergency", parser.nextInt() > 0);
- String[] cells = parser.next().split(",");
- int mcc = Integer.parseInt(cells[1]);
- int mnc = Integer.parseInt(cells[2]);
- int lac = Integer.parseInt(cells[3], 16);
- int cid = Integer.parseInt(cells[4], 16);
- Network network = new Network(CellTower.from(mcc, mnc, lac, cid, Integer.parseInt(cells[0])));
- if (!cells[5].startsWith("(")) {
- for (int i = 0; i < 4; i++) {
- lac = Integer.parseInt(cells[5 + 3 * i + 1], 16);
- cid = Integer.parseInt(cells[5 + 3 * i + 2], 16);
- if (lac > 0 && cid > 0) {
- network.addCellTower(CellTower.from(mcc, mnc, lac, cid));
+ String cellsString = parser.next();
+ if (!cellsString.contains("x")) {
+ String[] cells = cellsString.split(",");
+ int mcc = Integer.parseInt(cells[1]);
+ int mnc = Integer.parseInt(cells[2]);
+ int lac = Integer.parseInt(cells[3], 16);
+ int cid = Integer.parseInt(cells[4], 16);
+ Network network = new Network(CellTower.from(mcc, mnc, lac, cid, Integer.parseInt(cells[0])));
+ if (!cells[5].startsWith("(")) {
+ for (int i = 0; i < 4; i++) {
+ lac = Integer.parseInt(cells[5 + 3 * i + 1], 16);
+ cid = Integer.parseInt(cells[5 + 3 * i + 2], 16);
+ if (lac > 0 && cid > 0) {
+ network.addCellTower(CellTower.from(mcc, mnc, lac, cid));
+ }
}
}
+ position.setNetwork(network);
}
- position.setNetwork(network);
String input = parser.next();
if (input.charAt(input.length() - 1) == '2') {
diff --git a/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java b/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java
index b5d060ecc..bfefb94a7 100644
--- a/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/Jt600FrameDecoder.java
@@ -35,7 +35,7 @@ public class Jt600FrameDecoder extends BaseFrameDecoder {
char type = (char) buf.getByte(buf.readerIndex());
if (type == '$') {
- boolean longFormat = buf.getUnsignedByte(buf.readerIndex() + 1) == 0x75;
+ boolean longFormat = Jt600ProtocolDecoder.isLongFormat(buf, buf.readerIndex() + 1);
int length = buf.getUnsignedShort(buf.readerIndex() + (longFormat ? 8 : 7)) + 10;
if (length <= buf.readableBytes()) {
return buf.readRetainedSlice(length);
diff --git a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
index da055dff3..f456cd1ef 100644
--- a/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Jt600ProtocolDecoder.java
@@ -86,13 +86,17 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
}
+ static boolean isLongFormat(ByteBuf buf, int flagIndex) {
+ return buf.getUnsignedByte(flagIndex) >> 4 == 0x7;
+ }
+
private List<Position> decodeBinary(ByteBuf buf, Channel channel, SocketAddress remoteAddress) {
List<Position> positions = new LinkedList<>();
buf.readByte(); // header
- boolean longFormat = buf.getUnsignedByte(buf.readerIndex()) == 0x75;
+ boolean longFormat = isLongFormat(buf, buf.readerIndex());
String id = String.valueOf(Long.parseLong(ByteBufUtil.hexDump(buf.readSlice(5))));
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
index 343ac9431..0c9f8ebb8 100644
--- a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
@@ -40,13 +40,13 @@ public class LaipacProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return formatCommand(command, "{%s}",
+ return formatCommand(command, "%s",
Command.KEY_DATA);
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "AVREQ,{%s},1",
+ return formatCommand(command, "AVREQ,%s,1",
Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "AVRESET,{%s},{%s}",
+ return formatCommand(command, "AVRESET,%s,%s",
Command.KEY_UNIQUE_ID, Command.KEY_DEVICE_PASSWORD);
default:
return null;
diff --git a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
index cbfc3660a..bd66cdc4b 100644
--- a/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeiligaoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,7 +47,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
}
private static final Pattern PATTERN = new PatternBuilder()
- .number("(dd)(dd)(dd).?d*,") // time (hhmmss)
+ .number("(d+)(dd)(dd).?d*,") // time (hhmmss)
.expression("([AV]),") // validity
.number("(d+)(dd.d+),") // latitude
.expression("([NS]),")
diff --git a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
index 55260ef0c..529496928 100644
--- a/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/MeitrackProtocolDecoder.java
@@ -70,7 +70,8 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
.number("(d+)|") // mnc
.number("(x+)|") // lac
.number("(x+),") // cid
- .number("(x+),") // state
+ .number("(xx)") // input
+ .number("(xx),") // output
.number("(x+)?|") // adc1
.number("(x+)?|") // adc2
.number("(x+)?|") // adc3
@@ -149,39 +150,38 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder {
}
position.setDeviceId(deviceSession.getDeviceId());
- int event = parser.nextInt(0);
+ int event = parser.nextInt();
position.set(Position.KEY_EVENT, event);
position.set(Position.KEY_ALARM, decodeAlarm(event));
- position.setLatitude(parser.nextDouble(0));
- position.setLongitude(parser.nextDouble(0));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
position.setTime(parser.nextDateTime());
position.setValid(parser.next().equals("A"));
position.set(Position.KEY_SATELLITES, parser.nextInt());
- int rssi = parser.nextInt(0);
+ int rssi = parser.nextInt();
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0)));
- position.setCourse(parser.nextDouble(0));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
+ position.setCourse(parser.nextDouble());
position.set(Position.KEY_HDOP, parser.nextDouble());
- position.setAltitude(parser.nextDouble(0));
+ position.setAltitude(parser.nextDouble());
- position.set(Position.KEY_ODOMETER, parser.nextInt(0));
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
position.set("runtime", parser.next());
position.setNetwork(new Network(CellTower.from(
- parser.nextInt(0), parser.nextInt(0), parser.nextHexInt(0), parser.nextHexInt(0), rssi)));
+ parser.nextInt(), parser.nextInt(), parser.nextHexInt(), parser.nextHexInt(), rssi)));
- position.set(Position.KEY_STATUS, parser.next());
+ position.set(Position.KEY_OUTPUT, parser.nextHexInt());
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
for (int i = 1; i <= 3; i++) {
- if (parser.hasNext()) {
- position.set(Position.PREFIX_ADC + i, parser.nextHexInt(0));
- }
+ position.set(Position.PREFIX_ADC + i, parser.nextHexInt());
}
String deviceModel = Context.getIdentityManager().getById(deviceSession.getDeviceId()).getModel();
diff --git a/src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java b/src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java
index 36fb9fc2f..059f688f7 100644
--- a/src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/MiniFinderProtocolEncoder.java
@@ -57,28 +57,28 @@ public class MiniFinderProtocolEncoder extends StringProtocolEncoder implements
switch (command.getType()) {
case Command.TYPE_SET_TIMEZONE:
- return formatCommand(command, "{%s}L{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_TIMEZONE);
+ return formatCommand(command, "%sL%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_TIMEZONE);
case Command.TYPE_VOICE_MONITORING:
- return formatCommand(command, "{%s}P{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
+ return formatCommand(command, "%sP%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
case Command.TYPE_ALARM_SPEED:
- return formatCommand(command, "{%s}J1{%s}", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
+ return formatCommand(command, "%sJ1%s", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
case Command.TYPE_ALARM_GEOFENCE:
- return formatCommand(command, "{%s}R1{%s}", Command.KEY_DEVICE_PASSWORD, Command.KEY_RADIUS);
+ return formatCommand(command, "%sR1%s", Command.KEY_DEVICE_PASSWORD, Command.KEY_RADIUS);
case Command.TYPE_ALARM_VIBRATION:
- return formatCommand(command, "{%s}W1,{%s}", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
+ return formatCommand(command, "%sW1,%s", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
case Command.TYPE_SET_AGPS:
- return formatCommand(command, "{%s}AGPS{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
+ return formatCommand(command, "%sAGPS%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
case Command.TYPE_ALARM_FALL:
- return formatCommand(command, "{%s}F{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
+ return formatCommand(command, "%sF%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
case Command.TYPE_MODE_POWER_SAVING:
- return formatCommand(command, "{%s}SP{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
+ return formatCommand(command, "%sSP%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
case Command.TYPE_MODE_DEEP_SLEEP:
- return formatCommand(command, "{%s}DS{%s}", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
+ return formatCommand(command, "%sDS%s", this, Command.KEY_DEVICE_PASSWORD, Command.KEY_ENABLE);
case Command.TYPE_SOS_NUMBER:
- return formatCommand(command, "{%s}{%s}1,{%s}", this,
+ return formatCommand(command, "%s%s1,%s", this,
Command.KEY_DEVICE_PASSWORD, Command.KEY_INDEX, Command.KEY_PHONE);
case Command.TYPE_SET_INDICATOR:
- return formatCommand(command, "{%s}LED{%s}", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
+ return formatCommand(command, "%sLED%s", Command.KEY_DEVICE_PASSWORD, Command.KEY_DATA);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
index 75c8b11d3..b8ab134c5 100644
--- a/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Minifinder2ProtocolDecoder.java
@@ -117,6 +117,10 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
case 0x02:
position.set(Position.KEY_ALARM, decodeAlarm(buf.readIntLE()));
break;
+ case 0x14:
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001);
+ break;
case 0x20:
position.setLatitude(buf.readIntLE() * 0.0000001);
position.setLongitude(buf.readIntLE() * 0.0000001);
@@ -150,9 +154,20 @@ public class Minifinder2ProtocolDecoder extends BaseProtocolDecoder {
mac.substring(0, mac.length() - 1), rssi));
}
break;
+ case 0x23:
+ if (endIndex > buf.readerIndex()) {
+ buf.skipBytes(6); // mac
+ }
+ if (endIndex > buf.readerIndex()) {
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ }
+ break;
case 0x24:
position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
- position.set(Position.KEY_STATUS, buf.readUnsignedIntLE());
+ long status = buf.readUnsignedIntLE();
+ position.set(Position.KEY_BATTERY_LEVEL, BitUtil.from(status, 24));
+ position.set(Position.KEY_STATUS, status);
break;
case 0x40:
buf.readUnsignedIntLE(); // timestamp
diff --git a/src/main/java/org/traccar/protocol/MotorProtocol.java b/src/main/java/org/traccar/protocol/MotorProtocol.java
new file mode 100644
index 000000000..680687e15
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MotorProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 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.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class MotorProtocol extends BaseProtocol {
+
+ public MotorProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1024));
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new MotorProtocolDecoder(MotorProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java b/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java
new file mode 100644
index 000000000..8ce4fe8b1
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/MotorProtocolDecoder.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DataConverter;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class MotorProtocolDecoder extends BaseProtocolDecoder {
+
+ public MotorProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(sentence));
+
+ String id = String.format("%08x", buf.readUnsignedIntLE());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ buf.skipBytes(2); // divider
+
+ position.set(Position.KEY_STATUS, buf.readUnsignedShortLE());
+
+ buf.skipBytes(2); // divider
+ buf.readUnsignedMediumLE(); // command
+
+ int flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(flags, 7));
+ if (BitUtil.check(flags, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+
+ position.setLatitude(BcdUtil.readInteger(buf, 2) + BcdUtil.readInteger(buf, 6) * 0.0001 / 60);
+ position.setLongitude(BcdUtil.readInteger(buf, 4) + BcdUtil.readInteger(buf, 6) * 0.0001 / 60);
+ position.setSpeed(BcdUtil.readInteger(buf, 4) * 0.1);
+ position.setCourse(BcdUtil.readInteger(buf, 4) * 0.1);
+
+ position.setTime(new DateBuilder()
+ .setYear(BcdUtil.readInteger(buf, 2))
+ .setMonth(BcdUtil.readInteger(buf, 2))
+ .setDay(BcdUtil.readInteger(buf, 2))
+ .setHour(BcdUtil.readInteger(buf, 2))
+ .setMinute(BcdUtil.readInteger(buf, 2))
+ .setSecond(BcdUtil.readInteger(buf, 2)).getDate());
+
+ position.set(Position.KEY_RSSI, BcdUtil.readInteger(buf, 2));
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java b/src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java
new file mode 100644
index 000000000..314f19757
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OmnicommFrameDecoder.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class OmnicommFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ if (buf.readableBytes() < 6) {
+ return null;
+ }
+
+ int endIndex = buf.getUnsignedShortLE(buf.readerIndex() + 2) + buf.readerIndex() + 6;
+ if (buf.writerIndex() < endIndex) {
+ return null;
+ }
+
+ ByteBuf result = Unpooled.buffer();
+ result.writeByte(buf.readUnsignedByte());
+ while (buf.readerIndex() < endIndex) {
+ int b = buf.readUnsignedByte();
+ if (b == 0xDB) {
+ int ext = buf.readUnsignedByte();
+ if (ext == 0xDC) {
+ result.writeByte(0xC0);
+ } else if (ext == 0xDD) {
+ result.writeByte(0xDB);
+ }
+ endIndex += 1;
+ } else {
+ result.writeByte(b);
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OmnicommProtocol.java b/src/main/java/org/traccar/protocol/OmnicommProtocol.java
new file mode 100644
index 000000000..96660cb59
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OmnicommProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class OmnicommProtocol extends BaseProtocol {
+
+ public OmnicommProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new OmnicommFrameDecoder());
+ pipeline.addLast(new OmnicommProtocolDecoder(OmnicommProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java b/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java
new file mode 100644
index 000000000..cd8b74c9a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OmnicommProtocolDecoder.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2019 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 com.google.protobuf.InvalidProtocolBufferException;
+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.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+import org.traccar.protobuf.OmnicommMessageOuterClass;
+
+import java.net.SocketAddress;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+
+public class OmnicommProtocolDecoder extends BaseProtocolDecoder {
+
+ public OmnicommProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_IDENTIFICATION = 0x80;
+ public static final int MSG_ARCHIVE_INQUIRY = 0x85;
+ public static final int MSG_ARCHIVE_DATA = 0x86;
+ public static final int MSG_REMOVE_ARCHIVE_INQUIRY = 0x87;
+
+ private OmnicommMessageOuterClass.OmnicommMessage parseProto(
+ ByteBuf buf, int length) throws InvalidProtocolBufferException {
+
+ final byte[] array;
+ final int offset;
+ if (buf.hasArray()) {
+ array = buf.array();
+ offset = buf.arrayOffset() + buf.readerIndex();
+ } else {
+ array = ByteBufUtil.getBytes(buf, buf.readerIndex(), length, false);
+ offset = 0;
+ }
+ buf.skipBytes(length);
+
+ return OmnicommMessageOuterClass.OmnicommMessage
+ .getDefaultInstance().getParserForType().parseFrom(array, offset, length);
+ }
+
+ private void sendResponse(Channel channel, int type, long index) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte(0xC0);
+ response.writeByte(type);
+ response.writeShortLE(4);
+ response.writeIntLE((int) index);
+ response.writeShortLE(Checksum.crc16(Checksum.CRC16_CCITT_FALSE,
+ response.nioBuffer(1, response.writerIndex() - 1)));
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // prefix
+ int type = buf.readUnsignedByte();
+ buf.readUnsignedShortLE(); // length
+
+ if (type == MSG_IDENTIFICATION) {
+
+ getDeviceSession(channel, remoteAddress, String.valueOf(buf.readUnsignedIntLE()));
+ sendResponse(channel, MSG_ARCHIVE_INQUIRY, 0);
+
+ } else if (type == MSG_ARCHIVE_DATA) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ long index = buf.readUnsignedIntLE();
+ buf.readUnsignedIntLE(); // time
+ buf.readUnsignedByte(); // priority
+
+ List<Position> positions = new LinkedList<>();
+
+ while (buf.readableBytes() > 2) {
+
+ OmnicommMessageOuterClass.OmnicommMessage message = parseProto(buf, buf.readUnsignedShortLE());
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (message.hasNAV()) {
+ OmnicommMessageOuterClass.OmnicommMessage.NAV nav = message.getNAV();
+ position.setValid(true);
+ position.setTime(new Date((nav.getGPSTime() + 1230768000) * 1000L)); // from 2009-01-01 12:00
+ position.setLatitude(nav.getLAT() * 0.0000001);
+ position.setLongitude(nav.getLON() * 0.0000001);
+ position.setSpeed(UnitsConverter.knotsFromKph(nav.getGPSVel() * 0.1));
+ position.setCourse(nav.getGPSDir());
+ position.setAltitude(nav.getGPSAlt() * 0.1);
+ position.set(Position.KEY_SATELLITES, nav.getGPSNSat());
+ }
+
+ if (position.getFixTime() != null) {
+ positions.add(position);
+ }
+ }
+
+ if (positions.isEmpty()) {
+ sendResponse(channel, MSG_REMOVE_ARCHIVE_INQUIRY, index + 1);
+ return null;
+ } else {
+ return positions;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocol.java b/src/main/java/org/traccar/protocol/OutsafeProtocol.java
new file mode 100644
index 000000000..c728a404d
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OutsafeProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 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.HttpRequestDecoder;
+import io.netty.handler.codec.http.HttpResponseEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class OutsafeProtocol extends BaseProtocol {
+
+ public OutsafeProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HttpResponseEncoder());
+ pipeline.addLast(new HttpRequestDecoder());
+ pipeline.addLast(new HttpObjectAggregator(65535));
+ pipeline.addLast(new OutsafeProtocolDecoder(OutsafeProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java
new file mode 100644
index 000000000..5e95270e8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/OutsafeProtocolDecoder.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 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.FullHttpRequest;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import org.traccar.BaseHttpProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.model.Position;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+import java.io.StringReader;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+public class OutsafeProtocolDecoder extends BaseHttpProtocolDecoder {
+
+ public OutsafeProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ FullHttpRequest request = (FullHttpRequest) msg;
+ String content = request.content().toString(StandardCharsets.UTF_8);
+ JsonObject json = Json.createReader(new StringReader(content)).readObject();
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, json.getString("device"));
+ if (deviceSession == null) {
+ sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(new Date());
+ position.setValid(true);
+ position.setLatitude(json.getJsonNumber("latitude").doubleValue());
+ position.setLongitude(json.getJsonNumber("longitude").doubleValue());
+ position.setAltitude(json.getJsonNumber("altitude").doubleValue());
+ position.setCourse(json.getJsonNumber("heading").intValue());
+
+ position.set(Position.KEY_RSSI, json.getJsonNumber("rssi").intValue());
+
+ sendResponse(channel, HttpResponseStatus.OK);
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PacificTrackProtocol.java b/src/main/java/org/traccar/protocol/PacificTrackProtocol.java
new file mode 100644
index 000000000..08991ab64
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PacificTrackProtocol.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class PacificTrackProtocol extends BaseProtocol {
+
+ public PacificTrackProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new PacificTrackProtocolDecoder(PacificTrackProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
new file mode 100644
index 000000000..d49a73a86
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PacificTrackProtocolDecoder.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class PacificTrackProtocolDecoder extends BaseProtocolDecoder {
+
+ public PacificTrackProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static int readBitExt(ByteBuf buf) {
+ int result = 0;
+ while (buf.isReadable()) {
+ int b = buf.readUnsignedByte();
+ result <<= 7;
+ result += BitUtil.to(b, 7);
+ if (BitUtil.check(b, 7)) {
+ break;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readByte(); // frame start
+ readBitExt(buf); // frame control
+ readBitExt(buf); // frame length
+
+ DeviceSession deviceSession = null;
+ Position position = new Position(getProtocolName());
+
+ while (buf.isReadable()) {
+
+ int segmentId = readBitExt(buf);
+ int segmentEnd = readBitExt(buf) + buf.readerIndex();
+
+ switch (segmentId) {
+ case 0x01:
+ position.set(Position.KEY_EVENT, readBitExt(buf));
+ break;
+ case 0x10:
+ position.setValid(BitUtil.check(buf.readUnsignedByte(), 4));
+ int date = buf.readUnsignedByte();
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(2010 + BitUtil.from(date, 4), BitUtil.to(date, 4), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+ position.setLatitude(buf.readUnsignedInt() / 1000000.0 - 90.0);
+ position.setLongitude(buf.readUnsignedInt() / 1000000.0 - 180.0);
+ int speedAndCourse = buf.readUnsignedMedium();
+ position.setCourse(BitUtil.from(speedAndCourse, 12));
+ position.setSpeed(UnitsConverter.knotsFromKph(BitUtil.to(speedAndCourse, 12) * 0.1));
+ position.set(Position.KEY_INDEX, buf.readUnsignedShort());
+ break;
+ case 0x92:
+ while (buf.readerIndex() < segmentEnd) {
+ int field = buf.readUnsignedByte();
+ int fieldPrefix = BitUtil.from(field, 5);
+ if (fieldPrefix < 0b100) {
+ switch (BitUtil.between(field, 2, 5)) {
+ case 0b000:
+ position.set("bus", BitUtil.to(field, 2));
+ case 0b001:
+ position.set("currentGear", BitUtil.to(field, 2));
+ break;
+ default:
+ break;
+ }
+ } else if (fieldPrefix < 0b101) {
+ switch (BitUtil.to(field, 5)) {
+ case 0b00000:
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedByte());
+ break;
+ case 0b00001:
+ position.set(Position.KEY_RPM, buf.readUnsignedByte() * 32);
+ break;
+ default:
+ buf.readUnsignedByte();
+ break;
+ }
+ } else if (fieldPrefix < 0b110) {
+ buf.readUnsignedShort();
+ } else if (fieldPrefix < 0b111) {
+ switch (BitUtil.to(field, 5)) {
+ case 0b00000:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 100);
+ break;
+ case 0b00001:
+ position.set(Position.KEY_HOURS, buf.readUnsignedInt() * 180);
+ break;
+ case 0b00010:
+ position.set("idleHours", buf.readUnsignedInt() * 180);
+ break;
+ default:
+ buf.readUnsignedInt();
+ break;
+ }
+ } else {
+ buf.skipBytes(buf.readUnsignedByte());
+ }
+ }
+ break;
+ case 0x100:
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(0, 15);
+ deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ break;
+ default:
+ buf.readerIndex(segmentEnd);
+ break;
+ }
+ }
+
+ if (deviceSession != null) {
+ position.setDeviceId(deviceSession.getDeviceId());
+ return position;
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
index 106889ee0..949c994ee 100644
--- a/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/PluginProtocolDecoder.java
@@ -19,6 +19,7 @@ import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
@@ -40,17 +41,30 @@ public class PluginProtocolDecoder extends BaseProtocolDecoder {
.number("(dd)(dd)(dd),") // time (hhmmss)
.number("(-?d+.d+),") // longitude
.number("(-?d+.d+),") // latitude
- .number("(d+),") // speed
+ .number("(d+.?d*),") // speed
.number("(d+),") // course
.number("(-?d+),") // altitude
.number("(-?d+),") // satellites
.number("d+,") // type
- .number("(d+),") // odometer
+ .number("(d+.?d*),") // odometer
.number("(d+),") // status
- .expression("[^,]*,")
+ .number("(d+.?d*),") // fuel
.expression("[^,]*,")
.text("0")
.groupBegin()
+ .number(",(-?d+.?d*)") // temperature 1
+ .number(",(-?d+.?d*)") // temperature 2
+ .number(",d+") // oil level
+ .number(",(d+)") // rpm
+ .number(",(d+)") // obd speed
+ .number(",d+") // people up
+ .number(",d+") // people down
+ .number(",d+") // obd status
+ .number(",d+") // fuel intake air temperature
+ .number(",(d+)") // throttle
+ .number(",(d+)") // battery
+ .groupEnd("?")
+ .groupBegin()
.text(",+,")
.number("(d+),") // event
.groupEnd("?")
@@ -74,18 +88,50 @@ public class PluginProtocolDecoder extends BaseProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setValid(true);
position.setTime(parser.nextDateTime());
position.setLongitude(parser.nextDouble());
position.setLatitude(parser.nextDouble());
- position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
position.setCourse(parser.nextInt());
position.setAltitude(parser.nextInt());
position.set(Position.KEY_SATELLITES, parser.nextInt());
- position.set(Position.KEY_ODOMETER, parser.nextInt());
- position.set(Position.KEY_STATUS, parser.nextInt());
- position.set(Position.KEY_EVENT, parser.nextInt());
+ position.set(Position.KEY_ODOMETER, (long) (parser.nextDouble() * 1000));
+
+ int status = parser.nextInt();
+ position.setValid(BitUtil.check(status, 0));
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 1));
+ for (int i = 0; i < 4; i++) {
+ position.set(Position.PREFIX_IN + (i + 1), BitUtil.check(status, 20 + i));
+ }
+ position.set(Position.KEY_STATUS, status);
+
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble());
+
+ if (parser.hasNext(6)) {
+ position.set(Position.PREFIX_TEMP + 1, parser.nextDouble());
+ position.set(Position.PREFIX_TEMP + 2, parser.nextDouble());
+ position.set(Position.KEY_RPM, parser.nextInt());
+ position.set(Position.KEY_OBD_SPEED, parser.nextInt());
+ position.set(Position.KEY_THROTTLE, parser.nextInt() * 0.1);
+ position.set(Position.KEY_POWER, parser.nextInt() * 0.1);
+ }
+
+ if (parser.hasNext()) {
+ int event = parser.nextInt();
+ switch (event) {
+ case 11317:
+ position.set(Position.KEY_ALARM, Position.ALARM_ACCELERATION);
+ break;
+ case 11319:
+ position.set(Position.KEY_ALARM, Position.ALARM_BRAKING);
+ break;
+ default:
+ break;
+
+ }
+ position.set(Position.KEY_EVENT, event);
+ }
return position;
}
diff --git a/src/main/java/org/traccar/protocol/PstFrameDecoder.java b/src/main/java/org/traccar/protocol/PstFrameDecoder.java
new file mode 100644
index 000000000..dbafd1476
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PstFrameDecoder.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class PstFrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ while (buf.isReadable() && buf.getByte(buf.readerIndex()) == 0x28) {
+ buf.skipBytes(1);
+ }
+
+ int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0x29);
+ if (endIndex > 0) {
+ ByteBuf result = Unpooled.buffer(endIndex - buf.readerIndex());
+ while (buf.readerIndex() < endIndex) {
+ int b = buf.readUnsignedByte();
+ if (b == 0x27) {
+ b = buf.readUnsignedByte() ^ 0x40;
+ }
+ result.writeByte(b);
+ }
+ buf.skipBytes(1);
+ return result;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PstProtocol.java b/src/main/java/org/traccar/protocol/PstProtocol.java
new file mode 100644
index 000000000..0ed9affd8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PstProtocol.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class PstProtocol extends BaseProtocol {
+
+ public PstProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new PstProtocolDecoder(PstProtocol.this));
+ }
+ });
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new PstFrameDecoder());
+ pipeline.addLast(new PstProtocolDecoder(PstProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/PstProtocolDecoder.java b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java
new file mode 100644
index 000000000..23e2afbbd
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/PstProtocolDecoder.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.Date;
+
+public class PstProtocolDecoder extends BaseProtocolDecoder {
+
+ public PstProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_STATUS = 0x05;
+
+ private Date readDate(ByteBuf buf) {
+ long value = buf.readUnsignedInt();
+ return new DateBuilder()
+ .setYear((int) BitUtil.between(value, 26, 32))
+ .setMonth((int) BitUtil.between(value, 22, 26))
+ .setDay((int) BitUtil.between(value, 17, 22))
+ .setHour((int) BitUtil.between(value, 12, 17))
+ .setMinute((int) BitUtil.between(value, 6, 12))
+ .setSecond((int) BitUtil.between(value, 0, 6)).getDate();
+ }
+
+ private double readCoordinate(ByteBuf buf) {
+ long value = buf.readUnsignedInt();
+ int sign = BitUtil.check(value, 31) ? -1 : 1;
+ value = BitUtil.to(value, 31);
+ return sign * (BitUtil.from(value, 16) + BitUtil.to(value, 16) * 0.00001) / 60;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ String id = String.valueOf(buf.readUnsignedInt());
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ buf.readUnsignedByte(); // version
+ buf.readUnsignedInt(); // index
+
+ int type = buf.readUnsignedByte();
+
+ if (type == MSG_STATUS) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setDeviceTime(readDate(buf));
+
+ buf.readUnsignedByte();
+
+ int count = buf.readUnsignedByte();
+ for (int i = 0; i < count; i++) {
+
+ int tag = buf.readUnsignedByte();
+ int length = buf.readUnsignedByte();
+
+ switch (tag) {
+ case 0x0D:
+ int battery = buf.readUnsignedByte();
+ if (battery <= 20) {
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte() * 5);
+ }
+ break;
+ case 0x10:
+ position.setFixTime(readDate(buf));
+ position.setLatitude(readCoordinate(buf));
+ position.setLongitude(readCoordinate(buf));
+ position.setSpeed(buf.readUnsignedByte());
+ position.setCourse(buf.readUnsignedByte() * 2);
+ position.setAltitude(buf.readShort());
+ buf.readUnsignedInt(); // gps condition
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
+ }
+
+ return position.getFixTime() != null ? position : null;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Pt215Protocol.java b/src/main/java/org/traccar/protocol/Pt215Protocol.java
new file mode 100644
index 000000000..09bd9b121
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Pt215Protocol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 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.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class Pt215Protocol extends BaseProtocol {
+
+ public Pt215Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 1, -1, 0));
+ pipeline.addLast(new Pt215ProtocolDecoder(Pt215Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java
new file mode 100644
index 000000000..48ce7dede
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/Pt215ProtocolDecoder.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class Pt215ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Pt215ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN = 0x01;
+ public static final int MSG_HEARTBEAT = 0x08;
+ public static final int MSG_GPS_REALTIME = 0x10;
+ public static final int MSG_GPS_OFFLINE = 0x11;
+ public static final int MSG_STATUS = 0x13;
+
+ private void sendResponse(
+ Channel channel, SocketAddress remoteAddress, int type, ByteBuf content) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeByte('X');
+ response.writeByte('X');
+ response.writeByte(content != null ? 1 + content.readableBytes() : 1);
+ response.writeByte(type);
+ if (content != null) {
+ response.writeBytes(content);
+ content.release();
+ }
+ response.writeByte('\r');
+ response.writeByte('\n');
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+ buf.readUnsignedByte(); // length
+ int type = buf.readUnsignedByte();
+
+ if (type == MSG_LOGIN) {
+
+ getDeviceSession(channel, remoteAddress, ByteBufUtil.hexDump(buf.readSlice(8)).substring(1));
+ sendResponse(channel, remoteAddress, type, null);
+
+ } else if (type == MSG_GPS_OFFLINE || type == MSG_GPS_REALTIME) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ sendResponse(channel, remoteAddress, type, buf.retainedSlice(buf.readerIndex(), 6));
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+
+ double latitude = buf.readUnsignedInt() / 60.0 / 30000.0;
+ double longitude = buf.readUnsignedInt() / 60.0 / 30000.0;
+
+ int flags = buf.readUnsignedShort();
+ position.setCourse(BitUtil.to(flags, 10));
+ position.setValid(BitUtil.check(flags, 12));
+
+ if (!BitUtil.check(flags, 10)) {
+ latitude = -latitude;
+ }
+ if (BitUtil.check(flags, 11)) {
+ longitude = -longitude;
+ }
+
+ position.setLatitude(latitude);
+ position.setLongitude(longitude);
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/Pt502ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Pt502ProtocolEncoder.java
index ba08b16ae..364ecae86 100644
--- a/src/main/java/org/traccar/protocol/Pt502ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Pt502ProtocolEncoder.java
@@ -46,13 +46,13 @@ public class Pt502ProtocolEncoder extends StringProtocolEncoder implements Strin
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return formatCommand(command, "{%s}\r\n", Command.KEY_DATA);
+ return formatCommand(command, "%s\r\n", Command.KEY_DATA);
case Command.TYPE_OUTPUT_CONTROL:
- return formatCommand(command, "#OPC{%s},{%s}\r\n", Command.KEY_INDEX, Command.KEY_DATA);
+ return formatCommand(command, "#OPC%s,%s\r\n", Command.KEY_INDEX, Command.KEY_DATA);
case Command.TYPE_SET_TIMEZONE:
- return formatCommand(command, "#TMZ{%s}\r\n", Command.KEY_TIMEZONE);
+ return formatCommand(command, "#TMZ%s\r\n", Command.KEY_TIMEZONE);
case Command.TYPE_ALARM_SPEED:
- return formatCommand(command, "#SPD{%s}\r\n", Command.KEY_DATA);
+ return formatCommand(command, "#SPD%s\r\n", Command.KEY_DATA);
case Command.TYPE_REQUEST_PHOTO:
return formatCommand(command, "#PHO\r\n");
default:
diff --git a/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java
new file mode 100644
index 000000000..c9db10610
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RaceDynamicsProtocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 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.LineBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class RaceDynamicsProtocol extends BaseProtocol {
+
+ public RaceDynamicsProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LineBasedFrameDecoder(1500));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new RaceDynamicsProtocolDecoder(RaceDynamicsProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java b/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java
new file mode 100644
index 000000000..f441bf8ed
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RaceDynamicsProtocolDecoder.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2019 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.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class RaceDynamicsProtocolDecoder extends BaseProtocolDecoder {
+
+ public RaceDynamicsProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN = 12;
+ public static final int MSG_LOCATION = 15;
+
+ private static final Pattern PATTERN_LOGIN = new PatternBuilder()
+ .text("$GPRMC,")
+ .number("d+,") // type
+ .number("d{6},") // date
+ .number("d{6},") // time
+ .number("(d{15}),")
+ .compile();
+
+ private static final Pattern PATTERN_LOCATION = new PatternBuilder()
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .expression("([AV]),") // validity
+ .number("(dd)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(ddd)(dd.d+),") // longitude
+ .expression("([EW]),")
+ .number("(d+),") // speed
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(-?d+),") // altitude
+ .number("(d+),") // satellites
+ .number("([01]),") // ignition
+ .number("(d+),") // index
+ .text("%,")
+ .number("([^,]+),") // ibutton
+ .number("d+,") // acceleration
+ .number("d+,") // deceleration
+ .number("[01],") // cruise control
+ .number("[01],") // seat belt
+ .number("[01],") // wrong ibutton
+ .number("(d+),") // power
+ .number("[01],") // power status
+ .number("(d+),") // battery
+ .number("([01]),") // panic
+ .number("d+,")
+ .number("d+,")
+ .number("(d),") // overspeed
+ .number("d+,") // speed limit
+ .number("d+,") // tachometer
+ .number("d+,d+,d+,") // aux
+ .number("d+,") // geofence id
+ .number("d+,") // road speed type
+ .number("d+,") // ibutton count
+ .number("(d),") // overdriver alert
+ .any()
+ .compile();
+
+ private String imei;
+
+ private void sendResponse(Channel channel, SocketAddress remoteAddress, int type) {
+ if (channel != null) {
+ String response = String.format(
+ "$GPRMC,%1$d,%2$td%2$tm%2$ty,%2$tH%2$tM%2$tS,%3$s,\r\n", type, new Date(), imei);
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ int type = Integer.parseInt(sentence.substring(7, 9));
+
+ if (type == MSG_LOGIN) {
+
+ Parser parser = new Parser(PATTERN_LOGIN, sentence);
+ if (parser.matches()) {
+ imei = parser.next();
+ getDeviceSession(channel, remoteAddress, imei);
+ sendResponse(channel, remoteAddress, type);
+ }
+
+ } else if (type == MSG_LOCATION) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ List<Position> positions = new LinkedList<>();
+
+ for (String data : sentence.substring(17, sentence.length() - 3).split(",#,#,")) {
+ Parser parser = new Parser(PATTERN_LOCATION, data);
+ if (parser.matches()) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
+
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble());
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ position.setAltitude(parser.nextInt());
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
+ position.set(Position.KEY_INDEX, parser.nextInt());
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
+ position.set(Position.KEY_POWER, parser.nextInt() * 0.01);
+ position.set(Position.KEY_BATTERY, parser.nextInt() * 0.01);
+ position.set(Position.KEY_ALARM, parser.nextInt() > 0 ? Position.ALARM_SOS : null);
+ position.set(Position.KEY_ALARM, parser.nextInt() > 0 ? Position.ALARM_OVERSPEED : null);
+
+ int overDriver = parser.nextInt();
+ if (overDriver > 0) {
+ position.set("overDriver", overDriver);
+ }
+
+ positions.add(position);
+
+ }
+ }
+
+ sendResponse(channel, remoteAddress, type);
+
+ return positions;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RstProtocol.java b/src/main/java/org/traccar/protocol/RstProtocol.java
new file mode 100644
index 000000000..10d11d493
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RstProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class RstProtocol extends BaseProtocol {
+
+ public RstProtocol() {
+ addServer(new TrackerServer(true, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new RstProtocolDecoder(RstProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RstProtocolDecoder.java b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
new file mode 100644
index 000000000..05601ed51
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/RstProtocolDecoder.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.BitUtil;
+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 RstProtocolDecoder extends BaseProtocolDecoder {
+
+ public RstProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("RST;")
+ .expression("([AL]);") // archive
+ .expression("([^,]+);") // model
+ .expression("(.{5});") // firmware
+ .number("(d{9});") // serial number
+ .number("(d+);") // index
+ .number("(d+);") // type
+ .number("(dd)-(dd)-(dddd) ") // event date
+ .number("(dd):(dd):(dd);") // event time
+ .number("(dd)-(dd)-(dddd) ") // fix date
+ .number("(dd):(dd):(dd);") // fix time
+ .number("(-?d+.d+);") // latitude
+ .number("(-?d+.d+);") // longitude
+ .number("(d+);") // speed
+ .number("(d+);") // course
+ .number("(-?d+);") // altitude
+ .number("([01]);") // valid
+ .number("(d+);") // satellites
+ .number("(d+);") // hdop
+ .number("(xx);") // inputs 1
+ .number("(xx);") // inputs 2
+ .number("(xx);") // inputs 3
+ .number("(xx);") // outputs 1
+ .number("(xx);") // outputs 2
+ .number("(d+.d+);") // power
+ .number("(d+.d+);") // battery
+ .number("(d+);") // odometer
+ .number("(d+);") // rssi
+ .number("(xx);") // temperature
+ .number("x{4};") // sensors
+ .number("(xx);") // status 1
+ .number("(xx);") // status 2
+ .any()
+ .compile();
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ Parser parser = new Parser(PATTERN, (String) msg);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ String archive = parser.next();
+ String model = parser.next();
+ String firmware = parser.next();
+ String serial = parser.next();
+ int index = parser.nextInt();
+ parser.nextInt(); // type
+
+ if (channel != null && archive.equals("A")) {
+ String response = "RST;A;" + model + ";" + firmware + ";" + serial + ";" + index + ";6;FIM;";
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, serial);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setDeviceTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setFixTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
+ position.setLatitude(parser.nextDouble());
+ position.setLongitude(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt()));
+ position.setCourse(parser.nextInt());
+ position.setAltitude(parser.nextInt());
+ position.setValid(parser.nextInt() > 0);
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.KEY_HDOP, parser.nextInt());
+ position.set(Position.PREFIX_IN + 1, parser.nextHexInt());
+ position.set(Position.PREFIX_IN + 2, parser.nextHexInt());
+ position.set(Position.PREFIX_IN + 3, parser.nextHexInt());
+ position.set(Position.PREFIX_OUT + 1, parser.nextHexInt());
+ position.set(Position.PREFIX_OUT + 2, parser.nextHexInt());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, (int) parser.nextHexInt().byteValue());
+
+ int status = (parser.nextHexInt() << 8) + parser.nextHexInt();
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 7));
+ position.set(Position.KEY_STATUS, status);
+
+ return position;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
index 2d476427d..227a9ac91 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolDecoder.java
@@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -34,6 +35,8 @@ import java.util.List;
public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
+ private ByteBuf photo;
+
public RuptelaProtocolDecoder(Protocol protocol) {
super(protocol);
}
@@ -247,6 +250,43 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder {
return positions;
+ } else if (type == MSG_FILES) {
+
+ int subtype = buf.readUnsignedByte();
+ int source = buf.readUnsignedByte();
+
+ if (subtype == 2) {
+ ByteBuf filename = buf.readSlice(8);
+ int total = buf.readUnsignedShort();
+ int current = buf.readUnsignedShort();
+ if (photo == null) {
+ photo = Unpooled.buffer();
+ }
+ photo.writeBytes(buf.readSlice(buf.readableBytes() - 2));
+ if (current < total - 1) {
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(subtype);
+ content.writeByte(source);
+ content.writeBytes(filename);
+ content.writeShort(current + 1);
+ ByteBuf response = RuptelaProtocolEncoder.encodeContent(type, content);
+ content.release();
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+ } else {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ getLastLocation(position, null);
+ position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(imei, photo, "jpg"));
+ photo.release();
+ photo = null;
+ return position;
+ }
+ }
+
+ return null;
+
} else {
return decodeCommandResponse(deviceSession, type, buf);
diff --git a/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
index 51967403d..fb0dcf690 100644
--- a/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/RuptelaProtocolEncoder.java
@@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.traccar.BaseProtocolEncoder;
import org.traccar.helper.Checksum;
+import org.traccar.helper.DataConverter;
import org.traccar.model.Command;
import org.traccar.Protocol;
@@ -30,7 +31,7 @@ public class RuptelaProtocolEncoder extends BaseProtocolEncoder {
super(protocol);
}
- private ByteBuf encodeContent(int type, ByteBuf content) {
+ public static ByteBuf encodeContent(int type, ByteBuf content) {
ByteBuf buf = Unpooled.buffer();
@@ -49,8 +50,14 @@ public class RuptelaProtocolEncoder extends BaseProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- content.writeBytes(command.getString(Command.KEY_DATA).getBytes(StandardCharsets.US_ASCII));
- return encodeContent(RuptelaProtocolDecoder.MSG_SMS_VIA_GPRS, content);
+ String data = command.getString(Command.KEY_DATA);
+ if (data.matches("(\\p{XDigit}{2})+")) {
+ content.writeBytes(DataConverter.parseHex(data));
+ return content;
+ } else {
+ content.writeBytes(data.getBytes(StandardCharsets.US_ASCII));
+ return encodeContent(RuptelaProtocolDecoder.MSG_SMS_VIA_GPRS, content);
+ }
case Command.TYPE_REQUEST_PHOTO:
content.writeByte(1); // sub-command
content.writeByte(0); // source
diff --git a/src/main/java/org/traccar/protocol/S168Protocol.java b/src/main/java/org/traccar/protocol/S168Protocol.java
new file mode 100644
index 000000000..e78664c40
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/S168Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 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 S168Protocol extends BaseProtocol {
+
+ public S168Protocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new CharacterDelimiterFrameDecoder(1024, '$'));
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new S168ProtocolDecoder(S168Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java b/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java
new file mode 100644
index 000000000..71aff1a65
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/S168ProtocolDecoder.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+public class S168ProtocolDecoder extends BaseProtocolDecoder {
+
+ public S168ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+ String[] values = sentence.split("#");
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[1]);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ String content = values[4];
+ String[] fragments = content.split(";");
+ for (String fragment : fragments) {
+
+ int dataIndex = fragment.indexOf(':');
+ String type = fragment.substring(0, dataIndex);
+ values = fragment.substring(dataIndex + 1).split(",");
+ int index = 0;
+
+ switch (type) {
+ case "GDATA":
+ position.setValid(values[index++].equals("A"));
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[index++]));
+ DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ position.setTime(dateFormat.parse(values[index++]));
+ position.setLatitude(Double.parseDouble(values[index++]));
+ position.setLongitude(Double.parseDouble(values[index++]));
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ position.setCourse(Integer.parseInt(values[index++]));
+ position.setAltitude(Integer.parseInt(values[index++]));
+ break;
+ default:
+ break;
+ }
+ }
+
+ return position.getAttributes().containsKey(Position.KEY_SATELLITES) ? position : null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
index 7e0e3e110..304f61836 100644
--- a/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/SigfoxProtocolDecoder.java
@@ -24,6 +24,7 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import org.traccar.BaseHttpProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.DataConverter;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.Network;
@@ -31,7 +32,10 @@ import org.traccar.model.Position;
import org.traccar.model.WifiAccessPoint;
import javax.json.Json;
+import javax.json.JsonNumber;
import javax.json.JsonObject;
+import javax.json.JsonString;
+import javax.json.JsonValue;
import java.io.StringReader;
import java.net.SocketAddress;
import java.net.URLDecoder;
@@ -44,6 +48,30 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
super(protocol);
}
+ private int getJsonInt(JsonObject json, String key) {
+ JsonValue value = json.get(key);
+ if (value != null) {
+ if (value.getValueType() == JsonValue.ValueType.NUMBER) {
+ return ((JsonNumber) value).intValue();
+ } else if (value.getValueType() == JsonValue.ValueType.STRING) {
+ return Integer.parseInt(((JsonString) value).getString());
+ }
+ }
+ return 0;
+ }
+
+ private double getJsonDouble(JsonObject json, String key) {
+ JsonValue value = json.get(key);
+ if (value != null) {
+ if (value.getValueType() == JsonValue.ValueType.NUMBER) {
+ return ((JsonNumber) value).doubleValue();
+ } else if (value.getValueType() == JsonValue.ValueType.STRING) {
+ return Double.parseDouble(((JsonString) value).getString());
+ }
+ }
+ return 0;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -64,71 +92,113 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.setTime(new Date(json.getInt("time") * 1000L));
+ if (json.containsKey("time")) {
+ position.setTime(new Date(getJsonInt(json, "time") * 1000L));
+ } else {
+ position.setTime(new Date());
+ }
+
+ if (json.containsKey("location")
+ || json.containsKey("lat") && json.containsKey("lng") && !json.containsKey("data")) {
- String data = json.getString(json.containsKey("data") ? "data" : "payload");
- ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(data));
- try {
- int event = buf.readUnsignedByte();
- if (event >> 4 == 0) {
+ JsonObject location;
+ if (json.containsKey("location")) {
+ location = json.getJsonObject("location");
+ } else {
+ location = json;
+ }
- position.setValid(true);
- position.setLatitude(buf.readIntLE() * 0.0000001);
- position.setLongitude(buf.readIntLE() * 0.0000001);
- position.setCourse(buf.readUnsignedByte() * 2);
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ position.setValid(true);
+ position.setLatitude(getJsonDouble(location, "lat"));
+ position.setLongitude(getJsonDouble(location, "lng"));
- position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.025);
+ } else {
- } else {
+ String data = json.getString(json.containsKey("data") ? "data" : "payload");
+ ByteBuf buf = Unpooled.wrappedBuffer(DataConverter.parseHex(data));
+ try {
+ int event = buf.readUnsignedByte();
+ if (event == 0x0f || event == 0x1f) {
- position.set(Position.KEY_EVENT, event);
-
- while (buf.isReadable()) {
- int type = buf.readUnsignedByte();
- switch (type) {
- case 0x01:
- position.setValid(true);
- position.setLatitude(buf.readMedium());
- position.setLongitude(buf.readMedium());
- break;
- case 0x02:
- position.setValid(true);
- position.setLatitude(buf.readFloat());
- position.setLongitude(buf.readFloat());
- break;
- case 0x03:
- position.set(Position.PREFIX_TEMP + 1, buf.readByte() * 0.5);
- break;
- case 0x04:
- position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.1);
- break;
- case 0x05:
- position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
- break;
- case 0x06:
- String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
- position.setNetwork(new Network(WifiAccessPoint.from(
- mac.substring(0, mac.length() - 1), buf.readUnsignedByte())));
- break;
- case 0x07:
- buf.skipBytes(10); // wifi extended
- break;
- case 0x08:
- buf.skipBytes(6); // accelerometer
- break;
- case 0x09:
- position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
- break;
- default:
- buf.readUnsignedByte(); // fence number
- break;
+ position.setValid(event >> 4 > 0);
+
+ long value;
+ value = buf.readUnsignedInt();
+ position.setLatitude(BitUtil.to(value, 31) * 0.000001);
+ if (BitUtil.check(value, 31)) {
+ position.setLatitude(-position.getLatitude());
}
- }
+ value = buf.readUnsignedInt();
+ position.setLongitude(BitUtil.to(value, 31) * 0.000001);
+ if (BitUtil.check(value, 31)) {
+ position.setLongitude(-position.getLongitude());
+ }
+
+ position.set(Position.KEY_BATTERY, (int) buf.readUnsignedByte());
+
+ } else if (event >> 4 == 0) {
+
+ position.setValid(true);
+ position.setLatitude(buf.readIntLE() * 0.0000001);
+ position.setLongitude(buf.readIntLE() * 0.0000001);
+ position.setCourse(buf.readUnsignedByte() * 2);
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.025);
+ } else {
+
+ position.set(Position.KEY_EVENT, event);
+ if (event == 0x22 || event == 0x62) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+
+ while (buf.isReadable()) {
+ int type = buf.readUnsignedByte();
+ switch (type) {
+ case 0x01:
+ position.setValid(true);
+ position.setLatitude(buf.readMedium());
+ position.setLongitude(buf.readMedium());
+ break;
+ case 0x02:
+ position.setValid(true);
+ position.setLatitude(buf.readFloat());
+ position.setLongitude(buf.readFloat());
+ break;
+ case 0x03:
+ position.set(Position.PREFIX_TEMP + 1, buf.readByte() * 0.5);
+ break;
+ case 0x04:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.1);
+ break;
+ case 0x05:
+ position.set(Position.KEY_BATTERY_LEVEL, buf.readUnsignedByte());
+ break;
+ case 0x06:
+ String mac = ByteBufUtil.hexDump(buf.readSlice(6)).replaceAll("(..)", "$1:");
+ position.setNetwork(new Network(WifiAccessPoint.from(
+ mac.substring(0, mac.length() - 1), buf.readUnsignedByte())));
+ break;
+ case 0x07:
+ buf.skipBytes(10); // wifi extended
+ break;
+ case 0x08:
+ buf.skipBytes(6); // accelerometer
+ break;
+ case 0x09:
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ break;
+ default:
+ buf.readUnsignedByte(); // fence number
+ break;
+ }
+ }
+
+ }
+ } finally {
+ buf.release();
}
- } finally {
- buf.release();
}
if (position.getLatitude() == 0 && position.getLongitude() == 0) {
@@ -136,10 +206,10 @@ public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder {
}
if (json.containsKey("rssi")) {
- position.set(Position.KEY_RSSI, json.getJsonNumber("rssi").doubleValue());
+ position.set(Position.KEY_RSSI, getJsonDouble(json, "rssi"));
}
if (json.containsKey("seqNumber")) {
- position.set(Position.KEY_INDEX, json.getInt("seqNumber"));
+ position.set(Position.KEY_INDEX, getJsonInt(json, "seqNumber"));
}
sendResponse(channel, HttpResponseStatus.OK);
diff --git a/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java
new file mode 100644
index 000000000..53a948cdc
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SolarPoweredProtocol.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class SolarPoweredProtocol extends BaseProtocol {
+
+ public SolarPoweredProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new HuabaoFrameDecoder());
+ pipeline.addLast(new SolarPoweredProtocolDecoder(SolarPoweredProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java b/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java
new file mode 100644
index 000000000..eae37386a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/SolarPoweredProtocolDecoder.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class SolarPoweredProtocolDecoder extends BaseProtocolDecoder {
+
+ public SolarPoweredProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_ACTIVE_REPORTING = 0x11;
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.readUnsignedByte(); // start marker
+
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(0, 15);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ int type = buf.readUnsignedByte();
+ buf.readUnsignedShort(); // attributes
+
+ if (type == MSG_ACTIVE_REPORTING) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ while (buf.readableBytes() > 2) {
+ int tag = buf.readUnsignedByte();
+ int length = buf.readUnsignedByte();
+ switch (tag) {
+ case 0x81:
+ int status = buf.readUnsignedByte();
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+ position.setLatitude(buf.readUnsignedInt() * 0.000001);
+ if (BitUtil.check(status, 3)) {
+ position.setLatitude(-position.getLatitude());
+ }
+ position.setLongitude(buf.readUnsignedInt() * 0.000001);
+ if (BitUtil.check(status, 2)) {
+ position.setLongitude(-position.getLongitude());
+ }
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ position.set(Position.KEY_DEVICE_TEMP, (int) buf.readByte());
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.02);
+ position.setCourse(buf.readUnsignedByte());
+ break;
+ default:
+ buf.skipBytes(length);
+ break;
+ }
+ }
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
index 90151d061..5ffddb318 100644
--- a/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/StarcomProtocolDecoder.java
@@ -18,6 +18,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.helper.UnitsConverter;
import org.traccar.model.Position;
import java.net.SocketAddress;
@@ -66,7 +67,7 @@ public class StarcomProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(Double.parseDouble(value));
break;
case "velocity":
- position.setSpeed(Integer.parseInt(value));
+ position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(value)));
break;
case "heading":
position.setCourse(Integer.parseInt(value));
@@ -74,8 +75,8 @@ public class StarcomProtocolDecoder extends BaseProtocolDecoder {
case "eventid":
position.set(Position.KEY_EVENT, Integer.parseInt(value));
break;
- case "odometer":
- position.set(Position.KEY_ODOMETER, Long.parseLong(value));
+ case "mileage":
+ position.set(Position.KEY_ODOMETER, (long) (Double.parseDouble(value) * 1000));
break;
case "satellites":
position.set(Position.KEY_SATELLITES, Integer.parseInt(value));
diff --git a/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java b/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java
index de8de17f0..8748ecc61 100644
--- a/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechFrameDecoder.java
@@ -32,13 +32,24 @@ public class SuntechFrameDecoder extends BaseFrameDecoder {
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
- int delimiterIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\r');
- while (delimiterIndex > 0) {
- if (delimiterIndex + 1 < buf.writerIndex() && buf.getByte(delimiterIndex + 1) == '\n') {
- delimiterIndex = buf.indexOf(delimiterIndex + 1, buf.writerIndex(), (byte) '\r');
- } else {
- return readFrame(buf, delimiterIndex);
+ if (buf.getByte(buf.readerIndex() + 1) == 0) {
+
+ int length = 1 + 2 + buf.getShort(buf.readerIndex() + 1);
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
}
+
+ } else {
+
+ int delimiterIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '\r');
+ while (delimiterIndex > 0) {
+ if (delimiterIndex + 1 < buf.writerIndex() && buf.getByte(delimiterIndex + 1) == '\n') {
+ delimiterIndex = buf.indexOf(delimiterIndex + 1, buf.writerIndex(), (byte) '\r');
+ } else {
+ return readFrame(buf, delimiterIndex);
+ }
+ }
+
}
return null;
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocol.java b/src/main/java/org/traccar/protocol/SuntechProtocol.java
index 7e2c20e6f..199885537 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocol.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocol.java
@@ -15,7 +15,6 @@
*/
package org.traccar.protocol;
-import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
@@ -38,7 +37,6 @@ public class SuntechProtocol extends BaseProtocol {
protected void addProtocolHandlers(PipelineBuilder pipeline) {
pipeline.addLast(new SuntechFrameDecoder());
pipeline.addLast(new StringEncoder());
- pipeline.addLast(new StringDecoder());
pipeline.addLast(new SuntechProtocolEncoder(SuntechProtocol.this));
pipeline.addLast(new SuntechProtocolDecoder(SuntechProtocol.this));
}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java
index e40096a77..978d768be 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 - 2019 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2020 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,18 +15,22 @@
*/
package org.traccar.protocol;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@@ -34,6 +38,8 @@ import java.util.TimeZone;
public class SuntechProtocolDecoder extends BaseProtocolDecoder {
+ private String prefix;
+
private int protocolType;
private boolean hbm;
private boolean includeAdc;
@@ -44,6 +50,10 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
+ public String getPrefix() {
+ return prefix;
+ }
+
public void setProtocolType(int protocolType) {
this.protocolType = protocolType;
}
@@ -232,6 +242,10 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
position.set(Position.KEY_STATUS, Integer.parseInt(values[index++]));
+ if (values[index].length() == 3) {
+ index += 1; // collaborative network
+ }
+
DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
position.setTime(dateFormat.parse(values[index++] + values[index++]));
@@ -268,7 +282,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
position.set(Position.KEY_TYPE, type);
- if (protocol.equals("ST300") || protocol.equals("ST500") || protocol.equals("ST600")) {
+ if (protocol.startsWith("ST3") || protocol.equals("ST500") || protocol.equals("ST600")) {
index += 1; // model
}
@@ -300,7 +314,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_POWER, Double.parseDouble(values[index++]));
String io = values[index++];
- if (io.length() == 6) {
+ if (io.length() >= 6) {
position.set(Position.KEY_IGNITION, io.charAt(0) == '1');
position.set(Position.PREFIX_IN + 1, io.charAt(1) == '1');
position.set(Position.PREFIX_IN + 2, io.charAt(2) == '1');
@@ -361,6 +375,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
remaining -= attribute.length() + 1;
}
+ index += 1; // checksum
break;
default:
break;
@@ -382,7 +397,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
if (isIncludeAdc(deviceSession.getDeviceId())) {
for (int i = 1; i <= 3; i++) {
- if (!values[index++].isEmpty()) {
+ if (index < values.length && !values[index++].isEmpty()) {
position.set(Position.PREFIX_ADC + i, Double.parseDouble(values[index - 1]));
}
}
@@ -501,20 +516,149 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeBinary(
+ Channel channel, SocketAddress remoteAddress, ByteBuf buf) {
+
+ int type = buf.readUnsignedByte();
+ buf.readUnsignedShort(); // length
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, ByteBufUtil.hexDump(buf.readSlice(5)));
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ int mask = buf.readUnsignedMedium();
+
+ if (BitUtil.check(mask, 1)) {
+ buf.readUnsignedByte(); // model
+ }
+
+ if (BitUtil.check(mask, 2)) {
+ position.set(Position.KEY_VERSION_FW, String.format("%d.%d.%d",
+ buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()));
+ }
+
+ if (BitUtil.check(mask, 3) && buf.readUnsignedByte() == 0) {
+ position.set(Position.KEY_ARCHIVE, true);
+ }
+
+ if (BitUtil.check(mask, 4) && BitUtil.check(mask, 5)) {
+ position.setTime(new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .getDate());
+ }
+
+ if (BitUtil.check(mask, 6)) {
+ buf.readUnsignedInt(); // cell
+ }
+
+ if (BitUtil.check(mask, 7)) {
+ buf.readUnsignedShort(); // mcc
+ }
+
+ if (BitUtil.check(mask, 8)) {
+ buf.readUnsignedShort(); // mnc
+ }
+
+ if (BitUtil.check(mask, 9)) {
+ buf.readUnsignedShort(); // lac
+ }
+
+ if (BitUtil.check(mask, 10)) {
+ position.set(Position.KEY_RSSI, buf.readUnsignedByte());
+ }
+
+ if (BitUtil.check(mask, 11)) {
+ long value = buf.readUnsignedInt();
+ if (BitUtil.check(value, 31)) {
+ value = -BitUtil.to(value, 31);
+ }
+ position.setLatitude(value / 1000000.0);
+ }
+
+ if (BitUtil.check(mask, 12)) {
+ long value = buf.readUnsignedInt();
+ if (BitUtil.check(value, 31)) {
+ value = -BitUtil.to(value, 31);
+ }
+ position.setLongitude(value / 1000000.0);
+ }
+
+ if (BitUtil.check(mask, 13)) {
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() / 100.0));
+ }
+
+ if (BitUtil.check(mask, 14)) {
+ position.setCourse(buf.readUnsignedShort() / 100.0);
+ }
+
+ if (BitUtil.check(mask, 15)) {
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ }
+
+ if (BitUtil.check(mask, 16)) {
+ position.setValid(buf.readUnsignedByte() > 0);
+ }
+
+ if (BitUtil.check(mask, 17)) {
+ int input = buf.readUnsignedByte();
+ position.set(Position.KEY_IGNITION, BitUtil.check(input, 0));
+ position.set(Position.KEY_INPUT, input);
+ }
+
+ if (BitUtil.check(mask, 18)) {
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedByte());
+ }
+
+ int alertId = 0;
+ if (BitUtil.check(mask, 19)) {
+ alertId = buf.readUnsignedByte();
+ if (type == 0x82) {
+ position.set(Position.KEY_ALARM, decodeAlert(alertId));
+ }
+ }
+
+ if (BitUtil.check(mask, 20)) {
+ buf.readUnsignedShort(); // alert mod
+ }
+
+ if (BitUtil.check(mask, 21)) {
+ if (alertId == 59) {
+ position.set(Position.KEY_DRIVER_UNIQUE_ID, ByteBufUtil.hexDump(buf.readSlice(8)));
+ }
+ }
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- String[] values = ((String) msg).split(";");
+ ByteBuf buf = (ByteBuf) msg;
+
+ if (buf.getByte(buf.readerIndex() + 1) == 0) {
+
+ return decodeBinary(channel, remoteAddress, buf);
- if (values[0].length() < 5) {
- return decodeUniversal(channel, remoteAddress, values);
- } else if (values[0].startsWith("ST9")) {
- return decode9(channel, remoteAddress, values);
- } else if (values[0].startsWith("ST4")) {
- return decode4(channel, remoteAddress, values);
} else {
- return decode2356(channel, remoteAddress, values[0].substring(0, 5), values);
+
+ String[] values = buf.toString(StandardCharsets.US_ASCII).split(";");
+ prefix = values[0];
+
+ if (prefix.length() < 5) {
+ return decodeUniversal(channel, remoteAddress, values);
+ } else if (prefix.startsWith("ST9")) {
+ return decode9(channel, remoteAddress, values);
+ } else if (prefix.startsWith("ST4")) {
+ return decode4(channel, remoteAddress, values);
+ } else {
+ return decode2356(channel, remoteAddress, prefix.substring(0, 5), values);
+ }
}
}
diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java
index 6dae42ad5..3b4995110 100644
--- a/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java
@@ -15,6 +15,8 @@
*/
package org.traccar.protocol;
+import io.netty.channel.Channel;
+import org.traccar.BasePipelineFactory;
import org.traccar.StringProtocolEncoder;
import org.traccar.model.Command;
import org.traccar.Protocol;
@@ -25,32 +27,49 @@ public class SuntechProtocolEncoder extends StringProtocolEncoder {
super(protocol);
}
+ private String getPrefix(Channel channel) {
+ String prefix = "SA200CMD";
+ if (channel != null) {
+ SuntechProtocolDecoder protocolDecoder =
+ BasePipelineFactory.getHandler(channel.pipeline(), SuntechProtocolDecoder.class);
+ if (protocolDecoder != null) {
+ String decoderPrefix = protocolDecoder.getPrefix();
+ if (decoderPrefix != null && decoderPrefix.length() > 5) {
+ prefix = decoderPrefix.substring(0, decoderPrefix.length() - 3) + "CMD";
+ }
+ }
+ }
+ return prefix;
+ }
+
@Override
- protected Object encodeCommand(Command command) {
+ protected Object encodeCommand(Channel channel, Command command) {
+
+ String prefix = getPrefix(channel);
switch (command.getType()) {
case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "SA200CMD;{%s};02;Reboot\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, prefix + ";%s;02;Reboot\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "SA200GTR;{%s};02;\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, prefix + ";%s;02;\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_OUTPUT_CONTROL:
if (command.getAttributes().containsKey(Command.KEY_DATA)) {
if (command.getAttributes().get(Command.KEY_DATA).equals("1")) {
- return formatCommand(command, "SA200CMD;{%s};02;Enable{%s}\r",
+ return formatCommand(command, prefix + ";%s;02;Enable%s\r",
Command.KEY_UNIQUE_ID, Command.KEY_INDEX);
} else {
- return formatCommand(command, "SA200CMD;{%s};02;Disable{%s}\r",
+ return formatCommand(command, prefix + ";%s;02;Disable%s\r",
Command.KEY_UNIQUE_ID, Command.KEY_INDEX);
}
}
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "SA200CMD;{%s};02;Enable1\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, prefix + ";%s;02;Enable1\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "SA200CMD;{%s};02;Disable1\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, prefix + ";%s;02;Disable1\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_ALARM_ARM:
- return formatCommand(command, "SA200CMD;{%s};02;Enable2\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, prefix + ";%s;02;Enable2\r", Command.KEY_UNIQUE_ID);
case Command.TYPE_ALARM_DISARM:
- return formatCommand(command, "SA200CMD;{%s};02;Disable2\r", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, prefix + ";%s;02;Disable2\r", Command.KEY_UNIQUE_ID);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/SviasProtocolEncoder.java b/src/main/java/org/traccar/protocol/SviasProtocolEncoder.java
index 2607d7bd1..d218f63ce 100644
--- a/src/main/java/org/traccar/protocol/SviasProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/SviasProtocolEncoder.java
@@ -30,11 +30,11 @@ public class SviasProtocolEncoder extends StringProtocolEncoder {
protected Object encodeCommand(Command command) {
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return formatCommand(command, "{%s}", Command.KEY_DATA);
+ return formatCommand(command, "%s", Command.KEY_DATA);
case Command.TYPE_POSITION_SINGLE:
return formatCommand(command, "AT+STR=1*");
case Command.TYPE_SET_ODOMETER:
- return formatCommand(command, "AT+ODT={%s}*", Command.KEY_DATA);
+ return formatCommand(command, "AT+ODT=%s*", Command.KEY_DATA);
case Command.TYPE_ENGINE_STOP:
return formatCommand(command, "AT+OUT=1,1*");
case Command.TYPE_ENGINE_RESUME:
diff --git a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
index e25f9e11a..b75addfae 100644
--- a/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/T55ProtocolDecoder.java
@@ -96,6 +96,19 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
.any()
.compile();
+ private static final Pattern PATTERN_GPIOP = new PatternBuilder()
+ .text("$GPIOP,")
+ .number("[01]{8},") // inputs
+ .number("[01]{8},") // outputs
+ .number("d+.d+,") // adc 1
+ .number("d+.d+,") // adc 2
+ .number("d+.d+,") // adc 3
+ .number("d+.d+,") // adc 4
+ .number("(d+.d+),") // power
+ .number("(d+.d+)") // battery
+ .any()
+ .compile();
+
private Position position = null;
private Position decodeGprmc(
@@ -225,6 +238,24 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Position decodeGpiop(DeviceSession deviceSession, String sentence) {
+
+ Parser parser = new Parser(PATTERN_GPIOP, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+
+ return position;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -275,6 +306,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder {
return decodeGprma(deviceSession, sentence);
} else if (sentence.startsWith("$TRCCR") && deviceSession != null) {
return decodeTrccr(deviceSession, sentence);
+ } else if (sentence.startsWith("$GPIOP")) {
+ return decodeGpiop(deviceSession, sentence);
}
return null;
diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 485df833a..cc2868a20 100644
--- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -157,7 +157,19 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
}
if (readable) {
- position.set(Position.KEY_RESULT, buf.readSlice(length).toString(StandardCharsets.US_ASCII));
+ String data = buf.readSlice(length).toString(StandardCharsets.US_ASCII).trim();
+ if (data.startsWith("UUUUww") && data.endsWith("SSS")) {
+ String[] values = data.substring(6, data.length() - 4).split(";");
+ for (int i = 0; i < 8; i++) {
+ position.set("axle" + (i + 1), Double.parseDouble(values[i]));
+ }
+ position.set("loadTruck", Double.parseDouble(values[8]));
+ position.set("loadTrailer", Double.parseDouble(values[9]));
+ position.set("totalTruck", Double.parseDouble(values[10]));
+ position.set("totalTrailer", Double.parseDouble(values[11]));
+ } else {
+ position.set(Position.KEY_RESULT, data);
+ }
} else {
position.set(Position.KEY_RESULT, ByteBufUtil.hexDump(buf.readSlice(length)));
}
@@ -188,6 +200,9 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
case 9:
position.set(Position.PREFIX_ADC + 1, readValue(buf, length, false));
break;
+ case 10:
+ position.set(Position.PREFIX_ADC + 2, readValue(buf, length, false));
+ break;
case 17:
position.set("axisX", readValue(buf, length, true));
break;
diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java
index a8aa84105..5d7e63920 100644
--- a/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/Tk103ProtocolEncoder.java
@@ -50,7 +50,7 @@ public class Tk103ProtocolEncoder extends StringProtocolEncoder {
if (alternative) {
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return formatAlt(command, "{%s}", Command.KEY_DATA);
+ return formatAlt(command, "%s", Command.KEY_DATA);
case Command.TYPE_GET_VERSION:
return formatAlt(command, "*about*");
case Command.TYPE_POWER_OFF:
@@ -74,36 +74,36 @@ public class Tk103ProtocolEncoder extends StringProtocolEncoder {
case Command.TYPE_ALARM_SOS:
return formatAlt(command, command.getBoolean(Command.KEY_ENABLE) ? "*soson*" : "*sosoff*");
case Command.TYPE_SET_CONNECTION:
- return formatAlt(command, "*setip*%s*{%s}*",
- command.getString(Command.KEY_SERVER).replace(".", "*"), Command.KEY_PORT);
+ String server = command.getString(Command.KEY_SERVER).replace(".", "*");
+ return formatAlt(command, "*setip*" + server + "*%s*", Command.KEY_PORT);
case Command.TYPE_SOS_NUMBER:
- return formatAlt(command, "*master*{%s}*{%s}*", Command.KEY_DEVICE_PASSWORD, Command.KEY_PHONE);
+ return formatAlt(command, "*master*%s*%s*", Command.KEY_DEVICE_PASSWORD, Command.KEY_PHONE);
default:
return null;
}
} else {
switch (command.getType()) {
case Command.TYPE_CUSTOM:
- return formatCommand(command, "({%s}{%s})", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
+ return formatCommand(command, "(%s%s)", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
case Command.TYPE_GET_VERSION:
- return formatCommand(command, "({%s}AP07)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAP07)", Command.KEY_UNIQUE_ID);
case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "({%s}AT00)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAT00)", Command.KEY_UNIQUE_ID);
case Command.TYPE_SET_ODOMETER:
- return formatCommand(command, "({%s}AX01)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAX01)", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "({%s}AP00)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAP00)", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_PERIODIC:
- return formatCommand(command, "({%s}AR00%s0000)", Command.KEY_UNIQUE_ID,
- String.format("%04X", command.getInteger(Command.KEY_FREQUENCY)));
+ String frequency = String.format("%04X", command.getInteger(Command.KEY_FREQUENCY));
+ return formatCommand(command, "(%sAR00" + frequency + "0000)", Command.KEY_UNIQUE_ID);
case Command.TYPE_POSITION_STOP:
- return formatCommand(command, "({%s}AR0000000000)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAR0000000000)", Command.KEY_UNIQUE_ID);
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "({%s}AV010)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAV010)", Command.KEY_UNIQUE_ID);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "({%s}AV011)", Command.KEY_UNIQUE_ID);
+ return formatCommand(command, "(%sAV011)", Command.KEY_UNIQUE_ID);
case Command.TYPE_OUTPUT_CONTROL:
- return formatCommand(command, "({%s}AV00{%s})", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
+ return formatCommand(command, "(%sAV00%s)", Command.KEY_UNIQUE_ID, Command.KEY_DATA);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
index 2a06d35e5..3ce6438f8 100644
--- a/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tlt2hProtocolDecoder.java
@@ -36,10 +36,18 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
}
private static final Pattern PATTERN_HEADER = new PatternBuilder()
- .number("#(d+)#") // imei
- .any()
- .expression("#([^#]+)#") // status
- .number("d+") // number of records
+ .number("#(d+)") // imei
+ .expression("#[^#]*") // user
+ .number("#d*") // password
+ .groupBegin()
+ .number("#([01])") // door
+ .number("#(d+)") // fuel voltage
+ .number("#(d+)") // power
+ .number("#(d+)") // battery
+ .number("#(d+)") // temperature
+ .groupEnd("?")
+ .expression("#([^#]+)") // status
+ .number("#d+") // number of records
.compile();
private static final Pattern PATTERN_POSITION = new PatternBuilder()
@@ -114,6 +122,19 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ Boolean door = null;
+ Double adc = null;
+ Double power = null;
+ Double battery = null;
+ Double temperature = null;
+ if (parser.hasNext(5)) {
+ door = parser.nextInt() == 1;
+ adc = parser.nextInt() * 0.1;
+ power = parser.nextInt() * 0.1;
+ battery = parser.nextInt() * 0.1;
+ temperature = parser.nextInt() * 0.1;
+ }
+
String status = parser.next();
String[] messages = sentence.substring(sentence.indexOf('\n') + 1).split("\r\n");
@@ -140,6 +161,11 @@ public class Tlt2hProtocolDecoder extends BaseProtocolDecoder {
dateBuilder.setDateReverse(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
position.setTime(dateBuilder.getDate());
+ position.set(Position.KEY_DOOR, door);
+ position.set(Position.PREFIX_ADC + 1, adc);
+ position.set(Position.KEY_POWER, power);
+ position.set(Position.KEY_BATTERY, battery);
+ position.set(Position.PREFIX_TEMP + 1, temperature);
decodeStatus(position, status);
positions.add(position);
diff --git a/src/main/java/org/traccar/protocol/TopinProtocol.java b/src/main/java/org/traccar/protocol/TopinProtocol.java
new file mode 100644
index 000000000..844dd7518
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TopinProtocol.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+public class TopinProtocol extends BaseProtocol {
+
+ public TopinProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new TopinProtocolDecoder(TopinProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
new file mode 100644
index 000000000..eee0e9ae8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/TopinProtocolDecoder.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2019 - 2020 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BcdUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.model.CellTower;
+import org.traccar.model.Network;
+import org.traccar.model.Position;
+import org.traccar.model.WifiAccessPoint;
+
+import java.net.SocketAddress;
+import java.util.TimeZone;
+
+public class TopinProtocolDecoder extends BaseProtocolDecoder {
+
+ public TopinProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN = 0x01;
+ public static final int MSG_GPS = 0x10;
+ public static final int MSG_GPS_OFFLINE = 0x11;
+ public static final int MSG_STATUS = 0x13;
+ public static final int MSG_WIFI_OFFLINE = 0x17;
+ public static final int MSG_WIFI = 0x69;
+
+ private void sendResponse(Channel channel, int length, int type, ByteBuf content) {
+ if (channel != null) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeShort(0x7878);
+ response.writeByte(length);
+ response.writeByte(type);
+ response.writeBytes(content);
+ response.writeByte('\r');
+ response.writeByte('\n');
+ content.release();
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+ int length = buf.readUnsignedByte();
+
+ int type = buf.readUnsignedByte();
+
+ DeviceSession deviceSession;
+ if (type == MSG_LOGIN) {
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(1);
+ deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(deviceSession != null ? 0x01 : 0x44);
+ sendResponse(channel, length, type, content);
+ return null;
+ } else {
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+ }
+
+ if (type == MSG_GPS || type == MSG_GPS_OFFLINE) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ ByteBuf time = buf.slice(buf.readerIndex(), 6);
+
+ Gt06ProtocolDecoder.decodeGps(position, buf, false, TimeZone.getTimeZone("UTC"));
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeBytes(time);
+ sendResponse(channel, length, type, content);
+
+ return position;
+
+ } else if (type == MSG_STATUS) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ getLastLocation(position, null);
+
+ int battery = buf.readUnsignedByte();
+ int firmware = buf.readUnsignedByte();
+ int timezone = buf.readUnsignedByte();
+ int interval = buf.readUnsignedByte();
+ int signal = 0;
+ if (length >= 7) {
+ signal = buf.readUnsignedByte();
+ position.set(Position.KEY_RSSI, signal);
+ }
+
+ position.set(Position.KEY_BATTERY_LEVEL, battery);
+ position.set(Position.KEY_VERSION_FW, firmware);
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeByte(battery);
+ content.writeByte(firmware);
+ content.writeByte(timezone);
+ content.writeByte(interval);
+ if (length >= 7) {
+ content.writeByte(signal);
+ }
+ sendResponse(channel, length, type, content);
+
+ return position;
+
+ } else if (type == MSG_WIFI || type == MSG_WIFI_OFFLINE) {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ ByteBuf time = buf.readSlice(6);
+ DateBuilder dateBuilder = new DateBuilder()
+ .setYear(BcdUtil.readInteger(time, 2))
+ .setMonth(BcdUtil.readInteger(time, 2))
+ .setDay(BcdUtil.readInteger(time, 2))
+ .setHour(BcdUtil.readInteger(time, 2))
+ .setMinute(BcdUtil.readInteger(time, 2))
+ .setSecond(BcdUtil.readInteger(time, 2));
+ time.resetReaderIndex();
+
+ getLastLocation(position, dateBuilder.getDate());
+
+ Network network = new Network();
+ for (int i = 0; i < length; i++) {
+ String mac = String.format("%02x:%02x:%02x:%02x:%02x:%02x",
+ buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte(),
+ buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte());
+ network.addWifiAccessPoint(WifiAccessPoint.from(mac, buf.readUnsignedByte()));
+ }
+
+ int cellCount = buf.readUnsignedByte();
+ int mcc = buf.readUnsignedShort();
+ int mnc = buf.readUnsignedByte();
+ for (int i = 0; i < cellCount; i++) {
+ network.addCellTower(CellTower.from(
+ mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedShort(), buf.readUnsignedByte()));
+ }
+
+ position.setNetwork(network);
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeBytes(time);
+ sendResponse(channel, length, type, content);
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java b/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
index 3bbb92031..a96dd1ee3 100644
--- a/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/TotemProtocolEncoder.java
@@ -34,9 +34,9 @@ public class TotemProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
// Assuming PIN 8 (Output C) is the power wire, like manual says but it can be PIN 5,7,8
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "*{%s},025,C,1#", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "*%s,025,C,1#", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "*{%s},025,C,0#", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "*%s,025,C,0#", Command.KEY_DEVICE_PASSWORD);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
index 87b44a4b2..4f6854098 100644
--- a/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/TzoneProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import org.traccar.DeviceSession;
import org.traccar.Protocol;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
@@ -67,7 +68,17 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
return false;
}
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ if (hardware == 0x413) {
+ buf.readUnsignedByte(); // status
+ } else {
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ }
+
+ if (hardware == 0x413) {
+ position.setFixTime(new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()).getDate());
+ }
double lat;
double lon;
@@ -80,25 +91,39 @@ public class TzoneProtocolDecoder extends BaseProtocolDecoder {
lon = buf.readUnsignedInt() / 100000.0 / 60.0;
}
- position.setFixTime(new DateBuilder()
- .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
- .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()).getDate());
+ if (hardware == 0x413) {
- position.setSpeed(buf.readUnsignedShort() * 0.01);
+ position.set(Position.KEY_HDOP, buf.readUnsignedShort() * 0.1);
- position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium());
+ position.setAltitude(buf.readUnsignedShort());
+ position.setCourse(buf.readUnsignedShort());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));
+
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+
+ } else {
+
+ position.setFixTime(new DateBuilder()
+ .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte())
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()).getDate());
+
+ position.setSpeed(buf.readUnsignedShort() * 0.01);
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium());
+
+ int flags = buf.readUnsignedShort();
+ position.setCourse(BitUtil.to(flags, 9));
+ if (!BitUtil.check(flags, 10)) {
+ lat = -lat;
+ }
+ position.setLatitude(lat);
+ if (BitUtil.check(flags, 9)) {
+ lon = -lon;
+ }
+ position.setLongitude(lon);
+ position.setValid(BitUtil.check(flags, 11));
- int flags = buf.readUnsignedShort();
- position.setCourse(BitUtil.to(flags, 9));
- if (!BitUtil.check(flags, 10)) {
- lat = -lat;
- }
- position.setLatitude(lat);
- if (BitUtil.check(flags, 9)) {
- lon = -lon;
}
- position.setLongitude(lon);
- position.setValid(BitUtil.check(flags, 11));
buf.readerIndex(blockEnd);
diff --git a/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java
index 0a2a59e23..7fec0bf8b 100644
--- a/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/UlbotechProtocolDecoder.java
@@ -237,12 +237,13 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
case DATA_GPS:
hasLocation = true;
- position.setValid(true);
position.setLatitude(buf.readInt() / 1000000.0);
position.setLongitude(buf.readInt() / 1000000.0);
position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort()));
position.setCourse(buf.readUnsignedShort());
- position.set(Position.KEY_HDOP, buf.readUnsignedShort());
+ int hdop = buf.readUnsignedShort();
+ position.setValid(hdop < 9999);
+ position.set(Position.KEY_HDOP, hdop * 0.01);
break;
case DATA_LBS:
diff --git a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
index 873b22006..a17003fc8 100644
--- a/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/UproProtocolDecoder.java
@@ -171,12 +171,31 @@ public class UproProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(
Integer.parseInt(data.readSlice(6).toString(StandardCharsets.US_ASCII)) * 0.1);
break;
+ case 'J':
+ if (data.readableBytes() == 6) {
+ char index = (char) data.readUnsignedByte();
+ int status = data.readUnsignedByte();
+ double value = Integer.parseInt(data.readSlice(4).toString(StandardCharsets.US_ASCII)) * 0.1;
+ if (BitUtil.check(status, 0)) {
+ value = -value;
+ }
+ position.set(Position.PREFIX_TEMP + index, value);
+ }
+ break;
case 'K':
position.set("statusExtended", data.toString(StandardCharsets.US_ASCII));
break;
case 'M':
- position.set(Position.KEY_BATTERY_LEVEL,
- Integer.parseInt(data.readSlice(3).toString(StandardCharsets.US_ASCII)) * 0.1);
+ if (data.readableBytes() == 3) {
+ position.set(Position.KEY_BATTERY_LEVEL,
+ Integer.parseInt(data.readSlice(3).toString(StandardCharsets.US_ASCII)) * 0.1);
+ } else if (data.readableBytes() == 4) {
+ char index = (char) data.readUnsignedByte();
+ data.readUnsignedByte(); // status
+ position.set(
+ "humidity" + index,
+ Integer.parseInt(data.readSlice(2).toString(StandardCharsets.US_ASCII)));
+ }
break;
case 'N':
position.set(Position.KEY_RSSI,
diff --git a/src/main/java/org/traccar/protocol/VnetProtocol.java b/src/main/java/org/traccar/protocol/VnetProtocol.java
new file mode 100644
index 000000000..0fed30e13
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/VnetProtocol.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 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.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.nio.ByteOrder;
+
+public class VnetProtocol extends BaseProtocol {
+
+ public VnetProtocol() {
+ addServer(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1500, 4, 2, 12, 0, true));
+ pipeline.addLast(new VnetProtocolDecoder(VnetProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java b/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java
new file mode 100644
index 000000000..1ee00bd3f
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/VnetProtocolDecoder.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2019 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+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.BcdUtil;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+
+public class VnetProtocolDecoder extends BaseProtocolDecoder {
+
+ public VnetProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ public static final int MSG_LOGIN = 0x0000;
+ public static final int MSG_LBS = 0x32;
+ public static final int MSG_GPS = 0x33;
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ buf.skipBytes(2); // header
+ int type = BitUtil.to(buf.readUnsignedShortLE(), 15);
+ buf.readUnsignedShortLE(); // length
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDateReverse(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2))
+ .setTime(BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2), BcdUtil.readInteger(buf, 2));
+
+ if (type == MSG_LOGIN) {
+
+ String imei = ByteBufUtil.hexDump(buf.readSlice(8)).substring(0, 15);
+ getDeviceSession(channel, remoteAddress, imei);
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(
+ buf.retainedSlice(0, buf.writerIndex()), channel.remoteAddress()));
+ }
+
+ } else if (type == MSG_GPS) {
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setTime(dateBuilder.getDate());
+
+ int value;
+ int degrees;
+
+ value = BcdUtil.readInteger(buf, 8);
+ degrees = value / 1000000;
+ double lat = degrees + value % 1000000 * 0.0001 / 60;
+
+ value = BcdUtil.readInteger(buf, 10);
+ degrees = value / 10000000;
+ double lon = degrees + value % 10000000 * 0.00001 / 60;
+
+ int flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(flags, 0));
+ position.setLatitude(BitUtil.check(flags, 1) ? lat : -lat);
+ position.setLongitude(BitUtil.check(flags, 2) ? lon : -lon);
+
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedIntLE());
+ position.setCourse(buf.readUnsignedByte() * 2);
+
+ return position;
+
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
index 70b207e9b..0647afdee 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocolDecoder.java
@@ -249,8 +249,7 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
}
}
- } else if (type.equals("UD") || type.equals("UD2") || type.equals("UD3")
- || type.equals("AL") || type.equals("WT")) {
+ } else if (type.startsWith("UD") || type.equals("AL") || type.equals("WT")) {
Position position = decodePosition(deviceSession, buf.toString(StandardCharsets.US_ASCII));
@@ -267,7 +266,10 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
sendResponse(channel, id, index, "TKQ");
- } else if (type.equals("PULSE") || type.equals("heart") || type.equals("bphrt")) {
+ } else if (type.equalsIgnoreCase("PULSE")
+ || type.equalsIgnoreCase("HEART")
+ || type.equalsIgnoreCase("BLOOD")
+ || type.equalsIgnoreCase("BPHRT")) {
if (buf.isReadable()) {
@@ -279,11 +281,14 @@ public class WatchProtocolDecoder extends BaseProtocolDecoder {
String[] values = buf.toString(StandardCharsets.US_ASCII).split(",");
int valueIndex = 0;
- if (type.equals("bphrt")) {
+ if (type.equalsIgnoreCase("BPHRT") || type.equalsIgnoreCase("BLOOD")) {
position.set("pressureHigh", values[valueIndex++]);
position.set("pressureLow", values[valueIndex++]);
}
- position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[valueIndex]));
+
+ if (valueIndex <= values.length - 1) {
+ position.set(Position.KEY_HEART_RATE, Integer.parseInt(values[valueIndex]));
+ }
return position;
diff --git a/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java b/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java
index b433dfd2a..f285267ba 100644
--- a/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/WatchProtocolEncoder.java
@@ -137,33 +137,33 @@ public class WatchProtocolEncoder extends StringProtocolEncoder implements Strin
case Command.TYPE_POSITION_SINGLE:
return formatTextCommand(channel, command, "RG");
case Command.TYPE_SOS_NUMBER:
- return formatTextCommand(channel, command, "SOS{%s},{%s}", Command.KEY_INDEX, Command.KEY_PHONE);
+ return formatTextCommand(channel, command, "SOS%s,%s", Command.KEY_INDEX, Command.KEY_PHONE);
case Command.TYPE_ALARM_SOS:
- return formatTextCommand(channel, command, "SOSSMS,{%s}", Command.KEY_ENABLE);
+ return formatTextCommand(channel, command, "SOSSMS,%s", Command.KEY_ENABLE);
case Command.TYPE_ALARM_BATTERY:
- return formatTextCommand(channel, command, "LOWBAT,{%s}", Command.KEY_ENABLE);
+ return formatTextCommand(channel, command, "LOWBAT,%s", Command.KEY_ENABLE);
case Command.TYPE_REBOOT_DEVICE:
return formatTextCommand(channel, command, "RESET");
case Command.TYPE_POWER_OFF:
return formatTextCommand(channel, command, "POWEROFF");
case Command.TYPE_ALARM_REMOVE:
- return formatTextCommand(channel, command, "REMOVE,{%s}", Command.KEY_ENABLE);
+ return formatTextCommand(channel, command, "REMOVE,%s", Command.KEY_ENABLE);
case Command.TYPE_SILENCE_TIME:
- return formatTextCommand(channel, command, "SILENCETIME,{%s}", Command.KEY_DATA);
+ return formatTextCommand(channel, command, "SILENCETIME,%s", Command.KEY_DATA);
case Command.TYPE_ALARM_CLOCK:
- return formatTextCommand(channel, command, "REMIND,{%s}", Command.KEY_DATA);
+ return formatTextCommand(channel, command, "REMIND,%s", Command.KEY_DATA);
case Command.TYPE_SET_PHONEBOOK:
- return formatTextCommand(channel, command, "PHB,{%s}", Command.KEY_DATA);
+ return formatTextCommand(channel, command, "PHB,%s", Command.KEY_DATA);
case Command.TYPE_MESSAGE:
- return formatTextCommand(channel, command, "MESSAGE,{%s}", Command.KEY_MESSAGE);
+ return formatTextCommand(channel, command, "MESSAGE,%s", Command.KEY_MESSAGE);
case Command.TYPE_VOICE_MESSAGE:
return formatBinaryCommand(channel, command, "TK,", getBinaryData(command));
case Command.TYPE_POSITION_PERIODIC:
- return formatTextCommand(channel, command, "UPLOAD,{%s}", Command.KEY_FREQUENCY);
+ return formatTextCommand(channel, command, "UPLOAD,%s", Command.KEY_FREQUENCY);
case Command.TYPE_SET_TIMEZONE:
- return formatTextCommand(channel, command, "LZ,,{%s}", Command.KEY_TIMEZONE);
+ return formatTextCommand(channel, command, "LZ,%s,%s", Command.KEY_LANGUAGE, Command.KEY_TIMEZONE);
case Command.TYPE_SET_INDICATOR:
- return formatTextCommand(channel, command, "FLOWER,{%s}", Command.KEY_DATA);
+ return formatTextCommand(channel, command, "FLOWER,%s", Command.KEY_DATA);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/WialonProtocolEncoder.java b/src/main/java/org/traccar/protocol/WialonProtocolEncoder.java
index c45edf00d..93086bf8a 100644
--- a/src/main/java/org/traccar/protocol/WialonProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/WialonProtocolEncoder.java
@@ -32,11 +32,11 @@ public class WialonProtocolEncoder extends StringProtocolEncoder {
case Command.TYPE_REBOOT_DEVICE:
return formatCommand(command, "reboot\r\n");
case Command.TYPE_SEND_USSD:
- return formatCommand(command, "USSD:{%s}\r\n", Command.KEY_PHONE);
+ return formatCommand(command, "USSD:%s\r\n", Command.KEY_PHONE);
case Command.TYPE_IDENTIFICATION:
return formatCommand(command, "VER?\r\n");
case Command.TYPE_OUTPUT_CONTROL:
- return formatCommand(command, "L{%s}={%s}\r\n", Command.KEY_INDEX, Command.KEY_DATA);
+ return formatCommand(command, "L%s=%s\r\n", Command.KEY_INDEX, Command.KEY_DATA);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java b/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
index e9bb23d15..21f1ee321 100644
--- a/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/WondexProtocolEncoder.java
@@ -32,17 +32,17 @@ public class WondexProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_REBOOT_DEVICE:
- return formatCommand(command, "$WP+REBOOT={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+REBOOT=%s", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_GET_DEVICE_STATUS:
- return formatCommand(command, "$WP+TEST={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+TEST=%s", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_GET_MODEM_STATUS:
- return formatCommand(command, "$WP+GSMINFO={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+GSMINFO=%s", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_IDENTIFICATION:
- return formatCommand(command, "$WP+IMEI={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+IMEI=%s", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_POSITION_SINGLE:
- return formatCommand(command, "$WP+GETLOCATION={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+GETLOCATION=%s", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_GET_VERSION:
- return formatCommand(command, "$WP+VER={%s}", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "$WP+VER=%s", Command.KEY_DEVICE_PASSWORD);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/XexunProtocolEncoder.java b/src/main/java/org/traccar/protocol/XexunProtocolEncoder.java
index fc849fe15..4f2707c2a 100644
--- a/src/main/java/org/traccar/protocol/XexunProtocolEncoder.java
+++ b/src/main/java/org/traccar/protocol/XexunProtocolEncoder.java
@@ -32,9 +32,9 @@ public class XexunProtocolEncoder extends StringProtocolEncoder {
switch (command.getType()) {
case Command.TYPE_ENGINE_STOP:
- return formatCommand(command, "powercar{%s} 11", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "powercar%s 11", Command.KEY_DEVICE_PASSWORD);
case Command.TYPE_ENGINE_RESUME:
- return formatCommand(command, "powercar{%s} 00", Command.KEY_DEVICE_PASSWORD);
+ return formatCommand(command, "powercar%s 00", Command.KEY_DEVICE_PASSWORD);
default:
return null;
}
diff --git a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
index 938394d6b..69e5b7372 100644
--- a/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Xrb28ProtocolDecoder.java
@@ -47,7 +47,7 @@ public class Xrb28ProtocolDecoder extends BaseProtocolDecoder {
.expression("..,") // vendor
.number("d{15},") // imei
.expression("..,") // type
- .number("0,") // reserved
+ .number("[01],") // reserved
.number("(dd)(dd)(dd).d+,") // time (hhmmss)
.expression("([AV]),") // validity
.number("(dd)(dd.d+),") // latitude