diff options
40 files changed, 974 insertions, 144 deletions
@@ -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")); + + } + +} |