aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debug.xml4
-rw-r--r--schema/changelog-3.15.xml7
-rw-r--r--setup/default.xml4
-rw-r--r--src/org/traccar/BasePipelineFactory.java2
-rw-r--r--src/org/traccar/Context.java85
-rw-r--r--src/org/traccar/GeocoderHandler.java11
-rw-r--r--src/org/traccar/api/resource/ServerResource.java9
-rw-r--r--src/org/traccar/database/DataManager.java20
-rw-r--r--src/org/traccar/database/LdapProvider.java176
-rw-r--r--src/org/traccar/database/NotificationManager.java8
-rw-r--r--src/org/traccar/geocoder/BingMapsGeocoder.java5
-rw-r--r--src/org/traccar/geocoder/FactualGeocoder.java5
-rw-r--r--src/org/traccar/geocoder/GeocodeFarmGeocoder.java6
-rw-r--r--src/org/traccar/geocoder/Geocoder.java4
-rw-r--r--src/org/traccar/geocoder/GisgraphyGeocoder.java10
-rw-r--r--src/org/traccar/geocoder/GoogleGeocoder.java6
-rw-r--r--src/org/traccar/geocoder/JsonGeocoder.java85
-rw-r--r--src/org/traccar/geocoder/MapQuestGeocoder.java5
-rw-r--r--src/org/traccar/geocoder/NominatimGeocoder.java6
-rw-r--r--src/org/traccar/geocoder/OpenCageGeocoder.java6
-rw-r--r--src/org/traccar/model/User.java10
-rw-r--r--src/org/traccar/protocol/DwayProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/GenxProtocolDecoder.java1
-rw-r--r--src/org/traccar/protocol/Gl200TextProtocolDecoder.java134
-rw-r--r--src/org/traccar/protocol/Ivt401Protocol.java45
-rw-r--r--src/org/traccar/protocol/Ivt401ProtocolDecoder.java97
-rw-r--r--src/org/traccar/protocol/Jt600ProtocolDecoder.java14
-rw-r--r--src/org/traccar/protocol/MegastekProtocolDecoder.java40
-rw-r--r--src/org/traccar/protocol/MeiligaoProtocolDecoder.java3
-rw-r--r--src/org/traccar/protocol/OkoProtocol.java45
-rw-r--r--src/org/traccar/protocol/OkoProtocolDecoder.java100
-rw-r--r--src/org/traccar/reports/ReportUtils.java22
-rw-r--r--test/org/traccar/geocoder/GeocoderTest.java46
-rw-r--r--test/org/traccar/protocol/DwayProtocolDecoderTest.java2
-rw-r--r--test/org/traccar/protocol/GenxProtocolDecoderTest.java7
-rw-r--r--test/org/traccar/protocol/Gl200TextProtocolDecoderTest.java12
-rw-r--r--test/org/traccar/protocol/Gt06ProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/Ivt401ProtocolDecoderTest.java33
-rw-r--r--test/org/traccar/protocol/Jt600ProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/OkoProtocolDecoderTest.java33
40 files changed, 974 insertions, 144 deletions
diff --git a/debug.xml b/debug.xml
index 282350b8a..e8647b6fe 100644
--- a/debug.xml
+++ b/debug.xml
@@ -21,4 +21,8 @@
<entry key='intellitrac.port'>6037</entry>
+ <!--<entry key='ldap.enable'>true</entry>
+ <entry key='ldap.url'>ldap://ldap.forumsys.com:389</entry>
+ <entry key='ldap.context'>dc=example,dc=com</entry>-->
+
</properties>
diff --git a/schema/changelog-3.15.xml b/schema/changelog-3.15.xml
index 9756fe696..202ae21eb 100644
--- a/schema/changelog-3.15.xml
+++ b/schema/changelog-3.15.xml
@@ -133,5 +133,12 @@
<addForeignKeyConstraint baseTableName="device_notification" baseColumnNames="deviceid" constraintName="fk_device_notification_deviceid" referencedTableName="devices" referencedColumnNames="id" onDelete="CASCADE" />
<addForeignKeyConstraint baseTableName="device_notification" baseColumnNames="notificationid" constraintName="fk_device_notification_notificationid" referencedTableName="notifications" referencedColumnNames="id" onDelete="CASCADE" />
+ <dropNotNullConstraint tableName="users" columnName="hashedpassword" columnDataType="VARCHAR(128)" />
+ <dropNotNullConstraint tableName="users" columnName="salt" columnDataType="VARCHAR(128)" />
+
+ <addColumn tableName="users">
+ <column name="login" type="VARCHAR(128)" />
+ </addColumn>
+
</changeSet>
</databaseChangeLog>
diff --git a/setup/default.xml b/setup/default.xml
index 8752432c6..3424e8187 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -35,7 +35,7 @@
<entry key='database.loginUser'>
SELECT * FROM users
- WHERE email = :email
+ WHERE email = :email OR login = :email
</entry>
<entry key='database.selectPositions'>
@@ -220,5 +220,7 @@
<entry key='flespi.port'>5149</entry>
<entry key='dway.port'>5150</entry>
<entry key='recoda.port'>5151</entry>
+ <entry key='oko.port'>5152</entry>
+ <entry key='ivt401.port'>5153</entry>
</properties>
diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java
index 771ab8acb..aa188d209 100644
--- a/src/org/traccar/BasePipelineFactory.java
+++ b/src/org/traccar/BasePipelineFactory.java
@@ -141,7 +141,7 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
filterHandler = new FilterHandler();
}
- if (Context.getGeocoder() != null) {
+ if (Context.getGeocoder() != null && !Context.getConfig().getBoolean("geocoder.ignorePositions")) {
geocoderHandler = new GeocoderHandler(
Context.getGeocoder(),
Context.getConfig().getBoolean("geocoder.processInvalidPositions"));
diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java
index 5d2d5a86a..a03b14b1b 100644
--- a/src/org/traccar/Context.java
+++ b/src/org/traccar/Context.java
@@ -34,6 +34,7 @@ import org.traccar.database.DataManager;
import org.traccar.database.DeviceManager;
import org.traccar.database.DriversManager;
import org.traccar.database.IdentityManager;
+import org.traccar.database.LdapProvider;
import org.traccar.database.MediaManager;
import org.traccar.database.NotificationManager;
import org.traccar.database.PermissionsManager;
@@ -43,6 +44,7 @@ import org.traccar.database.StatisticsManager;
import org.traccar.database.UsersManager;
import org.traccar.events.MotionEventHandler;
import org.traccar.events.OverspeedEventHandler;
+import org.traccar.geocoder.AddressFormat;
import org.traccar.geocoder.BingMapsGeocoder;
import org.traccar.geocoder.FactualGeocoder;
import org.traccar.geocoder.GeocodeFarmGeocoder;
@@ -110,6 +112,12 @@ public final class Context {
return dataManager;
}
+ private static LdapProvider ldapProvider;
+
+ public static LdapProvider getLdapProvider() {
+ return ldapProvider;
+ }
+
private static MediaManager mediaManager;
public static MediaManager getMediaManager() {
@@ -265,6 +273,41 @@ public final class Context {
config.getDouble("event.motion.speedThreshold", 0.01));
}
+ public static Geocoder initGeocoder() {
+ String type = config.getString("geocoder.type", "google");
+ String url = config.getString("geocoder.url");
+ String key = config.getString("geocoder.key");
+ String language = config.getString("geocoder.language");
+
+ String formatString = config.getString("geocoder.format");
+ AddressFormat addressFormat;
+ if (formatString != null) {
+ addressFormat = new AddressFormat(formatString);
+ } else {
+ addressFormat = new AddressFormat();
+ }
+
+ int cacheSize = config.getInteger("geocoder.cacheSize");
+ switch (type) {
+ case "nominatim":
+ return new NominatimGeocoder(url, key, language, cacheSize, addressFormat);
+ case "gisgraphy":
+ return new GisgraphyGeocoder(url, cacheSize, addressFormat);
+ case "mapquest":
+ return new MapQuestGeocoder(url, key, cacheSize, addressFormat);
+ case "opencage":
+ return new OpenCageGeocoder(url, key, cacheSize, addressFormat);
+ case "bingmaps":
+ return new BingMapsGeocoder(url, key, cacheSize, addressFormat);
+ case "factual":
+ return new FactualGeocoder(url, key, cacheSize, addressFormat);
+ case "geocodefarm":
+ return new GeocodeFarmGeocoder(key, language, cacheSize, addressFormat);
+ default:
+ return new GoogleGeocoder(key, language, cacheSize, addressFormat);
+ }
+ }
+
public static void init(String[] arguments) throws Exception {
config = new Config();
@@ -290,6 +333,10 @@ public final class Context {
dataManager = new DataManager(config);
}
+ if (config.getBoolean("ldap.enable")) {
+ ldapProvider = new LdapProvider(config);
+ }
+
if (config.hasKey("media.path")) {
mediaManager = new MediaManager(config);
}
@@ -303,7 +350,7 @@ public final class Context {
identityManager = deviceManager;
if (config.getBoolean("geocoder.enable")) {
- initReverseGeocoder();
+ geocoder = initGeocoder();
}
if (config.getBoolean("geolocation.enable")) {
@@ -401,42 +448,6 @@ public final class Context {
Context.getConfig().getBoolean("event.overspeed.notRepeat"));
}
- private static void initReverseGeocoder() {
-
- String type = config.getString("geocoder.type", "google");
- String url = config.getString("geocoder.url");
- String key = config.getString("geocoder.key");
- String language = config.getString("geocoder.language");
-
- int cacheSize = config.getInteger("geocoder.cacheSize");
- switch (type) {
- case "nominatim":
- geocoder = new NominatimGeocoder(url, key, language, cacheSize);
- break;
- case "gisgraphy":
- geocoder = new GisgraphyGeocoder(url, cacheSize);
- break;
- case "mapquest":
- geocoder = new MapQuestGeocoder(url, key, cacheSize);
- break;
- case "opencage":
- geocoder = new OpenCageGeocoder(url, key, cacheSize);
- break;
- case "bingmaps":
- geocoder = new BingMapsGeocoder(url, key, cacheSize);
- break;
- case "factual":
- geocoder = new FactualGeocoder(url, key, cacheSize);
- break;
- case "geocodefarm":
- geocoder = new GeocodeFarmGeocoder(key, language, cacheSize);
- break;
- default:
- geocoder = new GoogleGeocoder(key, language, cacheSize);
- break;
- }
- }
-
public static void init(IdentityManager testIdentityManager) {
config = new Config();
objectMapper = new ObjectMapper();
diff --git a/src/org/traccar/GeocoderHandler.java b/src/org/traccar/GeocoderHandler.java
index a211d1a23..6b55e8162 100644
--- a/src/org/traccar/GeocoderHandler.java
+++ b/src/org/traccar/GeocoderHandler.java
@@ -20,7 +20,6 @@ import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelUpstreamHandler;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
-import org.traccar.geocoder.AddressFormat;
import org.traccar.geocoder.Geocoder;
import org.traccar.helper.Log;
import org.traccar.model.Position;
@@ -29,20 +28,12 @@ public class GeocoderHandler implements ChannelUpstreamHandler {
private final Geocoder geocoder;
private final boolean processInvalidPositions;
- private final AddressFormat addressFormat;
private final int geocoderReuseDistance;
public GeocoderHandler(Geocoder geocoder, boolean processInvalidPositions) {
this.geocoder = geocoder;
this.processInvalidPositions = processInvalidPositions;
- String formatString = Context.getConfig().getString("geocoder.format");
- if (formatString != null) {
- addressFormat = new AddressFormat(formatString);
- } else {
- addressFormat = new AddressFormat();
- }
-
geocoderReuseDistance = Context.getConfig().getInteger("geocoder.reuseDistance", 0);
}
@@ -70,7 +61,7 @@ public class GeocoderHandler implements ChannelUpstreamHandler {
Context.getStatisticsManager().registerGeocoderRequest();
- geocoder.getAddress(addressFormat, position.getLatitude(), position.getLongitude(),
+ geocoder.getAddress(position.getLatitude(), position.getLongitude(),
new Geocoder.ReverseGeocoderCallback() {
@Override
public void onSuccess(String address) {
diff --git a/src/org/traccar/api/resource/ServerResource.java b/src/org/traccar/api/resource/ServerResource.java
index 034a5c492..c0914995b 100644
--- a/src/org/traccar/api/resource/ServerResource.java
+++ b/src/org/traccar/api/resource/ServerResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 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,6 +25,7 @@ import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.sql.SQLException;
@@ -47,4 +48,10 @@ public class ServerResource extends BaseResource {
return Response.ok(entity).build();
}
+ @Path("geocode")
+ @GET
+ public String geocode(@QueryParam("latitude") double latitude, @QueryParam("longitude") double longitude) {
+ return Context.getGeocoder().getAddress(latitude, longitude, null);
+ }
+
}
diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java
index e88ff7f0d..abc424ff9 100644
--- a/src/org/traccar/database/DataManager.java
+++ b/src/org/traccar/database/DataManager.java
@@ -40,6 +40,7 @@ import liquibase.resource.FileSystemResourceAccessor;
import liquibase.resource.ResourceAccessor;
import org.traccar.Config;
+import org.traccar.Context;
import org.traccar.helper.Log;
import org.traccar.model.Attribute;
import org.traccar.model.Device;
@@ -75,9 +76,13 @@ public class DataManager {
private boolean generateQueries;
+ private boolean forceLdap;
+
public DataManager(Config config) throws Exception {
this.config = config;
+ forceLdap = config.getBoolean("ldap.force");
+
initDatabase();
initDatabaseSchema();
}
@@ -300,11 +305,20 @@ public class DataManager {
User user = QueryBuilder.create(dataSource, getQuery("database.loginUser"))
.setString("email", email.trim())
.executeQuerySingle(User.class);
- if (user != null && user.isPasswordValid(password)) {
- return user;
+ LdapProvider ldapProvider = Context.getLdapProvider();
+ if (user != null) {
+ if (ldapProvider != null && ldapProvider.login(user.getLogin(), password)
+ || !forceLdap && user.isPasswordValid(password)) {
+ return user;
+ }
} else {
- return null;
+ if (ldapProvider != null && ldapProvider.login(email, password)) {
+ user = ldapProvider.getUser(email);
+ Context.getUsersManager().addItem(user);
+ return user;
+ }
}
+ return null;
}
public void updateDeviceStatus(Device device) throws SQLException {
diff --git a/src/org/traccar/database/LdapProvider.java b/src/org/traccar/database/LdapProvider.java
new file mode 100644
index 000000000..2c4b7ceb1
--- /dev/null
+++ b/src/org/traccar/database/LdapProvider.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.database;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+
+import org.traccar.Config;
+import org.traccar.helper.Log;
+import org.traccar.model.User;
+
+import java.util.Hashtable;
+
+public class LdapProvider {
+
+ private String url;
+ private String searchBase;
+ private String idAttribute;
+ private String nameAttribute;
+ private String mailAttribute;
+ private String searchFilter;
+ private String adminFilter;
+ private String serviceUser;
+ private String servicePassword;
+
+ public LdapProvider(Config config) {
+ String url = config.getString("ldap.url");
+ if (url != null) {
+ this.url = url;
+ } else {
+ this.url = "ldap://" + config.getString("ldap.server") + ":" + config.getInteger("ldap.port", 389);
+ }
+ this.searchBase = config.getString("ldap.base");
+ this.idAttribute = config.getString("ldap.idAttribute", "uid");
+ this.nameAttribute = config.getString("ldap.nameAttribute", "cn");
+ this.mailAttribute = config.getString("ldap.mailAttribute", "mail");
+ this.searchFilter = config.getString("ldap.searchFilter", "(" + idAttribute + "=:login)");
+ String adminGroup = config.getString("ldap.adminGroup");
+ this.adminFilter = config.getString("ldap.adminFilter");
+ if (this.adminFilter == null && adminGroup != null) {
+ this.adminFilter = "(&(" + idAttribute + "=:login)(memberOf=" + adminGroup + "))";
+ }
+ this.serviceUser = config.getString("ldap.user");
+ this.servicePassword = config.getString("ldap.password");
+ }
+
+ private InitialDirContext auth(String accountName, String password) throws NamingException {
+ Hashtable<String, String> env = new Hashtable<>();
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+ env.put(Context.PROVIDER_URL, url);
+
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
+ env.put(Context.SECURITY_PRINCIPAL, accountName);
+ env.put(Context.SECURITY_CREDENTIALS, password);
+
+ return new InitialDirContext(env);
+ }
+
+ private boolean isAdmin(String accountName) {
+ if (this.adminFilter != null) {
+ try {
+ InitialDirContext context = initContext();
+ String searchString = adminFilter.replace(":login", accountName);
+ SearchControls searchControls = new SearchControls();
+ searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+ NamingEnumeration<SearchResult> results = context.search(searchBase, searchString, searchControls);
+ if (results.hasMoreElements()) {
+ results.nextElement();
+ if (results.hasMoreElements()) {
+ Log.warning("Matched multiple users for the accountName: " + accountName);
+ return false;
+ }
+ return true;
+ }
+ } catch (NamingException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public InitialDirContext initContext() throws NamingException {
+ return auth(serviceUser, servicePassword);
+ }
+
+ private SearchResult lookupUser(String accountName) throws NamingException {
+ InitialDirContext context = initContext();
+
+ String searchString = searchFilter.replace(":login", accountName);
+
+ SearchControls searchControls = new SearchControls();
+ String[] attributeFilter = {idAttribute, nameAttribute, mailAttribute};
+ searchControls.setReturningAttributes(attributeFilter);
+ searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
+ NamingEnumeration<SearchResult> results = context.search(searchBase, searchString, searchControls);
+
+ SearchResult searchResult = null;
+ if (results.hasMoreElements()) {
+ searchResult = (SearchResult) results.nextElement();
+ if (results.hasMoreElements()) {
+ Log.warning("Matched multiple users for the accountName: " + accountName);
+ return null;
+ }
+ }
+
+ return searchResult;
+ }
+
+ public User getUser(String accountName) {
+ SearchResult ldapUser;
+ User user = new User();
+ try {
+ ldapUser = lookupUser(accountName);
+ if (ldapUser != null) {
+ Attribute attribute = ldapUser.getAttributes().get(idAttribute);
+ if (attribute != null) {
+ user.setLogin((String) attribute.get());
+ } else {
+ user.setLogin(accountName);
+ }
+ attribute = ldapUser.getAttributes().get(nameAttribute);
+ if (attribute != null) {
+ user.setName((String) attribute.get());
+ } else {
+ user.setName(accountName);
+ }
+ attribute = ldapUser.getAttributes().get(mailAttribute);
+ if (attribute != null) {
+ user.setEmail((String) attribute.get());
+ } else {
+ user.setEmail(accountName);
+ }
+ }
+ user.setAdmin(isAdmin(accountName));
+ } catch (NamingException e) {
+ user.setLogin(accountName);
+ user.setName(accountName);
+ user.setEmail(accountName);
+ Log.warning(e);
+ }
+ return user;
+ }
+
+ public boolean login(String username, String password) {
+ try {
+ SearchResult ldapUser = lookupUser(username);
+ if (ldapUser != null) {
+ auth(ldapUser.getNameInNamespace(), password).close();
+ return true;
+ }
+ } catch (NamingException e) {
+ return false;
+ }
+ return false;
+ }
+
+}
diff --git a/src/org/traccar/database/NotificationManager.java b/src/org/traccar/database/NotificationManager.java
index 73041a23f..4e6114001 100644
--- a/src/org/traccar/database/NotificationManager.java
+++ b/src/org/traccar/database/NotificationManager.java
@@ -35,8 +35,11 @@ import org.traccar.notification.NotificationSms;
public class NotificationManager extends ExtendedObjectManager<Notification> {
+ private boolean geocodeOnRequest;
+
public NotificationManager(DataManager dataManager) {
super(dataManager, Notification.class);
+ geocodeOnRequest = Context.getConfig().getBoolean("geocoder.onRequest");
}
private Set<Long> getEffectiveNotifications(long userId, long deviceId) {
@@ -57,6 +60,11 @@ public class NotificationManager extends ExtendedObjectManager<Notification> {
Log.warning(error);
}
+ if (position != null && geocodeOnRequest && Context.getGeocoder() != null && position.getAddress() == null) {
+ position.setAddress(Context.getGeocoder()
+ .getAddress(position.getLatitude(), position.getLongitude(), null));
+ }
+
long deviceId = event.getDeviceId();
Set<Long> users = Context.getPermissionsManager().getDeviceUsers(deviceId);
for (long userId : users) {
diff --git a/src/org/traccar/geocoder/BingMapsGeocoder.java b/src/org/traccar/geocoder/BingMapsGeocoder.java
index a9b36219a..fbfb5394a 100644
--- a/src/org/traccar/geocoder/BingMapsGeocoder.java
+++ b/src/org/traccar/geocoder/BingMapsGeocoder.java
@@ -1,5 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
+ * Copyright 2017 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 +21,8 @@ import javax.json.JsonObject;
public class BingMapsGeocoder extends JsonGeocoder {
- public BingMapsGeocoder(String url, String key, int cacheSize) {
- super(url + "/Locations/%f,%f?key=" + key + "&include=ciso2", cacheSize);
+ public BingMapsGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(url + "/Locations/%f,%f?key=" + key + "&include=ciso2", cacheSize, addressFormat);
}
@Override
diff --git a/src/org/traccar/geocoder/FactualGeocoder.java b/src/org/traccar/geocoder/FactualGeocoder.java
index 0c76e4625..c7a68c293 100644
--- a/src/org/traccar/geocoder/FactualGeocoder.java
+++ b/src/org/traccar/geocoder/FactualGeocoder.java
@@ -1,5 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
+ * Copyright 2017 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,8 +20,8 @@ import javax.json.JsonObject;
public class FactualGeocoder extends JsonGeocoder {
- public FactualGeocoder(String url, String key, int cacheSize) {
- super(url + "?latitude=%f&longitude=%f&KEY=" + key, cacheSize);
+ public FactualGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(url + "?latitude=%f&longitude=%f&KEY=" + key, cacheSize, addressFormat);
}
@Override
diff --git a/src/org/traccar/geocoder/GeocodeFarmGeocoder.java b/src/org/traccar/geocoder/GeocodeFarmGeocoder.java
index 73ff85aa4..4fb956af9 100644
--- a/src/org/traccar/geocoder/GeocodeFarmGeocoder.java
+++ b/src/org/traccar/geocoder/GeocodeFarmGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 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.
@@ -30,8 +30,8 @@ public class GeocodeFarmGeocoder extends JsonGeocoder {
}
return url;
}
- public GeocodeFarmGeocoder(String key, String language, int cacheSize) {
- super(formatUrl(key, language), cacheSize);
+ public GeocodeFarmGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(formatUrl(key, language), cacheSize, addressFormat);
}
@Override
diff --git a/src/org/traccar/geocoder/Geocoder.java b/src/org/traccar/geocoder/Geocoder.java
index 3ce3fb67f..587a27520 100644
--- a/src/org/traccar/geocoder/Geocoder.java
+++ b/src/org/traccar/geocoder/Geocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2013 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2017 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,6 +25,6 @@ public interface Geocoder {
}
- void getAddress(AddressFormat format, double latitude, double longitude, ReverseGeocoderCallback callback);
+ String getAddress(double latitude, double longitude, ReverseGeocoderCallback callback);
}
diff --git a/src/org/traccar/geocoder/GisgraphyGeocoder.java b/src/org/traccar/geocoder/GisgraphyGeocoder.java
index 1432166e9..a0c831966 100644
--- a/src/org/traccar/geocoder/GisgraphyGeocoder.java
+++ b/src/org/traccar/geocoder/GisgraphyGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 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,12 @@ import javax.json.JsonObject;
public class GisgraphyGeocoder extends JsonGeocoder {
- public GisgraphyGeocoder() {
- this("http://services.gisgraphy.com/reversegeocoding/search", 0);
+ public GisgraphyGeocoder(AddressFormat addressFormat) {
+ this("http://services.gisgraphy.com/reversegeocoding/search", 0, addressFormat);
}
- public GisgraphyGeocoder(String url, int cacheSize) {
- super(url + "?format=json&lat=%f&lng=%f&from=1&to=1", cacheSize);
+ public GisgraphyGeocoder(String url, int cacheSize, AddressFormat addressFormat) {
+ super(url + "?format=json&lat=%f&lng=%f&from=1&to=1", cacheSize, addressFormat);
}
@Override
diff --git a/src/org/traccar/geocoder/GoogleGeocoder.java b/src/org/traccar/geocoder/GoogleGeocoder.java
index b38870c8f..235ea9ea9 100644
--- a/src/org/traccar/geocoder/GoogleGeocoder.java
+++ b/src/org/traccar/geocoder/GoogleGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,8 +32,8 @@ public class GoogleGeocoder extends JsonGeocoder {
return url;
}
- public GoogleGeocoder(String key, String language, int cacheSize) {
- super(formatUrl(key, language), cacheSize);
+ public GoogleGeocoder(String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(formatUrl(key, language), cacheSize, addressFormat);
}
@Override
diff --git a/src/org/traccar/geocoder/JsonGeocoder.java b/src/org/traccar/geocoder/JsonGeocoder.java
index 6d1380729..82a6ee604 100644
--- a/src/org/traccar/geocoder/JsonGeocoder.java
+++ b/src/org/traccar/geocoder/JsonGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,23 +18,29 @@ package org.traccar.geocoder;
import com.ning.http.client.AsyncCompletionHandler;
import com.ning.http.client.Response;
import org.traccar.Context;
+import org.traccar.helper.Log;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
+
+import java.io.IOException;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
public abstract class JsonGeocoder implements Geocoder {
private final String url;
+ private final AddressFormat addressFormat;
private Map<Map.Entry<Double, Double>, String> cache;
- public JsonGeocoder(String url, final int cacheSize) {
+ public JsonGeocoder(String url, final int cacheSize, AddressFormat addressFormat) {
this.url = url;
+ this.addressFormat = addressFormat;
if (cacheSize > 0) {
this.cache = Collections.synchronizedMap(new LinkedHashMap<Map.Entry<Double, Double>, String>() {
@Override
@@ -45,43 +51,66 @@ public abstract class JsonGeocoder implements Geocoder {
}
}
+ private String handleResponse(double latitude, double longitude, Response response,
+ ReverseGeocoderCallback callback) throws IOException {
+ try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) {
+ Address address = parseAddress(reader.readObject());
+ if (address != null) {
+ String formattedAddress = addressFormat.format(address);
+ if (cache != null) {
+ cache.put(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude), formattedAddress);
+ }
+ if (callback != null) {
+ callback.onSuccess(formattedAddress);
+ }
+ return formattedAddress;
+ } else {
+ if (callback != null) {
+ callback.onFailure(new GeocoderException("Empty address"));
+ }
+ Log.warning("Empty address");
+ }
+ }
+ return null;
+ }
+
@Override
- public void getAddress(
- final AddressFormat format, final double latitude,
- final double longitude, final ReverseGeocoderCallback callback) {
+ public String getAddress(
+ final double latitude, final double longitude, final ReverseGeocoderCallback callback) {
if (cache != null) {
String cachedAddress = cache.get(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude));
if (cachedAddress != null) {
- callback.onSuccess(cachedAddress);
- return;
+ if (callback != null) {
+ callback.onSuccess(cachedAddress);
+ }
+ return cachedAddress;
}
}
- Context.getAsyncHttpClient().prepareGet(String.format(url, latitude, longitude))
- .execute(new AsyncCompletionHandler() {
- @Override
- public Object onCompleted(Response response) throws Exception {
- try (JsonReader reader = Json.createReader(response.getResponseBodyAsStream())) {
- Address address = parseAddress(reader.readObject());
- if (address != null) {
- String formattedAddress = format.format(address);
- if (cache != null) {
- cache.put(new AbstractMap.SimpleImmutableEntry<>(latitude, longitude), formattedAddress);
- }
- callback.onSuccess(formattedAddress);
- } else {
- callback.onFailure(new GeocoderException("Empty address"));
- }
+ if (callback != null) {
+ Context.getAsyncHttpClient().prepareGet(String.format(url, latitude, longitude))
+ .execute(new AsyncCompletionHandler() {
+ @Override
+ public Object onCompleted(Response response) throws Exception {
+ return handleResponse(latitude, longitude, response, callback);
}
- return null;
- }
- @Override
- public void onThrowable(Throwable t) {
- callback.onFailure(t);
+ @Override
+ public void onThrowable(Throwable t) {
+ callback.onFailure(t);
+ }
+ });
+ } else {
+ try {
+ Response response = Context.getAsyncHttpClient()
+ .prepareGet(String.format(url, latitude, longitude)).execute().get();
+ return handleResponse(latitude, longitude, response, null);
+ } catch (InterruptedException | ExecutionException | IOException error) {
+ Log.warning("Geocoding failed", error);
}
- });
+ }
+ return null;
}
public abstract Address parseAddress(JsonObject json);
diff --git a/src/org/traccar/geocoder/MapQuestGeocoder.java b/src/org/traccar/geocoder/MapQuestGeocoder.java
index 7d7217e91..4029e3f07 100644
--- a/src/org/traccar/geocoder/MapQuestGeocoder.java
+++ b/src/org/traccar/geocoder/MapQuestGeocoder.java
@@ -1,5 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
+ * Copyright 2017 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 +21,8 @@ import javax.json.JsonObject;
public class MapQuestGeocoder extends JsonGeocoder {
- public MapQuestGeocoder(String url, String key, int cacheSize) {
- super(url + "?key=" + key + "&location=%f,%f", cacheSize);
+ public MapQuestGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(url + "?key=" + key + "&location=%f,%f", cacheSize, addressFormat);
}
@Override
diff --git a/src/org/traccar/geocoder/NominatimGeocoder.java b/src/org/traccar/geocoder/NominatimGeocoder.java
index 051727e93..75d2e9f99 100644
--- a/src/org/traccar/geocoder/NominatimGeocoder.java
+++ b/src/org/traccar/geocoder/NominatimGeocoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 - 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2014 - 2017 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,8 +33,8 @@ public class NominatimGeocoder extends JsonGeocoder {
return url;
}
- public NominatimGeocoder(String url, String key, String language, int cacheSize) {
- super(formatUrl(url, key, language), cacheSize);
+ public NominatimGeocoder(String url, String key, String language, int cacheSize, AddressFormat addressFormat) {
+ super(formatUrl(url, key, language), cacheSize, addressFormat);
}
@Override
diff --git a/src/org/traccar/geocoder/OpenCageGeocoder.java b/src/org/traccar/geocoder/OpenCageGeocoder.java
index 9fa56a4a3..894397ee3 100644
--- a/src/org/traccar/geocoder/OpenCageGeocoder.java
+++ b/src/org/traccar/geocoder/OpenCageGeocoder.java
@@ -1,6 +1,6 @@
/*
* Copyright 2014 - 2015 Stefaan Van Dooren (stefaan.vandooren@gmail.com)
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 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,8 @@ import javax.json.JsonObject;
public class OpenCageGeocoder extends JsonGeocoder {
- public OpenCageGeocoder(String url, String key, int cacheSize) {
- super(url + "/json?q=%f,%f&key=" + key, cacheSize);
+ public OpenCageGeocoder(String url, String key, int cacheSize, AddressFormat addressFormat) {
+ super(url + "/json?q=%f,%f&key=" + key, cacheSize, addressFormat);
}
@Override
diff --git a/src/org/traccar/model/User.java b/src/org/traccar/model/User.java
index 5d89dcfae..b1883213c 100644
--- a/src/org/traccar/model/User.java
+++ b/src/org/traccar/model/User.java
@@ -35,6 +35,16 @@ public class User extends ExtendedModel {
this.name = name;
}
+ private String login;
+
+ public String getLogin() {
+ return login;
+ }
+
+ public void setLogin(String login) {
+ this.login = login;
+ }
+
private String email;
public String getEmail() {
diff --git a/src/org/traccar/protocol/DwayProtocolDecoder.java b/src/org/traccar/protocol/DwayProtocolDecoder.java
index 767b35c72..121681588 100644
--- a/src/org/traccar/protocol/DwayProtocolDecoder.java
+++ b/src/org/traccar/protocol/DwayProtocolDecoder.java
@@ -59,9 +59,9 @@ public class DwayProtocolDecoder extends BaseProtocolDecoder {
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
String sentence = (String) msg;
- if (sentence.startsWith(">H")) {
+ if (sentence.equals("AA55,HB")) {
if (channel != null) {
- channel.write(">ALIVE\r\n");
+ channel.write("55AA,HB,OK\r\n");
}
return null;
}
diff --git a/src/org/traccar/protocol/GenxProtocolDecoder.java b/src/org/traccar/protocol/GenxProtocolDecoder.java
index ebf6f2b53..3b38227dc 100644
--- a/src/org/traccar/protocol/GenxProtocolDecoder.java
+++ b/src/org/traccar/protocol/GenxProtocolDecoder.java
@@ -55,6 +55,7 @@ public class GenxProtocolDecoder extends BaseProtocolDecoder {
for (int i = 0; i < Math.min(values.length, reportColumns.length); i++) {
switch (reportColumns[i]) {
case 1:
+ case 28:
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[i]);
if (deviceSession != null) {
position.setDeviceId(deviceSession.getDeviceId());
diff --git a/src/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java
index 2f03cbb8f..fb0023dba 100644
--- a/src/org/traccar/protocol/Gl200TextProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -31,8 +31,12 @@ import org.traccar.model.WifiAccessPoint;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;
+import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -216,7 +220,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(d{1,7}.d)?,") // odometer
.number("(d{5}:dd:dd)?,") // hour meter
.number("(x+)?,") // adc 1
- .number("(x+)?,") // adc 2
+ .number("(x+)?,").optional() // adc 2
.number("(d{1,3})?,") // battery
.number("(?:(xx)(xx)(xx))?,") // device status
.expression("(.*)") // additional data
@@ -523,6 +527,131 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private Object decodeCan(Channel channel, SocketAddress remoteAddress, String sentence) throws ParseException {
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ int index = 0;
+ String[] values = sentence.split(",");
+
+ index += 1; // header
+ index += 1; // protocol version
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, values[index++]);
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ index += 1; // device name
+ index += 1; // report type
+ index += 1; // canbus state
+ long reportMask = Long.parseLong(values[index++], 16);
+
+ if (BitUtil.check(reportMask, 0)) {
+ position.set(Position.KEY_VIN, values[index++]);
+ }
+ if (BitUtil.check(reportMask, 1)) {
+ position.set(Position.KEY_IGNITION, Integer.parseInt(values[index++]) > 0);
+ }
+ if (BitUtil.check(reportMask, 2)) {
+ index += 1; // total distance
+ }
+ if (BitUtil.check(reportMask, 3)) {
+ position.set("totalFuelConsumption", Double.parseDouble(values[index++]));
+ }
+ if (BitUtil.check(reportMask, 5)) {
+ position.set(Position.KEY_RPM, Integer.parseInt(values[index++]));
+ }
+ if (BitUtil.check(reportMask, 4)) {
+ position.set(Position.KEY_OBD_SPEED, UnitsConverter.knotsFromKph(Integer.parseInt(values[index++])));
+ }
+ if (BitUtil.check(reportMask, 6)) {
+ position.set(Position.KEY_COOLANT_TEMP, Integer.parseInt(values[index++]));
+ }
+ if (BitUtil.check(reportMask, 7)) {
+ index += 1; // fuel consumption
+ }
+ if (BitUtil.check(reportMask, 8)) {
+ index += 1; // fuel level
+ }
+ if (BitUtil.check(reportMask, 9)) {
+ index += 1; // range
+ }
+ if (BitUtil.check(reportMask, 10)) {
+ if (!values[index++].isEmpty()) {
+ position.set(Position.KEY_THROTTLE, Integer.parseInt(values[index - 1]));
+ }
+ }
+ if (BitUtil.check(reportMask, 11)) {
+ position.set(Position.KEY_HOURS, Double.parseDouble(values[index++]));
+ }
+ if (BitUtil.check(reportMask, 12)) {
+ index += 1; // driving time
+ }
+ if (BitUtil.check(reportMask, 13)) {
+ index += 1; // idle time
+ }
+ if (BitUtil.check(reportMask, 14)) {
+ index += 1; // idle fuel
+ }
+ if (BitUtil.check(reportMask, 15)) {
+ index += 1; // axle weight
+ }
+ if (BitUtil.check(reportMask, 16)) {
+ index += 1; // tachograph info
+ }
+ if (BitUtil.check(reportMask, 17)) {
+ index += 1; // indicators
+ }
+ if (BitUtil.check(reportMask, 18)) {
+ index += 1; // lights
+ }
+ if (BitUtil.check(reportMask, 19)) {
+ index += 1; // doors
+ }
+ if (BitUtil.check(reportMask, 20)) {
+ index += 1; // total vehicle overspeed time
+ }
+ if (BitUtil.check(reportMask, 21)) {
+ index += 1; // total engine overspeed time
+ }
+ if (BitUtil.check(reportMask, 29)) {
+ index += 1; // expansion
+ }
+
+ DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ if (BitUtil.check(reportMask, 30)) {
+ position.setValid(Integer.parseInt(values[index++]) > 0);
+ if (!values[index].isEmpty()) {
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[index++])));
+ position.setCourse(Integer.parseInt(values[index++]));
+ position.setAltitude(Double.parseDouble(values[index++]));
+ position.setLongitude(Double.parseDouble(values[index++]));
+ position.setLatitude(Double.parseDouble(values[index++]));
+ position.setTime(dateFormat.parse(values[index++]));
+ } else {
+ index += 6; // no location
+ getLastLocation(position, null);
+ }
+ } else {
+ getLastLocation(position, null);
+ }
+
+ if (BitUtil.check(reportMask, 31)) {
+ index += 4; // cell
+ }
+
+ index += 1; // reserved
+
+ if (ignoreFixTime) {
+ position.setTime(dateFormat.parse(values[index]));
+ } else {
+ position.setDeviceTime(dateFormat.parse(values[index]));
+ }
+
+ return position;
+ }
+
private void decodeStatus(Position position, Parser parser) {
if (parser.hasNext(3)) {
int ignition = parser.nextHexInt(0);
@@ -869,6 +998,9 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
case "OBD":
result = decodeObd(channel, remoteAddress, sentence);
break;
+ case "CAN":
+ result = decodeCan(channel, remoteAddress, sentence);
+ break;
case "FRI":
result = decodeFri(channel, remoteAddress, sentence);
break;
diff --git a/src/org/traccar/protocol/Ivt401Protocol.java b/src/org/traccar/protocol/Ivt401Protocol.java
new file mode 100644
index 000000000..b58601ba1
--- /dev/null
+++ b/src/org/traccar/protocol/Ivt401Protocol.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 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.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class Ivt401Protocol extends BaseProtocol {
+
+ public Ivt401Protocol() {
+ super("ivt401");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ';'));
+ pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("objectDecoder", new Ivt401ProtocolDecoder(Ivt401Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Ivt401ProtocolDecoder.java b/src/org/traccar/protocol/Ivt401ProtocolDecoder.java
new file mode 100644
index 000000000..f209ee09e
--- /dev/null
+++ b/src/org/traccar/protocol/Ivt401ProtocolDecoder.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2017 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.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+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 Ivt401ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Ivt401ProtocolDecoder(Ivt401Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("(")
+ .expression("TL[ABLN],") // header
+ .number("(d+),") // imei
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(dd)(dd)(dd),") // time (hhmmss)
+ .number("([-+]d+.d+),") // latitude
+ .number("([-+]d+.d+),") // longitude
+ .number("(d+),") // speed
+ .number("(d+),") // course
+ .number("(-?d+.?d*),") // altitude
+ .number("(d+),") // satellites
+ .number("(d),") // gps status
+ .number("(d+),") // rssi
+ .number("(d+),") // input
+ .number("(d+),") // output
+ .number("(d+.d+),") // adc
+ .number("(d+.d+),") // power
+ .number("(d+.d+),") // battery
+ .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;
+ }
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(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.nextDouble());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+
+ position.setValid(parser.nextInt() > 0);
+
+ position.set(Position.KEY_RSSI, parser.nextInt());
+ position.set(Position.KEY_INPUT, parser.nextBinInt());
+ position.set(Position.KEY_OUTPUT, parser.nextBinInt());
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_BATTERY, parser.nextDouble());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/org/traccar/protocol/Jt600ProtocolDecoder.java
index f76fd8069..58835c7d6 100644
--- a/src/org/traccar/protocol/Jt600ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Jt600ProtocolDecoder.java
@@ -21,6 +21,7 @@ import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
import org.traccar.helper.BcdUtil;
+import org.traccar.helper.BitBuffer;
import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
@@ -152,6 +153,19 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder {
fuel += buf.readUnsignedByte();
position.set(Position.KEY_FUEL_LEVEL, fuel);
+ } else if (version == 3) {
+
+ BitBuffer bitBuffer = new BitBuffer(buf);
+
+ position.set("fuel1", bitBuffer.readUnsigned(12));
+ position.set("fuel2", bitBuffer.readUnsigned(12));
+ position.set("fuel3", bitBuffer.readUnsigned(12));
+ position.set(Position.KEY_ODOMETER, bitBuffer.readUnsigned(20) * 1000);
+
+ int status = bitBuffer.readUnsigned(24);
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 0));
+ position.set(Position.KEY_STATUS, status);
+
}
positions.add(position);
diff --git a/src/org/traccar/protocol/MegastekProtocolDecoder.java b/src/org/traccar/protocol/MegastekProtocolDecoder.java
index 3ef52acd1..ad0f20a24 100644
--- a/src/org/traccar/protocol/MegastekProtocolDecoder.java
+++ b/src/org/traccar/protocol/MegastekProtocolDecoder.java
@@ -340,30 +340,38 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder {
}
private String decodeAlarm(String value) {
+ value = value.toLowerCase();
+ if (value.startsWith("geo")) {
+ if (value.endsWith("in")) {
+ return Position.ALARM_GEOFENCE_ENTER;
+ } else if (value.endsWith("out")) {
+ return Position.ALARM_GEOFENCE_EXIT;
+ }
+ }
switch (value) {
- case "SOS":
- case "Help":
+ case "poweron":
+ return Position.ALARM_POWER_ON;
+ case "poweroff":
+ return Position.ALARM_POWER_ON;
+ case "sos":
+ case "help":
return Position.ALARM_SOS;
- case "Over Speed":
- case "OverSpeed":
+ case "over speed":
+ case "overspeed":
return Position.ALARM_OVERSPEED;
- case "LowSpeed":
+ case "lowspeed":
return Position.ALARM_LOW_SPEED;
- case "Low Battery":
- case "LowBattery":
+ case "low battery":
+ case "lowbattery":
return Position.ALARM_LOW_BATTERY;
- case "VIB":
+ case "vib":
return Position.ALARM_VIBRATION;
- case "Move in":
- case "Geo in":
- case "Geo1 in":
- case "Geo2 in":
+ case "move in":
return Position.ALARM_GEOFENCE_ENTER;
- case "Move out":
- case "Geo out":
- case "Geo1 out":
- case "Geo2 out":
+ case "move out":
return Position.ALARM_GEOFENCE_EXIT;
+ case "error":
+ return Position.ALARM_FAULT;
default:
return null;
}
diff --git a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
index b0793037f..bc7a0b71a 100644
--- a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
+++ b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
@@ -69,6 +69,8 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
.number("|(x{8})") // odometer
.groupBegin()
.number("|(xx)") // satellites
+ .text("|")
+ .expression("(.*)") // driver
.groupEnd("?")
.or()
.number("|(x{9})") // odometer
@@ -259,6 +261,7 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RSSI, parser.nextHexInt());
position.set(Position.KEY_ODOMETER, parser.nextHexLong());
position.set(Position.KEY_SATELLITES, parser.nextHexInt());
+ position.set("driverLicense", parser.next());
position.set(Position.KEY_ODOMETER, parser.nextHexLong());
position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next());
diff --git a/src/org/traccar/protocol/OkoProtocol.java b/src/org/traccar/protocol/OkoProtocol.java
new file mode 100644
index 000000000..0b38741e5
--- /dev/null
+++ b/src/org/traccar/protocol/OkoProtocol.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017 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.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.handler.codec.string.StringDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.CharacterDelimiterFrameDecoder;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class OkoProtocol extends BaseProtocol {
+
+ public OkoProtocol() {
+ super("oko");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '}'));
+ pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("objectDecoder", new OkoProtocolDecoder(OkoProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/OkoProtocolDecoder.java b/src/org/traccar/protocol/OkoProtocolDecoder.java
new file mode 100644
index 000000000..e86acf0b8
--- /dev/null
+++ b/src/org/traccar/protocol/OkoProtocolDecoder.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2017 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.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+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 OkoProtocolDecoder extends BaseProtocolDecoder {
+
+ public OkoProtocolDecoder(OkoProtocol protocol) {
+ super(protocol);
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("{")
+ .number("(d{15}),").optional() // imei
+ .number("(dd)(dd)(dd).d+,") // time
+ .expression("([AV]),") // validity
+ .number("(dd)(dd.d+),") // latitude
+ .expression("([NS]),")
+ .number("(ddd)(dd.d+),") // longitude
+ .expression("([EW]),")
+ .number("(d+.?d*)?,") // speed
+ .number("(d+.?d*)?,") // course
+ .number("(dd)(dd)(dd),") // date (ddmmyy)
+ .number("(d+),") // satellites
+ .number("(d+.d+),") // adc
+ .number("(xx),") // event
+ .number("(d+.d+),") // power
+ .number("d,") // memory status
+ .number("(xx)") // io
+ .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;
+ }
+
+ DeviceSession deviceSession;
+ if (parser.hasNext()) {
+ deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ } else {
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ }
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(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(0));
+ position.setCourse(parser.nextDouble(0));
+
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
+
+ position.set(Position.KEY_SATELLITES, parser.nextInt());
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.KEY_EVENT, parser.next());
+ position.set(Position.KEY_POWER, parser.nextDouble());
+ position.set(Position.KEY_INPUT, parser.nextHexInt());
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/reports/ReportUtils.java b/src/org/traccar/reports/ReportUtils.java
index f6f386e99..e04f2f90c 100644
--- a/src/org/traccar/reports/ReportUtils.java
+++ b/src/org/traccar/reports/ReportUtils.java
@@ -190,13 +190,23 @@ public final class ReportUtils {
trip.setStartLat(startTrip.getLatitude());
trip.setStartLon(startTrip.getLongitude());
trip.setStartTime(startTrip.getFixTime());
- trip.setStartAddress(startTrip.getAddress());
+ String startAddress = startTrip.getAddress();
+ if (startAddress == null && Context.getGeocoder() != null
+ && Context.getConfig().getBoolean("geocoder.onRequest")) {
+ startAddress = Context.getGeocoder().getAddress(startTrip.getLatitude(), startTrip.getLongitude(), null);
+ }
+ trip.setStartAddress(startAddress);
trip.setEndPositionId(endTrip.getId());
trip.setEndLat(endTrip.getLatitude());
trip.setEndLon(endTrip.getLongitude());
trip.setEndTime(endTrip.getFixTime());
- trip.setEndAddress(endTrip.getAddress());
+ String endAddress = endTrip.getAddress();
+ if (endAddress == null && Context.getGeocoder() != null
+ && Context.getConfig().getBoolean("geocoder.onRequest")) {
+ endAddress = Context.getGeocoder().getAddress(endTrip.getLatitude(), endTrip.getLongitude(), null);
+ }
+ trip.setEndAddress(endAddress);
trip.setDistance(calculateDistance(startTrip, endTrip, !ignoreOdometer));
trip.setDuration(tripDuration);
@@ -224,7 +234,13 @@ public final class ReportUtils {
stop.setLatitude(startStop.getLatitude());
stop.setLongitude(startStop.getLongitude());
stop.setStartTime(startStop.getFixTime());
- stop.setAddress(startStop.getAddress());
+ String address = startStop.getAddress();
+ if (address == null && Context.getGeocoder() != null
+ && Context.getConfig().getBoolean("geocoder.onRequest")) {
+ address = Context.getGeocoder().getAddress(stop.getLatitude(), stop.getLongitude(), null);
+ }
+ stop.setAddress(address);
+
stop.setEndTime(endStop.getFixTime());
long stopDuration = endStop.getFixTime().getTime() - startStop.getFixTime().getTime();
diff --git a/test/org/traccar/geocoder/GeocoderTest.java b/test/org/traccar/geocoder/GeocoderTest.java
index 40b6fd75d..7c6208048 100644
--- a/test/org/traccar/geocoder/GeocoderTest.java
+++ b/test/org/traccar/geocoder/GeocoderTest.java
@@ -1,5 +1,7 @@
package org.traccar.geocoder;
+import java.util.Locale;
+
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
@@ -9,6 +11,7 @@ public class GeocoderTest {
@Ignore
@Test
public void test() throws InterruptedException {
+ Locale.setDefault(Locale.US);
testGoogle();
}
@@ -29,25 +32,28 @@ public class GeocoderTest {
}
public void testGoogle() throws InterruptedException {
- Geocoder geocoder = new GoogleGeocoder(null, null, 0);
+ Geocoder geocoder = new GoogleGeocoder(null, null, 0, new AddressFormat());
- geocoder.getAddress(new AddressFormat(), 31.776797, 35.211489, new Geocoder.ReverseGeocoderCallback() {
+ geocoder.getAddress(31.776797, 35.211489, new Geocoder.ReverseGeocoderCallback() {
@Override
public void onSuccess(String address) {
setAddress(address);
}
@Override
- public void onFailure(Throwable e) {
+ public void onFailure(final Throwable e) {
}
});
Assert.assertEquals("1 Ibn Shaprut St, Jerusalem, Jerusalem District, IL", waitAddress());
+
+ Assert.assertEquals("1 Ibn Shaprut St, Jerusalem, Jerusalem District, IL",
+ geocoder.getAddress(31.776797, 35.211489, null));
}
public void testNominatim() throws InterruptedException {
- Geocoder geocoder = new NominatimGeocoder(null, null, null, 0);
+ Geocoder geocoder = new NominatimGeocoder(null, null, null, 0, new AddressFormat());
- geocoder.getAddress(new AddressFormat(), 40.7337807, -73.9974401, new Geocoder.ReverseGeocoderCallback() {
+ geocoder.getAddress(40.7337807, -73.9974401, new Geocoder.ReverseGeocoderCallback() {
@Override
public void onSuccess(String address) {
setAddress(address);
@@ -57,13 +63,16 @@ public class GeocoderTest {
public void onFailure(Throwable e) {
}
});
- Assert.assertEquals("35 West 9th Street, NYC, New York, US", waitAddress());
+ Assert.assertEquals("35 West 9th Street, NYC, New York, US", waitAddress());
+
+ Assert.assertEquals("35 West 9th Street, NYC, New York, US",
+ geocoder.getAddress(40.7337807, -73.9974401, null));
}
public void testGisgraphy() throws InterruptedException {
- Geocoder geocoder = new GisgraphyGeocoder();
+ Geocoder geocoder = new GisgraphyGeocoder(new AddressFormat());
- geocoder.getAddress(new AddressFormat(), 48.8530000, 2.3400000, new Geocoder.ReverseGeocoderCallback() {
+ geocoder.getAddress(48.8530000, 2.3400000, new Geocoder.ReverseGeocoderCallback() {
@Override
public void onSuccess(String address) {
setAddress(address);
@@ -73,14 +82,16 @@ public class GeocoderTest {
public void onFailure(Throwable e) {
}
});
- Assert.assertEquals("Rue du Jardinet, Paris, FR", waitAddress());
+ Assert.assertEquals("Rue du Jardinet, Paris, FR", waitAddress());
+
+ Assert.assertEquals("Rue du Jardinet, Paris, FR", geocoder.getAddress(48.8530000, 2.3400000, null));
}
public void testOpenCage() throws InterruptedException {
Geocoder geocoder = new OpenCageGeocoder(
- "http://api.opencagedata.com/geocode/v1", "SECRET", 0);
+ "http://api.opencagedata.com/geocode/v1", "SECRET", 0, new AddressFormat());
- geocoder.getAddress(new AddressFormat(), 34.116302, -118.051519, new Geocoder.ReverseGeocoderCallback() {
+ geocoder.getAddress(34.116302, -118.051519, new Geocoder.ReverseGeocoderCallback() {
@Override
public void onSuccess(String address) {
setAddress(address);
@@ -90,13 +101,15 @@ public class GeocoderTest {
public void onFailure(Throwable e) {
}
});
- Assert.assertEquals("Charleston Road, California, US", waitAddress());
+ Assert.assertEquals("Charleston Road, California, US", waitAddress());
+
+ Assert.assertEquals("Charleston Road, California, US", geocoder.getAddress(34.116302, -118.051519, null));
}
public void testGeocodeFarm() throws InterruptedException {
- Geocoder geocoder = new GeocodeFarmGeocoder(null, null, 0);
+ Geocoder geocoder = new GeocodeFarmGeocoder(null, null, 0, new AddressFormat());
- geocoder.getAddress(new AddressFormat(), 34.116302, -118.051519, new Geocoder.ReverseGeocoderCallback() {
+ geocoder.getAddress(34.116302, -118.051519, new Geocoder.ReverseGeocoderCallback() {
@Override
public void onSuccess(String address) {
setAddress(address);
@@ -106,7 +119,10 @@ public class GeocoderTest {
public void onFailure(Throwable e) {
}
});
- Assert.assertEquals("Estrella Avenue, Arcadia, California, United States", waitAddress());
+ Assert.assertEquals("Estrella Avenue, Arcadia, California, United States", waitAddress());
+
+ Assert.assertEquals("Estrella Avenue, Arcadia, California, United States",
+ geocoder.getAddress(34.116302, -118.051519, null));
}
}
diff --git a/test/org/traccar/protocol/DwayProtocolDecoderTest.java b/test/org/traccar/protocol/DwayProtocolDecoderTest.java
index be51f2b94..621aabf92 100644
--- a/test/org/traccar/protocol/DwayProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/DwayProtocolDecoderTest.java
@@ -23,7 +23,7 @@ public class DwayProtocolDecoderTest extends ProtocolTest {
"AA55,1,123456,1,140101,101132,22.5500,113.6770,75,70.5,320,1100,0011,1110,3950,33000,24000,12345678"));
verifyNull(decoder, text(
- ">H12345678"));
+ "AA55,HB"));
}
diff --git a/test/org/traccar/protocol/GenxProtocolDecoderTest.java b/test/org/traccar/protocol/GenxProtocolDecoderTest.java
index 9c49839c4..ee813b841 100644
--- a/test/org/traccar/protocol/GenxProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/GenxProtocolDecoderTest.java
@@ -10,6 +10,13 @@ public class GenxProtocolDecoderTest extends ProtocolTest {
GenxProtocolDecoder decoder = new GenxProtocolDecoder(new GenxProtocol());
+ decoder.setReportColumns("28,2,3,4,13,17,10,23,27,11,7,8,46,56,59,70,74,75,77,89,90,93,99,107,112,113,114,176,175,178,181,182");
+
+ verifyPosition(decoder, text(
+ "000036004133,11/05/2017 00:03:45,45.54767,-73.75547,0,0,63,569.35,118,ON,10687,0,12,O,9,3669.000,95.0,0.0,1,107.9464,0.0065,583.752,43,0.00,28.26,7.60,NA,U,UUU,0,-95.0,U"));
+
+ decoder.setReportColumns("1,2,3,4");
+
verifyPosition(decoder, text(
"000036004130,08/31/2017 17:24:13,45.47275,-73.65491,5,19,117,1.14,147,ON,1462,0,6,N,0,0.000,-95.0,-1.0,0,0.0000,0.0000,0.000,0,0.00,0.00,0.00,NA,U,UUU,0,-95.0,U"));
diff --git a/test/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/test/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
index 2b0395e48..d9e36ef47 100644
--- a/test/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
@@ -11,6 +11,18 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
Gl200TextProtocolDecoder decoder = new Gl200TextProtocolDecoder(new Gl200Protocol());
verifyAttributes(decoder, buffer(
+ "+RESP:GTCAN,310201,153759012347650,gv65,0,1,C03FFFFF,,2,H89394,63.14,200,0,87,,P43.60,0,0,17.53,11.61,5.92,0.00,0,0,4002,0,1,0.76,35.00,0,,,,0,0,,0000,0000,0000,0000,00,20040101000052,05A6$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,310603,863286023346480,gv65,00,1,C03FFFFF,,2,H2843820,373.76,1440,44,77,M23,P35.00,1810,,59.48,42.68,16.80,15.42,,,610,,0,,,0,42.7,263,27.2,-2.156478,51.899989,20171021151805,0234,0010,15D6,9AD2,00,20171021151807,0B28$"));
+
+ verifyPosition(decoder, buffer(
+ "+RESP:GTCAN,310603,863286023346480,gv65,02,1,C03FFFFF,,0,H2843820,373.80,0,4,75,M12,,1800,,59.49,42.69,16.80,15.42,,,0,,0,,,0,0.7,75,24.3,-2.155148,51.899400,20171021151837,0234,0010,15D6,9AD2,00,20171021152355,0B2E$"));
+
+ verifyPositions(decoder, buffer(
+ "+RESP:GTERI,380603,869606020025833,gv65,00000002,12003,10,1,1,0.0,172,24.6,-81.931875,26.577439,20171002045352,0310,0260,72BD,8E5B,00,1052.1,01383:52:12,0,100,210700,2,1,28FF4560A3150483,1,05B0,20171002045402,9548$"));
+
+ verifyAttributes(decoder, buffer(
"+RESP:GTINF,04040E,861074023747143,gv200,41,8959301000648637556f,24,0,1,0,1,4.4,0,1,0,0,20170912221854,0,00,01,-0500,1,20170912193448,1D5B$"));
verifyAttributes(decoder, buffer(
diff --git a/test/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/test/org/traccar/protocol/Gt06ProtocolDecoderTest.java
index ede2f385c..c9ef29791 100644
--- a/test/org/traccar/protocol/Gt06ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Gt06ProtocolDecoderTest.java
@@ -16,6 +16,9 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, binary(
"78780D01086471700328358100093F040D0A"));
+ verifyAttributes(decoder, binary(
+ "797900262100000000020043006f006d006d0061006e00640020006500720072006f0072002100236e850d0a"));
+
verifyNotNull(decoder, binary(
"787803691604130318491475905BD30E25001E10BBF7635D14759006E626560501CC0028660F213228660F1F2828660EA81E286610731428660F20140D0A"));
diff --git a/test/org/traccar/protocol/Ivt401ProtocolDecoderTest.java b/test/org/traccar/protocol/Ivt401ProtocolDecoderTest.java
new file mode 100644
index 000000000..c0f076014
--- /dev/null
+++ b/test/org/traccar/protocol/Ivt401ProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Ivt401ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Ivt401ProtocolDecoder decoder = new Ivt401ProtocolDecoder(new Ivt401Protocol());
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050291991,090315,133525,+12.990582,+77.589080,0,0,944,13,1,5,000,00,0.00,10.88,6.31,29.55,0.00,0,0.99,66,0,0,88,95)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050269732,061117,220046,+21.134126,+74.798561,51,28,204,14,1,5,100,00,0.0,13.92,7.82,23.74,0.0,1,1.33,-9,0,429,4848,67)"));
+
+ verifyPosition(decoder, text(
+ "(TLN,356917050269732,061117,220116,+21.137619,+74.800659,52,28,202,14,1,3,100,00,0.0,13.92,7.82,23.74,0.0,1,1.26,-23,0,445,4849,125)"));
+
+ verifyPosition(decoder, text(
+ "(TLA,356917050217335,190115,011336,+12.932403,+79.898887,0,0,71.7,08,3,10,000,00,0.00,10.41,7.07,26.84,0.00,0,0.99,63,0,0,0,0,0,0,000000000,0,0,0,0,0,0,0,2,0,0,14,86)"));
+
+ verifyPosition(decoder, text(
+ "(TLB,356917050291991,090315,133525,+12.990582,+77.589080,0,0,944,13,1,5,000,00,0.00,10.88,6.31,29.55,0.00,0,0.99,66,0,0,88,95)"));
+
+ verifyPosition(decoder, text(
+ "(TLL,356917050217335,190115,011336,+12.932403,+79.898887,0,0,71.7,08,3,10,000,00,0.00,10.41,7.07,26.84,0.00,0,0.99,63,0,0,0,0,0,0,000000000,0,0,0,0,0,0,0,2,0,0,14,86)"));
+
+ }
+
+}
diff --git a/test/org/traccar/protocol/Jt600ProtocolDecoderTest.java b/test/org/traccar/protocol/Jt600ProtocolDecoderTest.java
index eb82d8c23..33959bc29 100644
--- a/test/org/traccar/protocol/Jt600ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Jt600ProtocolDecoderTest.java
@@ -12,6 +12,9 @@ public class Jt600ProtocolDecoderTest extends ProtocolTest {
Jt600ProtocolDecoder decoder = new Jt600ProtocolDecoder(new Jt600Protocol());
verifyPositions(decoder, binary(
+ "24657060730131001b13111710361906538525079524797f000000000000000003f300036c"));
+
+ verifyPositions(decoder, binary(
"24624090196121001b19071703493631277203074235752f295800005308010000768b0822"));
verifyPositions(decoder, binary(
diff --git a/test/org/traccar/protocol/OkoProtocolDecoderTest.java b/test/org/traccar/protocol/OkoProtocolDecoderTest.java
new file mode 100644
index 000000000..7e913ca2b
--- /dev/null
+++ b/test/org/traccar/protocol/OkoProtocolDecoderTest.java
@@ -0,0 +1,33 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class OkoProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ OkoProtocolDecoder decoder = new OkoProtocolDecoder(new OkoProtocol());
+
+ verifyPosition(decoder, text(
+ "{861694033681089,045403.00,A,4924.14181,N,03207.43787,E,0.080,,151117,07,0.00,01,24.8,1,02,5n4}"));
+
+ verifyPosition(decoder, text(
+ "{045411.00,A,4924.14243,N,03207.43754,E,0.172,,151117,07,0.00,F9,28.1,2,C2,5n4}"));
+
+ verifyPosition(decoder, text(
+ "{861001001016415,115031.000,A,4804.101180,N,02255.227002,E,4.121,111.0,211215,6,0.00,F7,13.6,1,00"));
+
+ verifyPosition(decoder, text(
+ "{132810.000,A,4926.4243,N,03203.6831,E,25.0,183,131011,07,5.69,05,14.1,1,82,3N5"));
+
+ verifyPosition(decoder, text(
+ "{115034.000,A,4804.098944,N,02255.233436,E,7.858,120.9,211215,7,0.00,F7,13.7,1,00"));
+
+ verifyPosition(decoder, text(
+ "{115038.000,A,4804.091227,N,02255.250213,E,17.621,128.1,211215,8,0.00,00,13.7,2,00"));
+
+ }
+
+}