aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pom.xml5
-rw-r--r--schema/changelog-3.11.xml21
-rw-r--r--schema/changelog-master.xml1
-rw-r--r--setup/default.xml10
-rw-r--r--src/org/traccar/Context.java17
-rw-r--r--src/org/traccar/FilterHandler.java3
-rw-r--r--src/org/traccar/api/resource/NotificationResource.java12
-rw-r--r--src/org/traccar/database/NotificationManager.java14
-rw-r--r--src/org/traccar/database/QueryBuilder.java7
-rw-r--r--src/org/traccar/geocoder/GeocodeFarmGeocoder.java10
-rw-r--r--src/org/traccar/geocoder/NominatimGeocoder.java1
-rw-r--r--src/org/traccar/model/Geofence.java1
-rw-r--r--src/org/traccar/model/Notification.java10
-rw-r--r--src/org/traccar/model/Position.java6
-rw-r--r--src/org/traccar/model/User.java10
-rw-r--r--src/org/traccar/notification/NotificationFormatter.java34
-rw-r--r--src/org/traccar/notification/NotificationMail.java5
-rw-r--r--src/org/traccar/notification/NotificationSms.java50
-rw-r--r--src/org/traccar/protocol/CarcellProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/GalileoProtocolDecoder.java102
-rw-r--r--src/org/traccar/protocol/Gl200ProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/GoSafeProtocolDecoder.java5
-rw-r--r--src/org/traccar/protocol/HaicomProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/IdplProtocol.java4
-rw-r--r--src/org/traccar/protocol/IdplProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/KhdProtocolDecoder.java40
-rw-r--r--src/org/traccar/protocol/OigoProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/PricolProtocol.java7
-rw-r--r--src/org/traccar/protocol/PricolProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/Stl060ProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/SuntechProtocolDecoder.java4
-rw-r--r--src/org/traccar/protocol/TeltonikaProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/TmgProtocolDecoder.java23
-rw-r--r--src/org/traccar/protocol/TrakMateProtocolDecoder.java2
-rw-r--r--src/org/traccar/smpp/ClientSmppSessionHandler.java74
-rw-r--r--src/org/traccar/smpp/EnquireLinkTask.java56
-rw-r--r--src/org/traccar/smpp/ReconnectionTask.java31
-rw-r--r--src/org/traccar/smpp/SmppClient.java217
-rw-r--r--templates/sms/alarm.vm1
-rw-r--r--templates/sms/commandResult.vm1
-rw-r--r--templates/sms/deviceMoving.vm1
-rw-r--r--templates/sms/deviceOffline.vm1
-rw-r--r--templates/sms/deviceOnline.vm1
-rw-r--r--templates/sms/deviceOverspeed.vm8
-rw-r--r--templates/sms/deviceStopped.vm1
-rw-r--r--templates/sms/deviceUnknown.vm1
-rw-r--r--templates/sms/geofenceEnter.vm1
-rw-r--r--templates/sms/geofenceExit.vm1
-rw-r--r--templates/sms/ignitionOff.vm1
-rw-r--r--templates/sms/ignitionOn.vm1
-rw-r--r--templates/sms/maintenance.vm1
-rw-r--r--templates/sms/test.vm1
-rw-r--r--templates/sms/unknown.vm1
-rw-r--r--test/org/traccar/geocoder/GeocoderTest.java2
-rw-r--r--test/org/traccar/protocol/KhdProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/PricolProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/Tk103ProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/TmgProtocolDecoderTest.java8
58 files changed, 723 insertions, 118 deletions
diff --git a/pom.xml b/pom.xml
index 643627b64..952a24795 100644
--- a/pom.xml
+++ b/pom.xml
@@ -148,6 +148,11 @@
<artifactId>ical4j</artifactId>
<version>2.0.0</version>
</dependency>
+ <dependency>
+ <groupId>com.fizzed</groupId>
+ <artifactId>ch-smpp</artifactId>
+ <version>5.0.9</version>
+ </dependency>
</dependencies>
<build>
diff --git a/schema/changelog-3.11.xml b/schema/changelog-3.11.xml
new file mode 100644
index 000000000..a8deb0295
--- /dev/null
+++ b/schema/changelog-3.11.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<databaseChangeLog
+ xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
+ logicalFilePath="changelog-3.11">
+
+ <changeSet author="author" id="changelog-3.11">
+
+ <addColumn tableName="users">
+ <column name="phone" type="VARCHAR(128)" />
+ </addColumn>
+
+ <addColumn tableName="notifications">
+ <column name="sms" type="BOOLEAN" defaultValueBoolean="false" />
+ </addColumn>
+
+ </changeSet>
+
+</databaseChangeLog>
diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml
index 2aed50e72..eaa5324f1 100644
--- a/schema/changelog-master.xml
+++ b/schema/changelog-master.xml
@@ -12,4 +12,5 @@
<include file="changelog-3.8.xml" relativeToChangelogFile="true" />
<include file="changelog-3.9.xml" relativeToChangelogFile="true" />
<include file="changelog-3.10.xml" relativeToChangelogFile="true" />
+ <include file="changelog-3.11.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
diff --git a/setup/default.xml b/setup/default.xml
index 7cbf9dab6..b5bc4202a 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -64,14 +64,15 @@
</entry>
<entry key='database.insertUser'>
- INSERT INTO users (name, email, hashedPassword, salt, readonly, admin, map, distanceUnit, speedUnit, latitude, longitude, zoom, twelveHourFormat, coordinateFormat, disabled, expirationTime, deviceLimit, userLimit, deviceReadonly, token, attributes)
- VALUES (:name, :email, :hashedPassword, :salt, :readonly, :admin, :map, :distanceUnit, :speedUnit, :latitude, :longitude, :zoom, :twelveHourFormat, :coordinateFormat, :disabled, :expirationTime, :deviceLimit, :userLimit, :deviceReadonly, :token, :attributes)
+ INSERT INTO users (name, email, phone, hashedPassword, salt, readonly, admin, map, distanceUnit, speedUnit, latitude, longitude, zoom, twelveHourFormat, coordinateFormat, disabled, expirationTime, deviceLimit, userLimit, deviceReadonly, token, attributes)
+ VALUES (:name, :email, :phone, :hashedPassword, :salt, :readonly, :admin, :map, :distanceUnit, :speedUnit, :latitude, :longitude, :zoom, :twelveHourFormat, :coordinateFormat, :disabled, :expirationTime, :deviceLimit, :userLimit, :deviceReadonly, :token, :attributes)
</entry>
<entry key='database.updateUser'>
UPDATE users SET
name = :name,
email = :email,
+ phone = :phone,
readonly = :readonly,
admin = :admin,
map = :map,
@@ -268,8 +269,8 @@
</entry>
<entry key='database.insertNotification'>
- INSERT INTO notifications (userId, type, web, mail, attributes)
- VALUES (:userId, :type, :web, :mail, :attributes)
+ INSERT INTO notifications (userId, type, web, mail, sms, attributes)
+ VALUES (:userId, :type, :web, :mail, :sms, :attributes)
</entry>
<entry key='database.updateNotification'>
@@ -278,6 +279,7 @@
type = :type,
web = :web,
mail = :mail,
+ sms = :sms,
attributes = :attributes
WHERE id = :id
</entry>
diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java
index 27e61d32d..68558630d 100644
--- a/src/org/traccar/Context.java
+++ b/src/org/traccar/Context.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.
@@ -50,6 +50,7 @@ import org.traccar.geolocation.GeolocationProvider;
import org.traccar.geolocation.MozillaGeolocationProvider;
import org.traccar.geolocation.OpenCellIdGeolocationProvider;
import org.traccar.notification.EventForwarder;
+import org.traccar.smpp.SmppClient;
import org.traccar.web.WebServer;
public final class Context {
@@ -177,6 +178,12 @@ public final class Context {
return statisticsManager;
}
+ private static SmppClient smppClient;
+
+ public static SmppClient getSmppManager() {
+ return smppClient;
+ }
+
public static void init(String[] arguments) throws Exception {
config = new Config();
@@ -232,7 +239,7 @@ public final class Context {
geocoder = new FactualGeocoder(url, key, cacheSize);
break;
case "geocodefarm":
- geocoder = new GeocodeFarmGeocoder(key, cacheSize);
+ geocoder = new GeocodeFarmGeocoder(key, language, cacheSize);
break;
default:
geocoder = new GoogleGeocoder(key, language, cacheSize);
@@ -278,7 +285,7 @@ public final class Context {
notificationManager = new NotificationManager(dataManager);
Properties velocityProperties = new Properties();
velocityProperties.setProperty("file.resource.loader.path",
- Context.getConfig().getString("mail.templatesPath", "templates/mail") + "/");
+ Context.getConfig().getString("templates.rootPath", "templates") + "/");
velocityProperties.setProperty("runtime.log.logsystem.class",
"org.apache.velocity.runtime.log.NullLogChute");
@@ -307,6 +314,10 @@ public final class Context {
statisticsManager = new StatisticsManager();
+ if (config.getBoolean("sms.smpp.enable")) {
+ smppClient = new SmppClient();
+ }
+
}
public static void init(IdentityManager testIdentityManager) {
diff --git a/src/org/traccar/FilterHandler.java b/src/org/traccar/FilterHandler.java
index 0a54cfeca..ec3e67ec4 100644
--- a/src/org/traccar/FilterHandler.java
+++ b/src/org/traccar/FilterHandler.java
@@ -109,8 +109,7 @@ public class FilterHandler extends BaseDataHandler {
}
private boolean filterApproximate(Position position) {
- Boolean approximate = position.getBoolean(Position.KEY_APPROXIMATE);
- return filterApproximate && approximate != null && approximate;
+ return filterApproximate && position.getBoolean(Position.KEY_APPROXIMATE);
}
private boolean filterStatic(Position position) {
diff --git a/src/org/traccar/api/resource/NotificationResource.java b/src/org/traccar/api/resource/NotificationResource.java
index 03f7e4ba0..dee972607 100644
--- a/src/org/traccar/api/resource/NotificationResource.java
+++ b/src/org/traccar/api/resource/NotificationResource.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.
@@ -33,6 +33,12 @@ import org.traccar.api.BaseResource;
import org.traccar.model.Event;
import org.traccar.model.Notification;
import org.traccar.notification.NotificationMail;
+import org.traccar.notification.NotificationSms;
+
+import com.cloudhopper.smpp.type.RecoverablePduException;
+import com.cloudhopper.smpp.type.SmppChannelException;
+import com.cloudhopper.smpp.type.SmppTimeoutException;
+import com.cloudhopper.smpp.type.UnrecoverablePduException;
@Path("users/notifications")
@Produces(MediaType.APPLICATION_JSON)
@@ -62,8 +68,10 @@ public class NotificationResource extends BaseResource {
@Path("test")
@POST
- public Response testMail() throws MessagingException {
+ public Response testMessage() throws MessagingException, RecoverablePduException,
+ UnrecoverablePduException, SmppTimeoutException, SmppChannelException, InterruptedException {
NotificationMail.sendMailSync(getUserId(), new Event("test", 0), null);
+ NotificationSms.sendSmsSync(getUserId(), new Event("test", 0), null);
return Response.noContent().build();
}
diff --git a/src/org/traccar/database/NotificationManager.java b/src/org/traccar/database/NotificationManager.java
index 4ae173c6a..48caa615c 100644
--- a/src/org/traccar/database/NotificationManager.java
+++ b/src/org/traccar/database/NotificationManager.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.
@@ -32,6 +32,7 @@ import org.traccar.model.Event;
import org.traccar.model.Notification;
import org.traccar.model.Position;
import org.traccar.notification.NotificationMail;
+import org.traccar.notification.NotificationSms;
public class NotificationManager {
@@ -65,6 +66,9 @@ public class NotificationManager {
if (notification.getMail()) {
NotificationMail.sendMailAsync(userId, event, position);
}
+ if (notification.getSms()) {
+ NotificationSms.sendSmsAsync(userId, event, position);
+ }
}
}
}
@@ -131,8 +135,9 @@ public class NotificationManager {
Notification cachedNotification = getUserNotificationByType(notification.getUserId(), notification.getType());
if (cachedNotification != null) {
if (cachedNotification.getWeb() != notification.getWeb()
- || cachedNotification.getMail() != notification.getMail()) {
- if (!notification.getWeb() && !notification.getMail()) {
+ || cachedNotification.getMail() != notification.getMail()
+ || cachedNotification.getSms() != notification.getSms()) {
+ if (!notification.getWeb() && !notification.getMail() && !notification.getSms()) {
try {
dataManager.removeNotification(cachedNotification);
} catch (SQLException error) {
@@ -149,6 +154,7 @@ public class NotificationManager {
try {
cachedNotification.setWeb(notification.getWeb());
cachedNotification.setMail(notification.getMail());
+ cachedNotification.setSms(notification.getSms());
cachedNotification.setAttributes(notification.getAttributes());
} finally {
notificationsLock.writeLock().unlock();
@@ -162,7 +168,7 @@ public class NotificationManager {
} else {
notification.setId(cachedNotification.getId());
}
- } else if (notification.getWeb() || notification.getMail()) {
+ } else if (notification.getWeb() || notification.getMail() || notification.getSms()) {
try {
dataManager.addNotification(notification);
} catch (SQLException error) {
diff --git a/src/org/traccar/database/QueryBuilder.java b/src/org/traccar/database/QueryBuilder.java
index 9972bb74d..83bc5de2e 100644
--- a/src/org/traccar/database/QueryBuilder.java
+++ b/src/org/traccar/database/QueryBuilder.java
@@ -15,6 +15,7 @@
*/
package org.traccar.database;
+import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.traccar.Context;
import org.traccar.helper.Log;
@@ -260,7 +261,8 @@ public final class QueryBuilder {
Method[] methods = object.getClass().getMethods();
for (Method method : methods) {
- if (method.getName().startsWith("get") && method.getParameterTypes().length == 0) {
+ if (method.getName().startsWith("get") && method.getParameterTypes().length == 0
+ && !method.isAnnotationPresent(JsonIgnore.class)) {
String name = method.getName().substring(3);
try {
if (method.getReturnType().equals(boolean.class)) {
@@ -424,7 +426,8 @@ public final class QueryBuilder {
Method[] methods = clazz.getMethods();
for (final Method method : methods) {
- if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) {
+ if (method.getName().startsWith("set") && method.getParameterTypes().length == 1
+ && !method.isAnnotationPresent(JsonIgnore.class)) {
final String name = method.getName().substring(3);
diff --git a/src/org/traccar/geocoder/GeocodeFarmGeocoder.java b/src/org/traccar/geocoder/GeocodeFarmGeocoder.java
index db23aab79..73ff85aa4 100644
--- a/src/org/traccar/geocoder/GeocodeFarmGeocoder.java
+++ b/src/org/traccar/geocoder/GeocodeFarmGeocoder.java
@@ -19,15 +19,19 @@ import javax.json.JsonObject;
public class GeocodeFarmGeocoder extends JsonGeocoder {
- private static String formatUrl(String key) {
+ private static String formatUrl(String key, String language) {
String url = "https://www.geocode.farm/v3/json/reverse/";
+ url += "?lat=%f&lon=%f&country=us&count=1";
if (key != null) {
url += "&key=" + key;
}
+ if (language != null) {
+ url += "&lang=" + language;
+ }
return url;
}
- public GeocodeFarmGeocoder(String key, int cacheSize) {
- super(formatUrl(key), cacheSize);
+ public GeocodeFarmGeocoder(String key, String language, int cacheSize) {
+ super(formatUrl(key, language), cacheSize);
}
@Override
diff --git a/src/org/traccar/geocoder/NominatimGeocoder.java b/src/org/traccar/geocoder/NominatimGeocoder.java
index c10a1414e..4f33468fd 100644
--- a/src/org/traccar/geocoder/NominatimGeocoder.java
+++ b/src/org/traccar/geocoder/NominatimGeocoder.java
@@ -23,6 +23,7 @@ public class NominatimGeocoder extends JsonGeocoder {
if (url == null) {
url = "http://nominatim.openstreetmap.org/reverse";
}
+ url += "?format=json&lat=%f&lon=%f&zoom=18&addressdetails=1";
if (key != null) {
url += "&key=" + key;
}
diff --git a/src/org/traccar/model/Geofence.java b/src/org/traccar/model/Geofence.java
index f10ce6862..ffd4876bf 100644
--- a/src/org/traccar/model/Geofence.java
+++ b/src/org/traccar/model/Geofence.java
@@ -79,6 +79,7 @@ public class Geofence extends Extensible {
return geometry;
}
+ @JsonIgnore
public void setGeometry(GeofenceGeometry geometry) {
area = geometry.toWkt();
this.geometry = geometry;
diff --git a/src/org/traccar/model/Notification.java b/src/org/traccar/model/Notification.java
index dd5f66f15..6c61cafaf 100644
--- a/src/org/traccar/model/Notification.java
+++ b/src/org/traccar/model/Notification.java
@@ -56,4 +56,14 @@ public class Notification extends Extensible {
public void setMail(boolean mail) {
this.mail = mail;
}
+
+ private boolean sms;
+
+ public boolean getSms() {
+ return sms;
+ }
+
+ public void setSms(boolean sms) {
+ this.sms = sms;
+ }
}
diff --git a/src/org/traccar/model/Position.java b/src/org/traccar/model/Position.java
index 350524f32..18e7f7f46 100644
--- a/src/org/traccar/model/Position.java
+++ b/src/org/traccar/model/Position.java
@@ -22,6 +22,7 @@ public class Position extends Message {
public static final String KEY_ORIGINAL = "raw";
public static final String KEY_INDEX = "index";
public static final String KEY_HDOP = "hdop";
+ public static final String KEY_VDOP = "vdop";
public static final String KEY_SATELLITES = "sat"; // in use
public static final String KEY_SATELLITES_VISIBLE = "satVisible";
public static final String KEY_RSSI = "rssi";
@@ -39,7 +40,8 @@ public class Position extends Message {
public static final String KEY_FUEL = "fuel";
public static final String KEY_FUEL_CONSUMPTION = "fuelConsumption";
public static final String KEY_RFID = "rfid";
- public static final String KEY_VERSION = "version";
+ public static final String KEY_VERSION_FW = "versionFw";
+ public static final String KEY_VERSION_HW = "versionHw";
public static final String KEY_TYPE = "type";
public static final String KEY_IGNITION = "ignition";
public static final String KEY_FLAGS = "flags";
@@ -56,6 +58,8 @@ public class Position extends Message {
public static final String KEY_ARMED = "armed";
public static final String KEY_ACCURACY = "accuracy";
public static final String KEY_GEOFENCE = "geofence";
+ public static final String KEY_ACCELERATION = "acceleration";
+ public static final String KEY_DEVICE_TEMP = "deviceTemp";
public static final String KEY_DTCS = "dtcs";
public static final String KEY_OBD_SPEED = "obdSpeed";
diff --git a/src/org/traccar/model/User.java b/src/org/traccar/model/User.java
index a89a75dd8..274f2b2a2 100644
--- a/src/org/traccar/model/User.java
+++ b/src/org/traccar/model/User.java
@@ -42,6 +42,16 @@ public class User extends Extensible {
this.email = email;
}
+ private String phone;
+
+ public String getPhone() {
+ return phone;
+ }
+
+ public void setPhone(String phone) {
+ this.phone = phone;
+ }
+
private boolean readonly;
public boolean getReadonly() {
diff --git a/src/org/traccar/notification/NotificationFormatter.java b/src/org/traccar/notification/NotificationFormatter.java
index b68d53ee0..cec238548 100644
--- a/src/org/traccar/notification/NotificationFormatter.java
+++ b/src/org/traccar/notification/NotificationFormatter.java
@@ -1,5 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@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,7 +34,7 @@ public final class NotificationFormatter {
private NotificationFormatter() {
}
- public static MailMessage formatMessage(long userId, Event event, Position position) {
+ private static VelocityContext prepareContext(long userId, Event event, Position position) {
Device device = Context.getIdentityManager().getDeviceById(event.getDeviceId());
VelocityContext velocityContext = new VelocityContext();
@@ -47,18 +48,35 @@ public final class NotificationFormatter {
velocityContext.put("geofence", Context.getGeofenceManager().getGeofence(event.getGeofenceId()));
}
velocityContext.put("webUrl", Context.getVelocityEngine().getProperty("web.url"));
+ return velocityContext;
+ }
- Template template = null;
+ private static Template getTemplate(Event event, String path) {
+ Template template;
try {
- template = Context.getVelocityEngine().getTemplate(event.getType() + ".vm", StandardCharsets.UTF_8.name());
+ template = Context.getVelocityEngine().getTemplate(path + event.getType() + ".vm",
+ StandardCharsets.UTF_8.name());
} catch (ResourceNotFoundException error) {
Log.warning(error);
- template = Context.getVelocityEngine().getTemplate("unknown.vm", StandardCharsets.UTF_8.name());
+ template = Context.getVelocityEngine().getTemplate(path + "unknown.vm",
+ StandardCharsets.UTF_8.name());
}
+ return template;
+ }
+
+ public static MailMessage formatMailMessage(long userId, Event event, Position position) {
+ VelocityContext velocityContext = prepareContext(userId, event, position);
+ StringWriter writer = new StringWriter();
+ getTemplate(event, Context.getConfig().getString("mail.templatesPath", "mail") + "/")
+ .merge(velocityContext, writer);
+ return new MailMessage((String) velocityContext.get("subject"), writer.toString());
+ }
+ public static String formatSmsMessage(long userId, Event event, Position position) {
+ VelocityContext velocityContext = prepareContext(userId, event, position);
StringWriter writer = new StringWriter();
- template.merge(velocityContext, writer);
- String subject = (String) velocityContext.get("subject");
- return new MailMessage(subject, writer.toString());
+ getTemplate(event, Context.getConfig().getString("sms.templatesPath", "sms") + "/")
+ .merge(velocityContext, writer);
+ return writer.toString();
}
}
diff --git a/src/org/traccar/notification/NotificationMail.java b/src/org/traccar/notification/NotificationMail.java
index 7b7ef6e74..41cb2e681 100644
--- a/src/org/traccar/notification/NotificationMail.java
+++ b/src/org/traccar/notification/NotificationMail.java
@@ -1,5 +1,6 @@
/*
- * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -106,7 +107,7 @@ public final class NotificationMail {
}
message.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
- MailMessage mailMessage = NotificationFormatter.formatMessage(userId, event, position);
+ MailMessage mailMessage = NotificationFormatter.formatMailMessage(userId, event, position);
message.setSubject(mailMessage.getSubject());
message.setContent(mailMessage.getBody(), "text/html; charset=utf-8");
diff --git a/src/org/traccar/notification/NotificationSms.java b/src/org/traccar/notification/NotificationSms.java
new file mode 100644
index 000000000..cb5dd563a
--- /dev/null
+++ b/src/org/traccar/notification/NotificationSms.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@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.notification;
+
+import org.traccar.Context;
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+import org.traccar.model.User;
+
+import com.cloudhopper.smpp.type.RecoverablePduException;
+import com.cloudhopper.smpp.type.SmppChannelException;
+import com.cloudhopper.smpp.type.SmppTimeoutException;
+import com.cloudhopper.smpp.type.UnrecoverablePduException;
+
+public final class NotificationSms {
+
+ private NotificationSms() {
+ }
+
+ public static void sendSmsAsync(long userId, Event event, Position position) {
+ User user = Context.getPermissionsManager().getUser(userId);
+ if (Context.getSmppManager() != null && user.getPhone() != null) {
+ Context.getSmppManager().sendMessageAsync(user.getPhone(),
+ NotificationFormatter.formatSmsMessage(userId, event, position));
+ }
+ }
+
+ public static void sendSmsSync(long userId, Event event, Position position) throws RecoverablePduException,
+ UnrecoverablePduException, SmppTimeoutException, SmppChannelException, InterruptedException {
+ User user = Context.getPermissionsManager().getUser(userId);
+ if (Context.getSmppManager() != null && user.getPhone() != null) {
+ Context.getSmppManager().sendMessageSync(user.getPhone(),
+ NotificationFormatter.formatSmsMessage(userId, event, position));
+ }
+ }
+}
diff --git a/src/org/traccar/protocol/CarcellProtocolDecoder.java b/src/org/traccar/protocol/CarcellProtocolDecoder.java
index 23889a5bd..615115147 100644
--- a/src/org/traccar/protocol/CarcellProtocolDecoder.java
+++ b/src/org/traccar/protocol/CarcellProtocolDecoder.java
@@ -118,7 +118,7 @@ public class CarcellProtocolDecoder extends BaseProtocolDecoder {
}
if (parser.hasNext(1)) {
- position.set("accel", parser.nextInt());
+ position.set(Position.KEY_ACCELERATION, parser.nextInt());
}
Double internalBattery = (parser.nextDouble() + 100d) * 0.0294d;
diff --git a/src/org/traccar/protocol/GalileoProtocolDecoder.java b/src/org/traccar/protocol/GalileoProtocolDecoder.java
index c9aae8e96..a3f99c674 100644
--- a/src/org/traccar/protocol/GalileoProtocolDecoder.java
+++ b/src/org/traccar/protocol/GalileoProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 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.
@@ -39,24 +39,6 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
- private static final int TAG_IMEI = 0x03;
- private static final int TAG_DATE = 0x20;
- private static final int TAG_COORDINATES = 0x30;
- private static final int TAG_SPEED_COURSE = 0x33;
- private static final int TAG_ALTITUDE = 0x34;
- private static final int TAG_STATUS = 0x40;
- private static final int TAG_POWER = 0x41;
- private static final int TAG_BATTERY = 0x42;
- private static final int TAG_ODOMETER = 0xd4;
- private static final int TAG_REFRIGERATOR = 0x5b;
- private static final int TAG_PRESSURE = 0x5c;
- private static final int TAG_CAN = 0xc1;
- private static final int TAG_ADC0 = 0x50;
- private static final int TAG_ADC1 = 0x51;
- private static final int TAG_ADC2 = 0x52;
- private static final int TAG_ADC3 = 0x53;
- private static final int TAG_ARRAY = 0xea;
-
private static final Map<Integer, Integer> TAG_LENGTH_MAP = new HashMap<>();
static {
@@ -96,10 +78,8 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
for (int i : l4) {
TAG_LENGTH_MAP.put(i, 4);
}
- TAG_LENGTH_MAP.put(TAG_COORDINATES, 9);
- TAG_LENGTH_MAP.put(TAG_IMEI, 15);
- TAG_LENGTH_MAP.put(TAG_REFRIGERATOR, 7); // variable length
- TAG_LENGTH_MAP.put(TAG_PRESSURE, 68);
+ TAG_LENGTH_MAP.put(0x5b, 7); // variable length
+ TAG_LENGTH_MAP.put(0x5c, 68);
}
private static int getTagLength(int tag) {
@@ -149,77 +129,87 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
tags.add(tag);
switch (tag) {
-
- case TAG_IMEI:
+ case 0x01:
+ position.set(Position.KEY_VERSION_HW, buf.readUnsignedByte());
+ break;
+ case 0x02:
+ position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
+ break;
+ case 0x03:
getDeviceSession(channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII));
break;
-
- case TAG_DATE:
+ case 0x04:
+ position.set("deviceId", buf.readUnsignedShort());
+ break;
+ case 0x10:
+ position.set(Position.KEY_INDEX, buf.readUnsignedShort());
+ break;
+ case 0x20:
position.setTime(new Date(buf.readUnsignedInt() * 1000));
break;
-
- case TAG_COORDINATES:
+ case 0x30:
hasLocation = true;
position.setValid((buf.readUnsignedByte() & 0xf0) == 0x00);
position.setLatitude(buf.readInt() / 1000000.0);
position.setLongitude(buf.readInt() / 1000000.0);
break;
-
- case TAG_SPEED_COURSE:
+ case 0x33:
position.setSpeed(buf.readUnsignedShort() * 0.0539957);
position.setCourse(buf.readUnsignedShort() * 0.1);
break;
-
- case TAG_ALTITUDE:
+ case 0x34:
position.setAltitude(buf.readShort());
break;
-
- case TAG_STATUS:
+ case 0x40:
position.set(Position.KEY_STATUS, buf.readUnsignedShort());
break;
-
- case TAG_POWER:
+ case 0x41:
position.set(Position.KEY_POWER, buf.readUnsignedShort());
break;
-
- case TAG_BATTERY:
+ case 0x42:
position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
break;
-
- case TAG_ODOMETER:
+ case 0x43:
+ position.set(Position.KEY_DEVICE_TEMP, buf.readByte());
+ break;
+ case 0x44:
+ position.set(Position.KEY_ACCELERATION, buf.readUnsignedInt());
+ break;
+ case 0x45:
+ position.set(Position.KEY_OUTPUT, buf.readUnsignedShort());
+ break;
+ case 0x46:
+ position.set(Position.KEY_INPUT, buf.readUnsignedShort());
+ break;
+ case 0xd4:
position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
break;
-
- case TAG_CAN:
+ case 0xc1:
position.set(Position.KEY_FUEL, buf.readUnsignedByte() * 0.4);
position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedByte() - 40);
position.set(Position.KEY_RPM, buf.readUnsignedShort() * 0.125);
break;
-
- case TAG_ADC0:
+ case 0x50:
position.set(Position.PREFIX_ADC + 0, buf.readUnsignedShort());
break;
-
- case TAG_ADC1:
+ case 0x51:
position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
break;
-
- case TAG_ADC2:
+ case 0x52:
position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
break;
-
- case TAG_ADC3:
+ case 0x53:
position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShort());
break;
-
- case TAG_ARRAY:
- buf.skipBytes(buf.readUnsignedByte());
+ case 0xe2:
+ position.set("userData", buf.readUnsignedInt());
+ break;
+ case 0xea:
+ position.set("userDataArray", ChannelBuffers.hexDump(buf.readBytes(buf.readUnsignedByte())));
break;
-
default:
buf.skipBytes(getTagLength(tag));
break;
-
}
}
if (hasLocation && position.getFixTime() != null) {
diff --git a/src/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/org/traccar/protocol/Gl200ProtocolDecoder.java
index ef434b779..4103ebba6 100644
--- a/src/org/traccar/protocol/Gl200ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gl200ProtocolDecoder.java
@@ -387,8 +387,8 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder {
position.setDeviceId(deviceSession.getDeviceId());
position.set("deviceType", parser.next());
- position.set("firmwareVersion", parser.nextInt(16));
- position.set("hardwareVersion", parser.nextInt(16));
+ position.set(Position.KEY_VERSION_FW, parser.nextInt(16));
+ position.set(Position.KEY_VERSION_HW, parser.nextInt(16));
DateBuilder dateBuilder = new DateBuilder()
.setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
diff --git a/src/org/traccar/protocol/GoSafeProtocolDecoder.java b/src/org/traccar/protocol/GoSafeProtocolDecoder.java
index 94d12e989..96f745917 100644
--- a/src/org/traccar/protocol/GoSafeProtocolDecoder.java
+++ b/src/org/traccar/protocol/GoSafeProtocolDecoder.java
@@ -64,7 +64,7 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder {
.number("(d+);") // course
.number("(d+);") // altitude
.number("(d+.d+)") // hdop
- .number("(?:;d+.d+)?") // vdop
+ .number(";(d+.d+)").optional() // vdop
.expression(",?")
.groupEnd()
.groupBegin()
@@ -154,7 +154,8 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder {
position.setCourse(parser.nextDouble());
position.setAltitude(parser.nextDouble());
- position.set(Position.KEY_HDOP, parser.next());
+ position.set(Position.KEY_HDOP, parser.nextDouble());
+ position.set(Position.KEY_VDOP, parser.nextDouble());
if (parser.hasNext(5)) {
position.setNetwork(new Network(CellTower.from(
diff --git a/src/org/traccar/protocol/HaicomProtocolDecoder.java b/src/org/traccar/protocol/HaicomProtocolDecoder.java
index 7cd0b37f2..97e980290 100644
--- a/src/org/traccar/protocol/HaicomProtocolDecoder.java
+++ b/src/org/traccar/protocol/HaicomProtocolDecoder.java
@@ -72,7 +72,7 @@ public class HaicomProtocolDecoder extends BaseProtocolDecoder {
}
position.setDeviceId(deviceSession.getDeviceId());
- position.set(Position.KEY_VERSION, parser.next());
+ position.set(Position.KEY_VERSION_FW, parser.next());
DateBuilder dateBuilder = new DateBuilder()
.setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
diff --git a/src/org/traccar/protocol/IdplProtocol.java b/src/org/traccar/protocol/IdplProtocol.java
index a954397cd..f90d3fe7f 100644
--- a/src/org/traccar/protocol/IdplProtocol.java
+++ b/src/org/traccar/protocol/IdplProtocol.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.
@@ -28,7 +28,7 @@ import org.traccar.TrackerServer;
public class IdplProtocol extends BaseProtocol {
public IdplProtocol() {
- super("ldpl");
+ super("idpl");
}
@Override
diff --git a/src/org/traccar/protocol/IdplProtocolDecoder.java b/src/org/traccar/protocol/IdplProtocolDecoder.java
index 09a3db9bd..719314f28 100644
--- a/src/org/traccar/protocol/IdplProtocolDecoder.java
+++ b/src/org/traccar/protocol/IdplProtocolDecoder.java
@@ -106,7 +106,7 @@ public class IdplProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_OUTPUT, parser.nextInt());
position.set(Position.PREFIX_ADC + 1, parser.nextInt());
position.set(Position.PREFIX_ADC + 2, parser.nextInt());
- position.set(Position.KEY_VERSION, parser.next());
+ position.set(Position.KEY_VERSION_FW, parser.next());
position.set(Position.KEY_ARCHIVE, parser.next().equals("R"));
parser.next(); // checksum
diff --git a/src/org/traccar/protocol/KhdProtocolDecoder.java b/src/org/traccar/protocol/KhdProtocolDecoder.java
index 0c3d8ee51..1c88a8b9d 100644
--- a/src/org/traccar/protocol/KhdProtocolDecoder.java
+++ b/src/org/traccar/protocol/KhdProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 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.
@@ -93,24 +93,42 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder {
position.setLongitude(BcdUtil.readCoordinate(buf));
position.setSpeed(UnitsConverter.knotsFromKph(BcdUtil.readInteger(buf, 4)));
position.setCourse(BcdUtil.readInteger(buf, 4));
+ position.setValid((buf.readUnsignedByte() & 0x80) != 0);
- int flags = buf.readUnsignedByte();
- position.setValid((flags & 0x80) != 0);
+ if (type != MSG_ALARM) {
- if (type == MSG_ALARM) {
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium());
+ position.set(Position.KEY_STATUS, buf.readUnsignedInt());
+ position.set(Position.KEY_HDOP, buf.readUnsignedByte());
+ position.set(Position.KEY_VDOP, buf.readUnsignedByte());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
- buf.skipBytes(2);
+ buf.skipBytes(5); // other location data
- } else {
+ if (type == MSG_PERIPHERAL) {
- position.set(Position.KEY_ODOMETER, buf.readUnsignedMedium());
+ buf.readUnsignedShort(); // data length
- buf.skipBytes(4); // status
- buf.skipBytes(8); // other
+ int dataType = buf.readUnsignedByte();
- }
+ buf.readUnsignedByte(); // content length
- // parse extra data
+ switch (dataType) {
+ case 0x01:
+ position.set(Position.KEY_FUEL,
+ buf.readUnsignedByte() * 100 + buf.readUnsignedByte());
+ break;
+ case 0x02:
+ position.set(Position.PREFIX_TEMP + 1,
+ buf.readUnsignedByte() * 100 + buf.readUnsignedByte());
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ }
return position;
diff --git a/src/org/traccar/protocol/OigoProtocolDecoder.java b/src/org/traccar/protocol/OigoProtocolDecoder.java
index d10f754e9..9538053b6 100644
--- a/src/org/traccar/protocol/OigoProtocolDecoder.java
+++ b/src/org/traccar/protocol/OigoProtocolDecoder.java
@@ -208,7 +208,7 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
int index = buf.readUnsignedByte();
- position.set(Position.KEY_VERSION, buf.readUnsignedByte());
+ position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
position.set(Position.KEY_ODOMETER, (long) (buf.readUnsignedInt() * 1609.34));
diff --git a/src/org/traccar/protocol/PricolProtocol.java b/src/org/traccar/protocol/PricolProtocol.java
index 924c7c1d6..0005dc3c1 100644
--- a/src/org/traccar/protocol/PricolProtocol.java
+++ b/src/org/traccar/protocol/PricolProtocol.java
@@ -15,6 +15,7 @@
*/
package org.traccar.protocol;
+import org.jboss.netty.bootstrap.ConnectionlessBootstrap;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.handler.codec.frame.FixedLengthFrameDecoder;
@@ -38,6 +39,12 @@ public class PricolProtocol extends BaseProtocol {
pipeline.addLast("objectDecoder", new PricolProtocolDecoder(PricolProtocol.this));
}
});
+ serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("objectDecoder", new PricolProtocolDecoder(PricolProtocol.this));
+ }
+ });
}
}
diff --git a/src/org/traccar/protocol/PricolProtocolDecoder.java b/src/org/traccar/protocol/PricolProtocolDecoder.java
index 52b68cca6..3313afa22 100644
--- a/src/org/traccar/protocol/PricolProtocolDecoder.java
+++ b/src/org/traccar/protocol/PricolProtocolDecoder.java
@@ -87,7 +87,7 @@ public class PricolProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_RPM, buf.readUnsignedShort());
if (channel != null) {
- channel.write(ChannelBuffers.copiedBuffer("ACK", StandardCharsets.US_ASCII));
+ channel.write(ChannelBuffers.copiedBuffer("ACK", StandardCharsets.US_ASCII), remoteAddress);
}
return position;
diff --git a/src/org/traccar/protocol/Stl060ProtocolDecoder.java b/src/org/traccar/protocol/Stl060ProtocolDecoder.java
index fa789510e..87e55ea86 100644
--- a/src/org/traccar/protocol/Stl060ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Stl060ProtocolDecoder.java
@@ -112,7 +112,7 @@ public class Stl060ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ODOMETER, parser.nextInt());
position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
position.set(Position.KEY_FUEL, parser.nextInt());
- position.set("accel", parser.nextInt() == 1);
+ position.set(Position.KEY_ACCELERATION, parser.nextInt() == 1);
position.set(Position.KEY_OUTPUT, parser.nextInt() + parser.nextInt() << 1);
}
diff --git a/src/org/traccar/protocol/SuntechProtocolDecoder.java b/src/org/traccar/protocol/SuntechProtocolDecoder.java
index d081cb901..55d5c224a 100644
--- a/src/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -65,7 +65,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
}
position.setDeviceId(deviceSession.getDeviceId());
- position.set(Position.KEY_VERSION, values[index++]);
+ position.set(Position.KEY_VERSION_FW, values[index++]);
DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
@@ -116,7 +116,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
index += 1; // model
}
- position.set(Position.KEY_VERSION, values[index++]);
+ position.set(Position.KEY_VERSION_FW, values[index++]);
DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
diff --git a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
index 87cc72814..993029ddb 100644
--- a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
+++ b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java
@@ -89,7 +89,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_BATTERY, buf.readUnsignedShort() + "mV");
break;
case 70:
- position.set("pcbTemp", (length == 4 ? buf.readInt() : buf.readShort()) * 0.1);
+ position.set(Position.KEY_DEVICE_TEMP, (length == 4 ? buf.readInt() : buf.readShort()) * 0.1);
break;
case 72:
position.set(Position.PREFIX_TEMP + 1, buf.readInt() * 0.1);
diff --git a/src/org/traccar/protocol/TmgProtocolDecoder.java b/src/org/traccar/protocol/TmgProtocolDecoder.java
index 3b73a1516..40ce04c5b 100644
--- a/src/org/traccar/protocol/TmgProtocolDecoder.java
+++ b/src/org/traccar/protocol/TmgProtocolDecoder.java
@@ -18,6 +18,7 @@ package org.traccar.protocol;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
import org.traccar.DeviceSession;
+import org.traccar.helper.BitUtil;
import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
@@ -60,6 +61,11 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder {
.expression("([01]+),") // input
.expression("([01]+),") // output
.expression("[01]+,") // temper status
+ .number("(d+.?d*)[^,]*,") // adc1
+ .number("(d+.?d*)[^,]*,") // adc2
+ .number("d+.?d*,") // trip meter
+ .expression("([^,]*),") // software version
+ .expression("([^,]*),").optional() // rfid
.any()
.compile();
@@ -128,8 +134,21 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_IGNITION, parser.nextInt() == 1);
position.set(Position.KEY_BATTERY, parser.nextDouble());
position.set(Position.KEY_POWER, parser.nextDouble());
- position.set(Position.KEY_INPUT, parser.nextInt(2));
- position.set(Position.KEY_OUTPUT, parser.nextInt(2));
+
+ int input = parser.nextInt(2);
+ int output = parser.nextInt(2);
+
+ if (!BitUtil.check(input, 0)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_SOS);
+ }
+
+ position.set(Position.KEY_INPUT, input);
+ position.set(Position.KEY_OUTPUT, output);
+
+ position.set(Position.PREFIX_ADC + 1, parser.nextDouble());
+ position.set(Position.PREFIX_ADC + 2, parser.nextDouble());
+ position.set(Position.KEY_VERSION_FW, parser.next());
+ position.set(Position.KEY_RFID, parser.next());
return position;
}
diff --git a/src/org/traccar/protocol/TrakMateProtocolDecoder.java b/src/org/traccar/protocol/TrakMateProtocolDecoder.java
index 57003e832..d1bd1398b 100644
--- a/src/org/traccar/protocol/TrakMateProtocolDecoder.java
+++ b/src/org/traccar/protocol/TrakMateProtocolDecoder.java
@@ -125,7 +125,7 @@ public class TrakMateProtocolDecoder extends BaseProtocolDecoder {
.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
position.setTime(dateBuilder.getDate());
- position.set(Position.KEY_VERSION, parser.next());
+ position.set(Position.KEY_VERSION_FW, parser.next());
parser.next(); // hardware version
return position;
diff --git a/src/org/traccar/smpp/ClientSmppSessionHandler.java b/src/org/traccar/smpp/ClientSmppSessionHandler.java
new file mode 100644
index 000000000..721243f9f
--- /dev/null
+++ b/src/org/traccar/smpp/ClientSmppSessionHandler.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@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.smpp;
+
+import org.traccar.helper.Log;
+
+import com.cloudhopper.commons.charset.CharsetUtil;
+import com.cloudhopper.smpp.SmppConstants;
+import com.cloudhopper.smpp.impl.DefaultSmppSessionHandler;
+import com.cloudhopper.smpp.pdu.DeliverSm;
+import com.cloudhopper.smpp.pdu.PduRequest;
+import com.cloudhopper.smpp.pdu.PduResponse;
+
+public class ClientSmppSessionHandler extends DefaultSmppSessionHandler {
+
+ private SmppClient smppClient;
+
+ public ClientSmppSessionHandler(SmppClient smppClient) {
+ this.smppClient = smppClient;
+ }
+
+ @Override
+ public void firePduRequestExpired(PduRequest pduRequest) {
+ Log.warning("PDU request expired: " + pduRequest);
+ }
+
+ @Override
+ public PduResponse firePduRequestReceived(PduRequest request) {
+ PduResponse response = null;
+ try {
+ if (request instanceof DeliverSm) {
+ if (request.getOptionalParameters() != null) {
+ Log.debug("Message Delivered: "
+ + request.getOptionalParameter(SmppConstants.TAG_RECEIPTED_MSG_ID).getValueAsString()
+ + ", State: "
+ + request.getOptionalParameter(SmppConstants.TAG_MSG_STATE).getValueAsByte());
+ } else {
+ Log.debug("Message Received: "
+ + CharsetUtil.decode(((DeliverSm) request).getShortMessage(),
+ smppClient.mapDataCodingToCharset(((DeliverSm) request).getDataCoding()))
+ + ", Source Address: "
+ + ((DeliverSm) request).getSourceAddress().getAddress());
+ }
+ }
+ response = request.createResponse();
+ } catch (Throwable error) {
+ Log.warning(error);
+ response = request.createResponse();
+ response.setResultMessage(error.getMessage());
+ response.setCommandStatus(SmppConstants.STATUS_UNKNOWNERR);
+ }
+ return response;
+ }
+
+ @Override
+ public void fireChannelUnexpectedlyClosed() {
+ Log.warning("Smpp session channel unexpectedly closed");
+ smppClient.scheduleReconnect();
+ }
+}
diff --git a/src/org/traccar/smpp/EnquireLinkTask.java b/src/org/traccar/smpp/EnquireLinkTask.java
new file mode 100644
index 000000000..9a3121e24
--- /dev/null
+++ b/src/org/traccar/smpp/EnquireLinkTask.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@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.smpp;
+
+import org.traccar.helper.Log;
+
+import com.cloudhopper.smpp.SmppSession;
+import com.cloudhopper.smpp.pdu.EnquireLink;
+import com.cloudhopper.smpp.type.RecoverablePduException;
+import com.cloudhopper.smpp.type.SmppChannelException;
+import com.cloudhopper.smpp.type.SmppTimeoutException;
+import com.cloudhopper.smpp.type.UnrecoverablePduException;
+
+public class EnquireLinkTask implements Runnable {
+
+ private SmppClient smppClient;
+ private Integer enquireLinkTimeout;
+
+ public EnquireLinkTask(SmppClient smppClient, Integer enquireLinkTimeout) {
+ this.smppClient = smppClient;
+ this.enquireLinkTimeout = enquireLinkTimeout;
+ }
+
+ @Override
+ public void run() {
+ SmppSession smppSession = smppClient.getSession();
+ if (smppSession != null && smppSession.isBound()) {
+ try {
+ smppSession.enquireLink(new EnquireLink(), enquireLinkTimeout);
+ } catch (SmppTimeoutException | SmppChannelException
+ | RecoverablePduException | UnrecoverablePduException error) {
+ Log.warning("Enquire link failed, executing reconnect: ", error);
+ smppClient.reconnect();
+ } catch (InterruptedException error) {
+ Log.info("Enquire link interrupted, probably killed by reconnecting");
+ }
+ } else {
+ Log.warning("Enquire link running while session is not connected");
+ }
+ }
+
+}
diff --git a/src/org/traccar/smpp/ReconnectionTask.java b/src/org/traccar/smpp/ReconnectionTask.java
new file mode 100644
index 000000000..c9d9173ae
--- /dev/null
+++ b/src/org/traccar/smpp/ReconnectionTask.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@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.smpp;
+
+public class ReconnectionTask implements Runnable {
+
+ private final SmppClient smppClient;
+
+ protected ReconnectionTask(SmppClient smppClient) {
+ this.smppClient = smppClient;
+ }
+
+ @Override
+ public void run() {
+ smppClient.reconnect();
+ }
+}
diff --git a/src/org/traccar/smpp/SmppClient.java b/src/org/traccar/smpp/SmppClient.java
new file mode 100644
index 000000000..3680d20e2
--- /dev/null
+++ b/src/org/traccar/smpp/SmppClient.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2017 Anton Tananaev (anton@traccar.org)
+ * Copyright 2017 Andrey Kunitsyn (andrey@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.smpp;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+import org.traccar.Context;
+import org.traccar.helper.Log;
+
+import com.cloudhopper.commons.charset.CharsetUtil;
+import com.cloudhopper.smpp.SmppBindType;
+import com.cloudhopper.smpp.SmppConstants;
+import com.cloudhopper.smpp.SmppSession;
+import com.cloudhopper.smpp.SmppSessionConfiguration;
+import com.cloudhopper.smpp.impl.DefaultSmppClient;
+import com.cloudhopper.smpp.impl.DefaultSmppSessionHandler;
+import com.cloudhopper.smpp.pdu.SubmitSm;
+import com.cloudhopper.smpp.pdu.SubmitSmResp;
+import com.cloudhopper.smpp.type.Address;
+import com.cloudhopper.smpp.type.RecoverablePduException;
+import com.cloudhopper.smpp.type.SmppChannelException;
+import com.cloudhopper.smpp.type.SmppTimeoutException;
+import com.cloudhopper.smpp.type.UnrecoverablePduException;
+
+public class SmppClient {
+
+ private SmppSessionConfiguration sessionConfig = new SmppSessionConfiguration();
+ private SmppSession smppSession;
+ private DefaultSmppSessionHandler sessionHandler = new ClientSmppSessionHandler(this);
+ private ExecutorService executorService = Executors.newCachedThreadPool();
+ private DefaultSmppClient clientBootstrap = new DefaultSmppClient(executorService, 1);
+
+ private ScheduledExecutorService enquireLinkExecutor;
+ private ScheduledFuture<?> enquireLinkTask;
+ private Integer enquireLinkPeriod;
+ private Integer enquireLinkTimeout;
+
+ private ScheduledExecutorService reconnectionExecutor;
+ private ScheduledFuture<?> reconnectionTask;
+ private Integer reconnectionDelay;
+
+ private String sourceAddress;
+ private int submitTimeout;
+ private String charsetName;
+ private byte dataCoding;
+
+ private byte sourceTon;
+ private byte sourceNpi;
+
+ private byte destTon;
+ private byte destNpi;
+
+ public SmppClient() {
+ sessionConfig.setName("Traccar.smppSession");
+ sessionConfig.setInterfaceVersion(
+ (byte) Context.getConfig().getInteger("sms.smpp.version", SmppConstants.VERSION_3_4));
+ sessionConfig.setType(SmppBindType.TRANSCEIVER);
+ sessionConfig.setHost(Context.getConfig().getString("sms.smpp.host", "localhost"));
+ sessionConfig.setPort(Context.getConfig().getInteger("sms.smpp.port", 2775));
+ sessionConfig.setSystemId(Context.getConfig().getString("sms.smpp.username", "user"));
+ sessionConfig.setPassword(Context.getConfig().getString("sms.smpp.password", "password"));
+ sessionConfig.getLoggingOptions().setLogBytes(false);
+ sessionConfig.getLoggingOptions().setLogPdu(Context.getConfig().getBoolean("sms.smpp.logPdu"));
+
+ sourceAddress = Context.getConfig().getString("sms.smpp.sourceAddress", "");
+ submitTimeout = Context.getConfig().getInteger("sms.smpp.submitTimeout", 10000);
+
+ charsetName = Context.getConfig().getString("sms.smpp.charsetName", CharsetUtil.NAME_UCS_2);
+ dataCoding = (byte) Context.getConfig().getInteger("sms.smpp.dataCoding", SmppConstants.DATA_CODING_UCS2);
+
+ sourceTon = (byte) Context.getConfig().getInteger("sms.smpp.sourceTon", SmppConstants.TON_ALPHANUMERIC);
+ sourceNpi = (byte) Context.getConfig().getInteger("sms.smpp.sourceNpi", SmppConstants.NPI_UNKNOWN);
+
+ destTon = (byte) Context.getConfig().getInteger("sms.smpp.destTon", SmppConstants.TON_INTERNATIONAL);
+ destNpi = (byte) Context.getConfig().getInteger("sms.smpp.destNpi", SmppConstants.NPI_E164);
+
+ enquireLinkPeriod = Context.getConfig().getInteger("sms.smpp.enquireLinkPeriod", 60000);
+ enquireLinkTimeout = Context.getConfig().getInteger("sms.smpp.enquireLinkTimeout", 10000);
+ enquireLinkExecutor = Executors.newScheduledThreadPool(1, new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable runnable) {
+ Thread thread = new Thread(runnable);
+ String name = sessionConfig.getName();
+ thread.setName("EnquireLink-" + name);
+ return thread;
+ }
+ });
+
+ reconnectionDelay = Context.getConfig().getInteger("sms.smpp.reconnectionDelay", 10000);
+ reconnectionExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable runnable) {
+ Thread thread = new Thread(runnable);
+ String name = sessionConfig.getName();
+ thread.setName("Reconnection-" + name);
+ return thread;
+ }
+ });
+
+ scheduleReconnect();
+ }
+
+ public synchronized SmppSession getSession() {
+ return smppSession;
+ }
+
+ public String mapDataCodingToCharset(byte dataCoding) {
+ switch (dataCoding) {
+ case SmppConstants.DATA_CODING_LATIN1:
+ return CharsetUtil.NAME_ISO_8859_1;
+ case SmppConstants.DATA_CODING_UCS2:
+ return CharsetUtil.NAME_UCS_2;
+ default:
+ return CharsetUtil.NAME_GSM;
+ }
+ }
+
+ protected synchronized void reconnect() {
+ try {
+ disconnect();
+ smppSession = clientBootstrap.bind(sessionConfig, sessionHandler);
+ stopReconnectionkTask();
+ runEnquireLinkTask();
+ Log.info("Smpp session connected");
+ } catch (SmppTimeoutException | SmppChannelException
+ | UnrecoverablePduException | InterruptedException error) {
+ Log.warning("Unable to connect to smpp server: ", error);
+ }
+ }
+
+ public void scheduleReconnect() {
+ reconnectionTask = reconnectionExecutor.scheduleWithFixedDelay(
+ new ReconnectionTask(this),
+ reconnectionDelay, reconnectionDelay, TimeUnit.MILLISECONDS);
+ }
+
+ private void stopReconnectionkTask() {
+ if (reconnectionTask != null) {
+ reconnectionTask.cancel(false);
+ }
+ }
+
+ private void disconnect() {
+ stopEnquireLinkTask();
+ destroySession();
+ }
+
+ private void runEnquireLinkTask() {
+ enquireLinkTask = enquireLinkExecutor.scheduleWithFixedDelay(
+ new EnquireLinkTask(this, enquireLinkTimeout),
+ enquireLinkPeriod, enquireLinkPeriod, TimeUnit.MILLISECONDS);
+ }
+
+ private void stopEnquireLinkTask() {
+ if (enquireLinkTask != null) {
+ enquireLinkTask.cancel(true);
+ }
+ }
+
+ private void destroySession() {
+ if (smppSession != null) {
+ Log.debug("Cleaning up smpp session... ");
+ smppSession.destroy();
+ smppSession = null;
+ }
+ }
+
+ public synchronized void sendMessageSync(String destAddress, String message) throws RecoverablePduException,
+ UnrecoverablePduException, SmppTimeoutException, SmppChannelException, InterruptedException {
+ if (getSession() != null && getSession().isBound()) {
+ byte[] textBytes = CharsetUtil.encode(message, charsetName);
+
+ SubmitSm submit = new SubmitSm();
+ submit.setSourceAddress(new Address(sourceTon, sourceNpi, sourceAddress));
+ submit.setDestAddress(new Address(destTon, destNpi, destAddress));
+ submit.setDataCoding(dataCoding);
+ submit.setShortMessage(textBytes);
+ SubmitSmResp submitResponce = getSession().submit(submit, submitTimeout);
+ Log.debug("SMS submited, msg_id: " + submitResponce.getMessageId());
+ } else {
+ throw new SmppChannelException("Smpp session is not connected");
+ }
+ }
+
+ public void sendMessageAsync(final String destAddress, final String message) {
+ executorService.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ sendMessageSync(destAddress, message);
+ } catch (InterruptedException | RecoverablePduException | UnrecoverablePduException
+ | SmppTimeoutException | SmppChannelException error) {
+ Log.warning(error);
+ }
+ }
+ });
+ }
+}
diff --git a/templates/sms/alarm.vm b/templates/sms/alarm.vm
new file mode 100644
index 000000000..e35a573b3
--- /dev/null
+++ b/templates/sms/alarm.vm
@@ -0,0 +1 @@
+$device.name alarm: $position.getString("alarm") at $event.serverTime
diff --git a/templates/sms/commandResult.vm b/templates/sms/commandResult.vm
new file mode 100644
index 000000000..38fddcf25
--- /dev/null
+++ b/templates/sms/commandResult.vm
@@ -0,0 +1 @@
+$device.name command result received: $position.getString("result") at $event.serverTime
diff --git a/templates/sms/deviceMoving.vm b/templates/sms/deviceMoving.vm
new file mode 100644
index 000000000..febae6331
--- /dev/null
+++ b/templates/sms/deviceMoving.vm
@@ -0,0 +1 @@
+$device.name moving at $event.serverTime
diff --git a/templates/sms/deviceOffline.vm b/templates/sms/deviceOffline.vm
new file mode 100644
index 000000000..490502946
--- /dev/null
+++ b/templates/sms/deviceOffline.vm
@@ -0,0 +1 @@
+$device.name offline at $event.serverTime
diff --git a/templates/sms/deviceOnline.vm b/templates/sms/deviceOnline.vm
new file mode 100644
index 000000000..e8bc0a5bf
--- /dev/null
+++ b/templates/sms/deviceOnline.vm
@@ -0,0 +1 @@
+$device.name online at $event.serverTime
diff --git a/templates/sms/deviceOverspeed.vm b/templates/sms/deviceOverspeed.vm
new file mode 100644
index 000000000..63f967bc3
--- /dev/null
+++ b/templates/sms/deviceOverspeed.vm
@@ -0,0 +1,8 @@
+#if($speedUnits == 'kmh')
+#set($speedString = $position.speed * 1.852 + ' km/h')
+#elseif($speedUnits == 'mph')
+#set($speedString = $position.speed * 1.15078 + ' mph')
+#else
+#set($speedString = "$position.speed kn")
+#end
+$device.name exceeds the speed $speedString at $event.serverTime
diff --git a/templates/sms/deviceStopped.vm b/templates/sms/deviceStopped.vm
new file mode 100644
index 000000000..cf9118cac
--- /dev/null
+++ b/templates/sms/deviceStopped.vm
@@ -0,0 +1 @@
+$device.name stopped at $event.serverTime
diff --git a/templates/sms/deviceUnknown.vm b/templates/sms/deviceUnknown.vm
new file mode 100644
index 000000000..533219799
--- /dev/null
+++ b/templates/sms/deviceUnknown.vm
@@ -0,0 +1 @@
+$device.name status is unknown at $event.serverTime
diff --git a/templates/sms/geofenceEnter.vm b/templates/sms/geofenceEnter.vm
new file mode 100644
index 000000000..5a216f361
--- /dev/null
+++ b/templates/sms/geofenceEnter.vm
@@ -0,0 +1 @@
+$device.name has entered geofence $geofence.name at $event.serverTime
diff --git a/templates/sms/geofenceExit.vm b/templates/sms/geofenceExit.vm
new file mode 100644
index 000000000..fd8a8409d
--- /dev/null
+++ b/templates/sms/geofenceExit.vm
@@ -0,0 +1 @@
+$device.name has exited geofence $geofence.name at $event.serverTime
diff --git a/templates/sms/ignitionOff.vm b/templates/sms/ignitionOff.vm
new file mode 100644
index 000000000..6f90eb552
--- /dev/null
+++ b/templates/sms/ignitionOff.vm
@@ -0,0 +1 @@
+$device.name ignition OFF at $event.serverTime
diff --git a/templates/sms/ignitionOn.vm b/templates/sms/ignitionOn.vm
new file mode 100644
index 000000000..9c0d79888
--- /dev/null
+++ b/templates/sms/ignitionOn.vm
@@ -0,0 +1 @@
+$device.name ignition ON at $event.serverTime
diff --git a/templates/sms/maintenance.vm b/templates/sms/maintenance.vm
new file mode 100644
index 000000000..f2e6940f6
--- /dev/null
+++ b/templates/sms/maintenance.vm
@@ -0,0 +1 @@
+$device.name maintenance is required at $event.serverTime
diff --git a/templates/sms/test.vm b/templates/sms/test.vm
new file mode 100644
index 000000000..0311b6203
--- /dev/null
+++ b/templates/sms/test.vm
@@ -0,0 +1 @@
+Traccar test message
diff --git a/templates/sms/unknown.vm b/templates/sms/unknown.vm
new file mode 100644
index 000000000..fd20b50cc
--- /dev/null
+++ b/templates/sms/unknown.vm
@@ -0,0 +1 @@
+Unknown type
diff --git a/test/org/traccar/geocoder/GeocoderTest.java b/test/org/traccar/geocoder/GeocoderTest.java
index a5aecc8cf..6f4fc1710 100644
--- a/test/org/traccar/geocoder/GeocoderTest.java
+++ b/test/org/traccar/geocoder/GeocoderTest.java
@@ -94,7 +94,7 @@ public class GeocoderTest {
}
public void testGeocodeFarm() throws InterruptedException {
- Geocoder geocoder = new GeocodeFarmGeocoder(null, 0);
+ Geocoder geocoder = new GeocodeFarmGeocoder(null, null, 0);
geocoder.getAddress(new AddressFormat(), 34.116302, -118.051519, new Geocoder.ReverseGeocoderCallback() {
@Override
diff --git a/test/org/traccar/protocol/KhdProtocolDecoderTest.java b/test/org/traccar/protocol/KhdProtocolDecoderTest.java
index c58600bed..e85695be8 100644
--- a/test/org/traccar/protocol/KhdProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/KhdProtocolDecoderTest.java
@@ -14,6 +14,9 @@ public class KhdProtocolDecoderTest extends ProtocolTest {
"2929b1000605162935b80d"));
verifyPosition(decoder, binary(
+ "2929a3002e1780c663170216203353003060811013839500000114f8000000ffff5000000a00000000000000060102003db70d"));
+
+ verifyPosition(decoder, binary(
"292980002805162935140108074727801129670365336900000103ffff000082fc0000001e78091b000000360d"));
verifyPosition(decoder, binary(
diff --git a/test/org/traccar/protocol/PricolProtocolDecoderTest.java b/test/org/traccar/protocol/PricolProtocolDecoderTest.java
index 3826d56c4..0c792a8ec 100644
--- a/test/org/traccar/protocol/PricolProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/PricolProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class PricolProtocolDecoderTest extends ProtocolTest {
PricolProtocolDecoder decoder = new PricolProtocolDecoder(new PricolProtocol());
+ verifyPosition(decoder, binary(
+ "3c5052493030303350020000011402110b222b0455152e4e001de819ca450000000000000003820249000000000000000000000000000000000000000040003e"));
+
verifyNotNull(decoder, binary(
"3c544553303030324b02000000000000000000000000000000000000000000000000000000037c01f4000000000000000000000000000000000000000000003e"));
diff --git a/test/org/traccar/protocol/Tk103ProtocolDecoderTest.java b/test/org/traccar/protocol/Tk103ProtocolDecoderTest.java
index 3f4e12940..e6ca3f218 100644
--- a/test/org/traccar/protocol/Tk103ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Tk103ProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class Tk103ProtocolDecoderTest extends ProtocolTest {
Tk103ProtocolDecoder decoder = new Tk103ProtocolDecoder(new Tk103Protocol());
verifyPosition(decoder, text(
+ "(087073803649BR00170221A6142.0334N02712.2197E000.3203149000.00,00000000L00000000)"));
+
+ verifyPosition(decoder, text(
"(864768010869060,DW30,050117,A,5135.82713N,00001.17918E,0.089,154745,000.0,43.40,12)"));
verifyNotNull(decoder, text(
diff --git a/test/org/traccar/protocol/TmgProtocolDecoderTest.java b/test/org/traccar/protocol/TmgProtocolDecoderTest.java
index cbfe82abc..2fac25dab 100644
--- a/test/org/traccar/protocol/TmgProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/TmgProtocolDecoderTest.java
@@ -11,6 +11,12 @@ public class TmgProtocolDecoderTest extends ProtocolTest {
TmgProtocolDecoder decoder = new TmgProtocolDecoder(new TmgProtocol());
verifyPosition(decoder, text(
+ "$nor,L,868325023006341,14022017,103947,1,2836.6542,N,07706.2504,E,0.0,0.0,0.0,0.0,0,22,VODAFONE - DELH,15,49B7,1,2.57,13.2,00000010,00000000,0111,00.0,00.0,0.0,SW10.12,NA,#"));
+
+ verifyPosition(decoder, text(
+ "$rid,L,868325023006341,14022017,103706,1,2836.6542,N,07706.2504,E,0.0,0.0,0.0,0.0,0,22,VODAFONE - DELH,15,49B7,1,2.57,13.2,00000011,00000000,0111,00.0,00.0,0.0,SW10.12,0004909463,#"));
+
+ verifyPosition(decoder, text(
"$ion,H,868324023777431,27012017,101057,4,2830.2952,N,07705.2532,E,0.0,202.38,225.9,1.22,8,20,N.A,0,N.A,1,4.09,00.0,00000111,00000000,1101,00.0-00.0,00.0-0.0,4.42,01.02,#"));
verifyPosition(decoder, text(
@@ -32,7 +38,7 @@ public class TmgProtocolDecoderTest extends ProtocolTest {
"$nor,L,868324023777431,17012017,001523,4,2830.2939,N,07705.2527,E,0.0,50.96,236.5,1.05,11,21,IDEA CELLULAR L,18,DCDE,0,4.09,12.8,00000111,00000000,1111,00.0-00.0,00.0-0.0,3.59,01.02,#"));
verifyPosition(decoder, text(
- "$nor,L,869309999985699,24062015,094459,4,2826.1956,N,07659.7690,E,67.5,2.5,167,0.82,15,22,airtel,31,4441,1,4.1,12.7,00000011,00000011,1111,0.0,0.0, 21.3,SW00.01,#"));
+ "$nor,L,869309999985699,24062015,094459,4,2826.1956,N,07659.7690,E,67.5,2.5,167,0.82,15,22,airtel,31,4441,1,4.1,12.7,00000011,00000011,1111,0.0,0.0,21.3,SW00.01,#"));
}