aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pom.xml5
-rw-r--r--schema/changelog-3.11.xml10
-rw-r--r--setup/default.xml9
-rw-r--r--src/org/traccar/BasePipelineFactory.java6
-rw-r--r--src/org/traccar/BaseProtocolDecoder.java1
-rw-r--r--src/org/traccar/api/resource/ReportResource.java8
-rw-r--r--src/org/traccar/database/PermissionsManager.java28
-rw-r--r--src/org/traccar/helper/DateUtil.java6
-rw-r--r--src/org/traccar/model/Position.java2
-rw-r--r--src/org/traccar/model/Server.java11
-rw-r--r--src/org/traccar/model/User.java10
-rw-r--r--src/org/traccar/notification/NotificationFormatter.java5
-rw-r--r--src/org/traccar/protocol/AplicomProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/GalileoProtocolDecoder.java234
-rw-r--r--src/org/traccar/protocol/H02ProtocolDecoder.java26
-rw-r--r--src/org/traccar/protocol/StarLinkProtocolDecoder.java159
-rw-r--r--src/org/traccar/protocol/SuntechProtocolDecoder.java16
-rw-r--r--src/org/traccar/protocol/UlbotechProtocolDecoder.java62
-rw-r--r--src/org/traccar/protocol/Xt2400Protocol.java41
-rw-r--r--src/org/traccar/protocol/Xt2400ProtocolDecoder.java187
-rw-r--r--src/org/traccar/reports/Events.java29
-rw-r--r--src/org/traccar/reports/ReportUtils.java56
-rw-r--r--src/org/traccar/reports/Route.java29
-rw-r--r--src/org/traccar/reports/Summary.java11
-rw-r--r--src/org/traccar/reports/Trips.java28
-rw-r--r--templates/export/events.xlsxbin12346 -> 12336 bytes
-rw-r--r--templates/export/route.xlsxbin13042 -> 13039 bytes
-rw-r--r--templates/export/summary.xlsxbin12315 -> 12350 bytes
-rw-r--r--templates/export/trips.xlsxbin13197 -> 13189 bytes
-rw-r--r--templates/mail/alarm.vm2
-rw-r--r--templates/mail/commandResult.vm2
-rw-r--r--templates/mail/deviceMoving.vm2
-rw-r--r--templates/mail/deviceOffline.vm2
-rw-r--r--templates/mail/deviceOnline.vm2
-rw-r--r--templates/mail/deviceOverspeed.vm2
-rw-r--r--templates/mail/deviceStopped.vm2
-rw-r--r--templates/mail/deviceUnknown.vm2
-rw-r--r--templates/mail/geofenceEnter.vm2
-rw-r--r--templates/mail/geofenceExit.vm2
-rw-r--r--templates/mail/ignitionOff.vm2
-rw-r--r--templates/mail/ignitionOn.vm2
-rw-r--r--templates/mail/maintenance.vm2
-rw-r--r--templates/sms/alarm.vm2
-rw-r--r--templates/sms/commandResult.vm2
-rw-r--r--templates/sms/deviceMoving.vm2
-rw-r--r--templates/sms/deviceOffline.vm2
-rw-r--r--templates/sms/deviceOnline.vm2
-rw-r--r--templates/sms/deviceOverspeed.vm2
-rw-r--r--templates/sms/deviceStopped.vm2
-rw-r--r--templates/sms/deviceUnknown.vm2
-rw-r--r--templates/sms/geofenceEnter.vm2
-rw-r--r--templates/sms/geofenceExit.vm2
-rw-r--r--templates/sms/ignitionOff.vm2
-rw-r--r--templates/sms/ignitionOn.vm2
-rw-r--r--templates/sms/maintenance.vm2
-rw-r--r--test/org/traccar/protocol/AplicomProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/H02ProtocolDecoderTest.java12
-rw-r--r--test/org/traccar/protocol/StarLinkProtocolDecoderTest.java20
-rw-r--r--test/org/traccar/protocol/SuntechProtocolDecoderTest.java9
-rw-r--r--test/org/traccar/protocol/UlbotechProtocolDecoderTest.java4
-rw-r--r--test/org/traccar/protocol/Xt2400ProtocolDecoderTest.java20
61 files changed, 815 insertions, 286 deletions
diff --git a/pom.xml b/pom.xml
index 952a24795..8700c6ff1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -144,6 +144,11 @@
<version>1.7</version>
</dependency>
<dependency>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity-tools</artifactId>
+ <version>2.0</version>
+ </dependency>
+ <dependency>
<groupId>org.mnode.ical4j</groupId>
<artifactId>ical4j</artifactId>
<version>2.0.0</version>
diff --git a/schema/changelog-3.11.xml b/schema/changelog-3.11.xml
index c169e1146..1fdaa3f17 100644
--- a/schema/changelog-3.11.xml
+++ b/schema/changelog-3.11.xml
@@ -11,7 +11,7 @@
<addColumn tableName="users">
<column name="phone" type="VARCHAR(128)" />
</addColumn>
-
+
<addColumn tableName="notifications">
<column name="sms" type="BOOLEAN" defaultValueBoolean="false" />
</addColumn>
@@ -20,6 +20,14 @@
<column name="devicereadonly" type="BOOLEAN" defaultValueBoolean="false" />
</addColumn>
+ <addColumn tableName="server">
+ <column name="timezone" type="VARCHAR(128)" />
+ </addColumn>
+
+ <addColumn tableName="users">
+ <column name="timezone" type="VARCHAR(128)" />
+ </addColumn>
+
</changeSet>
</databaseChangeLog>
diff --git a/setup/default.xml b/setup/default.xml
index 9fe49edbe..e251fbeee 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -51,6 +51,9 @@
longitude = :longitude,
zoom = :zoom,
twelveHourFormat = :twelveHourFormat,
+ coordinateFormat = :coordinateFormat,
+ forceSettings = :forceSettings,
+ timezone = :timezone,
attributes = :attributes
WHERE id = :id
</entry>
@@ -65,8 +68,8 @@
</entry>
<entry key='database.insertUser'>
- 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)
+ INSERT INTO users (name, email, phone, hashedPassword, salt, readonly, admin, map, distanceUnit, speedUnit, latitude, longitude, zoom, twelveHourFormat, coordinateFormat, disabled, expirationTime, deviceLimit, userLimit, deviceReadonly, token, timezone, attributes)
+ VALUES (:name, :email, :phone, :hashedPassword, :salt, :readonly, :admin, :map, :distanceUnit, :speedUnit, :latitude, :longitude, :zoom, :twelveHourFormat, :coordinateFormat, :disabled, :expirationTime, :deviceLimit, :userLimit, :deviceReadonly, :token, :timezone, :attributes)
</entry>
<entry key='database.updateUser'>
@@ -90,6 +93,7 @@
userLimit = :userLimit,
deviceReadonly = :deviceReadonly,
token = :token,
+ timezone = :timezone,
attributes = :attributes
WHERE id = :id
</entry>
@@ -508,5 +512,6 @@
<entry key='siwi.port'>5135</entry>
<entry key='starlink.port'>5136</entry>
<entry key='dmt.port'>5137</entry>
+ <entry key='xt2400.port'>5138</entry>
</properties>
diff --git a/src/org/traccar/BasePipelineFactory.java b/src/org/traccar/BasePipelineFactory.java
index 74b59a486..775d609a7 100644
--- a/src/org/traccar/BasePipelineFactory.java
+++ b/src/org/traccar/BasePipelineFactory.java
@@ -188,15 +188,15 @@ public abstract class BasePipelineFactory implements ChannelPipelineFactory {
addSpecificHandlers(pipeline);
+ if (geolocationHandler != null) {
+ pipeline.addLast("location", geolocationHandler);
+ }
if (hemisphereHandler != null) {
pipeline.addLast("hemisphere", hemisphereHandler);
}
if (geocoderHandler != null) {
pipeline.addLast("geocoder", geocoderHandler);
}
- if (geolocationHandler != null) {
- pipeline.addLast("location", geolocationHandler);
- }
pipeline.addLast("remoteAddress", new RemoteAddressHandler());
addDynamicHandlers(pipeline);
diff --git a/src/org/traccar/BaseProtocolDecoder.java b/src/org/traccar/BaseProtocolDecoder.java
index e5fa76a47..54d2bf28f 100644
--- a/src/org/traccar/BaseProtocolDecoder.java
+++ b/src/org/traccar/BaseProtocolDecoder.java
@@ -162,6 +162,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
position.setAltitude(last.getAltitude());
position.setSpeed(last.getSpeed());
position.setCourse(last.getCourse());
+ position.setAccuracy(last.getAccuracy());
} else {
position.setFixTime(new Date(0));
}
diff --git a/src/org/traccar/api/resource/ReportResource.java b/src/org/traccar/api/resource/ReportResource.java
index a1b35d64e..a0f686e80 100644
--- a/src/org/traccar/api/resource/ReportResource.java
+++ b/src/org/traccar/api/resource/ReportResource.java
@@ -51,7 +51,7 @@ public class ReportResource extends BaseResource {
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Route.getExcel(stream, getUserId(), deviceIds, groupIds,
- DateUtil.parseDateTime(from), DateUtil.parseDateTime(to));
+ DateUtil.parseDate(from), DateUtil.parseDate(to));
return Response.ok(stream.toByteArray())
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
@@ -76,7 +76,7 @@ public class ReportResource extends BaseResource {
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Events.getExcel(stream, getUserId(), deviceIds, groupIds, types,
- DateUtil.parseDateTime(from), DateUtil.parseDateTime(to));
+ DateUtil.parseDate(from), DateUtil.parseDate(to));
return Response.ok(stream.toByteArray())
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
@@ -99,7 +99,7 @@ public class ReportResource extends BaseResource {
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Summary.getExcel(stream, getUserId(), deviceIds, groupIds,
- DateUtil.parseDateTime(from), DateUtil.parseDateTime(to));
+ DateUtil.parseDate(from), DateUtil.parseDate(to));
return Response.ok(stream.toByteArray())
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
@@ -123,7 +123,7 @@ public class ReportResource extends BaseResource {
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException, IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Trips.getExcel(stream, getUserId(), deviceIds, groupIds,
- DateUtil.parseDateTime(from), DateUtil.parseDateTime(to));
+ DateUtil.parseDate(from), DateUtil.parseDate(to));
return Response.ok(stream.toByteArray())
.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build();
diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java
index 5313859df..e4bd6f5db 100644
--- a/src/org/traccar/database/PermissionsManager.java
+++ b/src/org/traccar/database/PermissionsManager.java
@@ -25,6 +25,7 @@ import org.traccar.model.Server;
import org.traccar.model.User;
import org.traccar.model.UserPermission;
+import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
@@ -395,4 +396,31 @@ public class PermissionsManager {
return users.get(usersTokens.get(token));
}
+ public Object lookupPreference(long userId, String key, Object defaultValue) {
+ String methodName = "get" + key.substring(0, 1).toUpperCase() + key.substring(1);
+ Object preference;
+ Object serverPreference = null;
+ Object userPreference = null;
+ try {
+ Method method = null;
+ method = User.class.getMethod(methodName, (Class<?>[]) null);
+ if (method != null) {
+ userPreference = method.invoke(users.get(userId), (Object[]) null);
+ }
+ method = null;
+ method = Server.class.getMethod(methodName, (Class<?>[]) null);
+ if (method != null) {
+ serverPreference = method.invoke(server, (Object[]) null);
+ }
+ } catch (ReflectiveOperationException | SecurityException | IllegalArgumentException exception) {
+ return defaultValue;
+ }
+ if (server.getForceSettings()) {
+ preference = serverPreference != null ? serverPreference : userPreference;
+ } else {
+ preference = userPreference != null ? userPreference : serverPreference;
+ }
+ return preference != null ? preference : defaultValue;
+ }
+
}
diff --git a/src/org/traccar/helper/DateUtil.java b/src/org/traccar/helper/DateUtil.java
index de36d4420..30bb1b2fa 100644
--- a/src/org/traccar/helper/DateUtil.java
+++ b/src/org/traccar/helper/DateUtil.java
@@ -18,7 +18,6 @@ package org.traccar.helper;
import java.util.Calendar;
import java.util.Date;
-import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
@@ -64,9 +63,4 @@ public final class DateUtil {
public static Date parseDate(String value) {
return DATE_FORMAT.parseDateTime(value).toDate();
}
-
- public static DateTime parseDateTime(String value) {
- return DATE_FORMAT.withOffsetParsed().parseDateTime(value);
- }
-
}
diff --git a/src/org/traccar/model/Position.java b/src/org/traccar/model/Position.java
index aeea83773..1a1db717f 100644
--- a/src/org/traccar/model/Position.java
+++ b/src/org/traccar/model/Position.java
@@ -79,6 +79,8 @@ public class Position extends Message {
public static final String PREFIX_ADC = "adc";
public static final String PREFIX_IO = "io";
public static final String PREFIX_COUNT = "count";
+ public static final String PREFIX_IN = "in";
+ public static final String PREFIX_OUT = "out";
public static final String ALARM_GENERAL = "general";
public static final String ALARM_SOS = "sos";
diff --git a/src/org/traccar/model/Server.java b/src/org/traccar/model/Server.java
index b588a4de0..4ded65204 100644
--- a/src/org/traccar/model/Server.java
+++ b/src/org/traccar/model/Server.java
@@ -15,6 +15,8 @@
*/
package org.traccar.model;
+import java.util.TimeZone;
+
import org.traccar.helper.Log;
public class Server extends Extensible {
@@ -166,4 +168,13 @@ public class Server extends Extensible {
this.coordinateFormat = coordinateFormat;
}
+ private String timezone;
+
+ public void setTimezone(String timezone) {
+ this.timezone = timezone != null ? TimeZone.getTimeZone(timezone).getID() : null;
+ }
+
+ public String getTimezone() {
+ return timezone;
+ }
}
diff --git a/src/org/traccar/model/User.java b/src/org/traccar/model/User.java
index 274f2b2a2..366ced503 100644
--- a/src/org/traccar/model/User.java
+++ b/src/org/traccar/model/User.java
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import org.traccar.helper.Hashing;
import java.util.Date;
+import java.util.TimeZone;
public class User extends Extensible {
@@ -265,4 +266,13 @@ public class User extends Extensible {
return Hashing.validatePassword(password, hashedPassword, salt);
}
+ private String timezone;
+
+ public void setTimezone(String timezone) {
+ this.timezone = timezone != null ? TimeZone.getTimeZone(timezone).getID() : null;
+ }
+
+ public String getTimezone() {
+ return timezone;
+ }
}
diff --git a/src/org/traccar/notification/NotificationFormatter.java b/src/org/traccar/notification/NotificationFormatter.java
index cec238548..eae2681c9 100644
--- a/src/org/traccar/notification/NotificationFormatter.java
+++ b/src/org/traccar/notification/NotificationFormatter.java
@@ -18,10 +18,12 @@ package org.traccar.notification;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
+import java.util.Locale;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.tools.generic.DateTool;
import org.traccar.Context;
import org.traccar.helper.Log;
import org.traccar.model.Device;
@@ -48,6 +50,9 @@ public final class NotificationFormatter {
velocityContext.put("geofence", Context.getGeofenceManager().getGeofence(event.getGeofenceId()));
}
velocityContext.put("webUrl", Context.getVelocityEngine().getProperty("web.url"));
+ velocityContext.put("dateTool", new DateTool());
+ velocityContext.put("timezone", ReportUtils.getTimezone(userId));
+ velocityContext.put("locale", Locale.getDefault());
return velocityContext;
}
diff --git a/src/org/traccar/protocol/AplicomProtocolDecoder.java b/src/org/traccar/protocol/AplicomProtocolDecoder.java
index c07a656b3..03d7dbd6b 100644
--- a/src/org/traccar/protocol/AplicomProtocolDecoder.java
+++ b/src/org/traccar/protocol/AplicomProtocolDecoder.java
@@ -503,8 +503,6 @@ public class AplicomProtocolDecoder extends BaseProtocolDecoder {
getLastLocation(position, null);
- buf.readUnsignedShort(); // event
-
if ((selector & 0x0004) != 0) {
buf.skipBytes(4); // snapshot time
}
diff --git a/src/org/traccar/protocol/GalileoProtocolDecoder.java b/src/org/traccar/protocol/GalileoProtocolDecoder.java
index 64bfdd270..0e8163f8c 100644
--- a/src/org/traccar/protocol/GalileoProtocolDecoder.java
+++ b/src/org/traccar/protocol/GalileoProtocolDecoder.java
@@ -99,6 +99,134 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
}
}
+ private void decodeTag(Position position, ChannelBuffer buf, int tag) {
+ switch (tag) {
+ case 0x01:
+ position.set(Position.KEY_VERSION_HW, buf.readUnsignedByte());
+ break;
+ case 0x02:
+ position.set(Position.KEY_VERSION_FW, buf.readUnsignedByte());
+ break;
+ 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 0x33:
+ position.setSpeed(buf.readUnsignedShort() * 0.0539957);
+ position.setCourse(buf.readUnsignedShort() * 0.1);
+ break;
+ case 0x34:
+ position.setAltitude(buf.readShort());
+ break;
+ case 0x40:
+ position.set(Position.KEY_STATUS, buf.readUnsignedShort());
+ break;
+ case 0x41:
+ position.set(Position.KEY_POWER, buf.readUnsignedShort());
+ break;
+ case 0x42:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
+ break;
+ 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 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ position.set(Position.PREFIX_ADC + (tag - 0x50), buf.readUnsignedShort());
+ break;
+ case 0x58:
+ position.set("rs2320", buf.readUnsignedShort());
+ break;
+ case 0x59:
+ position.set("rs2321", buf.readUnsignedShort());
+ break;
+ case 0xc0:
+ position.set("fuelTotal", buf.readUnsignedInt() * 0.5);
+ break;
+ case 0xc1:
+ position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.4);
+ position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedByte() - 40);
+ position.set(Position.KEY_RPM, buf.readUnsignedShort() * 0.125);
+ break;
+ case 0xc2:
+ position.set("canB0", buf.readUnsignedInt());
+ break;
+ case 0xc3:
+ position.set("canB1", buf.readUnsignedInt());
+ break;
+ case 0xc4:
+ case 0xc5:
+ case 0xc6:
+ case 0xc7:
+ case 0xc8:
+ case 0xc9:
+ case 0xca:
+ case 0xcb:
+ case 0xcc:
+ case 0xcd:
+ case 0xce:
+ case 0xcf:
+ case 0xd0:
+ case 0xd1:
+ case 0xd2:
+ position.set("can8Bit" + (tag - 0xc4), buf.readUnsignedByte());
+ break;
+ case 0xd6:
+ case 0xd7:
+ case 0xd8:
+ case 0xd9:
+ case 0xda:
+ position.set("can16Bit" + (tag - 0xd6), buf.readUnsignedShort());
+ break;
+ case 0xdb:
+ case 0xdc:
+ case 0xdd:
+ case 0xde:
+ case 0xdf:
+ position.set("can32Bit" + (tag - 0xdb), buf.readUnsignedInt());
+ break;
+ case 0xd4:
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
+ break;
+ case 0xe2:
+ case 0xe3:
+ case 0xe4:
+ case 0xe5:
+ case 0xe6:
+ case 0xe7:
+ case 0xe8:
+ case 0xe9:
+ position.set("userData" + (tag - 0xe2), buf.readUnsignedInt());
+ break;
+ case 0xea:
+ position.set("userDataArray", ChannelBuffers.hexDump(buf.readBytes(buf.readUnsignedByte())));
+ break;
+ default:
+ buf.skipBytes(getTagLength(tag));
+ break;
+ }
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -112,6 +240,7 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
Set<Integer> tags = new HashSet<>();
boolean hasLocation = false;
+ DeviceSession deviceSession = null;
Position position = new Position();
while (buf.readerIndex() < length) {
@@ -128,97 +257,29 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
}
tags.add(tag);
- switch (tag) {
- 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 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 0x30:
- hasLocation = true;
- position.setValid((buf.readUnsignedByte() & 0xf0) == 0x00);
- position.setLatitude(buf.readInt() / 1000000.0);
- position.setLongitude(buf.readInt() / 1000000.0);
- break;
- case 0x33:
- position.setSpeed(buf.readUnsignedShort() * 0.0539957);
- position.setCourse(buf.readUnsignedShort() * 0.1);
- break;
- case 0x34:
- position.setAltitude(buf.readShort());
- break;
- case 0x40:
- position.set(Position.KEY_STATUS, buf.readUnsignedShort());
- break;
- case 0x41:
- position.set(Position.KEY_POWER, buf.readUnsignedShort());
- break;
- case 0x42:
- position.set(Position.KEY_BATTERY, buf.readUnsignedShort());
- break;
- 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 0xc1:
- position.set(Position.KEY_FUEL_LEVEL, buf.readUnsignedByte() * 0.4);
- position.set(Position.PREFIX_TEMP + 1, buf.readUnsignedByte() - 40);
- position.set(Position.KEY_RPM, buf.readUnsignedShort() * 0.125);
- break;
- case 0x50:
- position.set(Position.PREFIX_ADC + 0, buf.readUnsignedShort());
- break;
- case 0x51:
- position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort());
- break;
- case 0x52:
- position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort());
- break;
- case 0x53:
- position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShort());
- break;
- 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 (tag == 0x03) {
+ deviceSession = getDeviceSession(
+ channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII));
+ } else if (tag == 0x30) {
+ hasLocation = true;
+ position.setValid((buf.readUnsignedByte() & 0xf0) == 0x00);
+ position.setLatitude(buf.readInt() / 1000000.0);
+ position.setLongitude(buf.readInt() / 1000000.0);
+ } else {
+ decodeTag(position, buf, tag);
}
+
}
+
if (hasLocation && position.getFixTime() != null) {
positions.add(position);
}
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
if (deviceSession == null) {
- return null;
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
}
sendReply(channel, buf.readUnsignedShort());
@@ -228,10 +289,7 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder {
p.setDeviceId(deviceSession.getDeviceId());
}
- if (positions.isEmpty()) {
- return null;
- }
- return positions;
+ return positions.isEmpty() ? null : positions;
}
}
diff --git a/src/org/traccar/protocol/H02ProtocolDecoder.java b/src/org/traccar/protocol/H02ProtocolDecoder.java
index 033ff3ba5..b11a1e0e6 100644
--- a/src/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/org/traccar/protocol/H02ProtocolDecoder.java
@@ -73,6 +73,8 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ALARM, Position.ALARM_SOS);
} else if (!BitUtil.check(status, 2)) {
position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
+ } else if (!BitUtil.check(status, 19)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_POWER_CUT);
}
position.set(Position.KEY_IGNITION, BitUtil.check(status, 10));
@@ -140,6 +142,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.setCourse((buf.readUnsignedByte() & 0x0f) * 100.0 + BcdUtil.readInteger(buf, 2));
processStatus(position, buf.readUnsignedInt());
+
return position;
}
@@ -168,7 +171,18 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.number("(?:(dd)(dd)(dd))?") // date (ddmmyy)
.any()
.number(",(x{8})") // status
- .expression("(?:#|,.*)")
+ .groupBegin()
+ .number(",(d+),") // odometer
+ .number("(-?d+),") // temperature
+ .number("(d+.d+),") // fuel
+ .number("(-?d+),") // altitude
+ .number("(x+),") // lac
+ .number("(x+)#") // cid
+ .or()
+ .expression(",.*")
+ .or()
+ .text("#")
+ .groupEnd()
.compile();
private static final Pattern PATTERN_NBR = new PatternBuilder()
@@ -238,6 +252,16 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
processStatus(position, parser.nextLong(16));
+ if (parser.hasNext(6)) {
+ position.set(Position.KEY_ODOMETER, parser.nextInt());
+ position.set(Position.PREFIX_TEMP + 1, parser.nextInt());
+ position.set(Position.KEY_FUEL_LEVEL, parser.nextDouble());
+
+ position.setAltitude(parser.nextInt());
+
+ position.setNetwork(new Network(CellTower.fromLacCid(parser.nextInt(16), parser.nextInt(16))));
+ }
+
return position;
}
diff --git a/src/org/traccar/protocol/StarLinkProtocolDecoder.java b/src/org/traccar/protocol/StarLinkProtocolDecoder.java
index 46d8ee9cc..eaee07870 100644
--- a/src/org/traccar/protocol/StarLinkProtocolDecoder.java
+++ b/src/org/traccar/protocol/StarLinkProtocolDecoder.java
@@ -17,8 +17,8 @@ package org.traccar.protocol;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
-import org.traccar.helper.DateBuilder;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.CellTower;
@@ -26,12 +26,27 @@ import org.traccar.model.Network;
import org.traccar.model.Position;
import java.net.SocketAddress;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
import java.util.regex.Pattern;
public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
+ private String[] dataTags;
+ private DateFormat dateFormat;
+
public StarLinkProtocolDecoder(StarLinkProtocol protocol) {
super(protocol);
+
+ String format = Context.getConfig().getString(
+ getProtocolName() + ".format", "#EDT#,#EID#,#PDT#,#LAT#,#LONG#,#SPD#,#HEAD#,#ODO#,"
+ + "#IN1#,#IN2#,#IN3#,#IN4#,#OUT1#,#OUT2#,#OUT3#,#OUT4#,#LAC#,#CID#,#VIN#,#VBAT#,#DEST#,#IGN#,#ENG#");
+ dataTags = format.split(",");
+
+ dateFormat = new SimpleDateFormat(
+ Context.getConfig().getString(getProtocolName() + ".dateFormat", "yyMMddHHmmss"));
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
private static final Pattern PATTERN = new PatternBuilder()
@@ -40,23 +55,20 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
.number("(x{6}|d{15}),") // id
.number("(d+),") // type
.number("(d+),") // index
- .number("(dd)(dd)(dd)") // event date
- .number("(dd)(dd)(dd),") // event time
- .number("(d+),") // event
- .number("(dd)(dd)(dd)") // fix date
- .number("(dd)(dd)(dd),") // fix time
- .number("([-+])(dd)(dd.d+),") // latitude
- .number("([-+])(ddd)(dd.d+),") // longitude
- .number("(d+.d+),") // speed
- .number("(d+),") // course
- .number("(d+),") // odometer
- .number("(d+),") // lac
- .number("(d+),") // cid
- .number("(d+.d+),") // power
- .number("(d+.d+)") // battery
- .any()
+ .expression("(.+)") // data
+ .text("*")
+ .number("xx") // checksum
.compile();
+ public static final int MSG_EVENT_REPORT = 6;
+
+ private double parseCoordinate(String value) {
+ int minutesIndex = value.indexOf('.') - 2;
+ double result = Double.parseDouble(value.substring(1, minutesIndex));
+ result += Double.parseDouble(value.substring(minutesIndex)) / 60;
+ return value.charAt(0) == '+' ? result : -result;
+ }
+
@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
@@ -71,38 +83,105 @@ public class StarLinkProtocolDecoder extends BaseProtocolDecoder {
return null;
}
+ int type = parser.nextInt();
+ if (type != MSG_EVENT_REPORT) {
+ return null;
+ }
+
Position position = new Position();
position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
- position.set(Position.KEY_TYPE, parser.nextInt());
position.set(Position.KEY_INDEX, parser.nextInt());
- DateBuilder dateBuilder = new DateBuilder()
- .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
- .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
- position.setDeviceTime(dateBuilder.getDate());
-
- position.set(Position.KEY_EVENT, parser.nextInt());
-
- dateBuilder = new DateBuilder()
- .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt())
- .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt());
- position.setFixTime(dateBuilder.getDate());
-
- position.setValid(true);
- position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN));
- position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN));
-
- position.setSpeed(parser.nextDouble());
- position.setCourse(parser.nextInt());
-
- position.set(Position.KEY_ODOMETER, parser.nextInt());
+ String[] data = parser.next().split(",");
+ Integer lac = null, cid = null;
+ int event = 0;
+
+ for (int i = 0; i < Math.min(data.length, dataTags.length); i++) {
+ switch (dataTags[i]) {
+ case "#EDT#":
+ position.setDeviceTime(dateFormat.parse(data[i]));
+ break;
+ case "#EID#":
+ event = Integer.parseInt(data[i]);
+ position.set(Position.KEY_EVENT, event);
+ break;
+ case "#PDT#":
+ position.setFixTime(dateFormat.parse(data[i]));
+ break;
+ case "#LAT#":
+ position.setLatitude(parseCoordinate(data[i]));
+ break;
+ case "#LONG#":
+ position.setLongitude(parseCoordinate(data[i]));
+ break;
+ case "#SPD#":
+ position.setSpeed(Double.parseDouble(data[i]));
+ break;
+ case "#HEAD#":
+ position.setCourse(Integer.parseInt(data[i]));
+ break;
+ case "#ODO#":
+ position.set(Position.KEY_ODOMETER, Integer.parseInt(data[i]));
+ break;
+ case "#IN1#":
+ position.set(Position.PREFIX_IN + 1, Integer.parseInt(data[i]));
+ break;
+ case "#IN2#":
+ position.set(Position.PREFIX_IN + 2, Integer.parseInt(data[i]));
+ break;
+ case "#IN3#":
+ position.set(Position.PREFIX_IN + 3, Integer.parseInt(data[i]));
+ break;
+ case "#IN4#":
+ position.set(Position.PREFIX_IN + 4, Integer.parseInt(data[i]));
+ break;
+ case "#OUT1#":
+ position.set(Position.PREFIX_OUT + 1, Integer.parseInt(data[i]));
+ break;
+ case "#OUT2#":
+ position.set(Position.PREFIX_OUT + 2, Integer.parseInt(data[i]));
+ break;
+ case "#OUT3#":
+ position.set(Position.PREFIX_OUT + 3, Integer.parseInt(data[i]));
+ break;
+ case "#OUT4#":
+ position.set(Position.PREFIX_OUT + 4, Integer.parseInt(data[i]));
+ break;
+ case "#LAC#":
+ lac = Integer.parseInt(data[i]);
+ break;
+ case "#CID#":
+ cid = Integer.parseInt(data[i]);
+ break;
+ case "#VIN#":
+ position.set(Position.KEY_POWER, Double.parseDouble(data[i]));
+ break;
+ case "#VBAT#":
+ position.set(Position.KEY_BATTERY, Double.parseDouble(data[i]));
+ break;
+ case "#DEST#":
+ position.set("destination", data[i]);
+ break;
+ case "#IGN#":
+ position.set(Position.KEY_IGNITION, data[i].equals("1"));
+ break;
+ case "#ENG#":
+ position.set("engine", data[i].equals("1"));
+ break;
+ default:
+ break;
+ }
+ }
- position.setNetwork(new Network(CellTower.fromLacCid(parser.nextInt(), parser.nextInt())));
+ if (lac != null && cid != null) {
+ position.setNetwork(new Network(CellTower.fromLacCid(lac, cid)));
+ }
- position.set(Position.KEY_POWER, parser.nextDouble());
- position.set(Position.KEY_BATTERY, parser.nextDouble());
+ if (event == 20) {
+ position.set(Position.KEY_RFID, data[data.length - 1]);
+ }
return position;
}
diff --git a/src/org/traccar/protocol/SuntechProtocolDecoder.java b/src/org/traccar/protocol/SuntechProtocolDecoder.java
index 55d5c224a..e19335cb0 100644
--- a/src/org/traccar/protocol/SuntechProtocolDecoder.java
+++ b/src/org/traccar/protocol/SuntechProtocolDecoder.java
@@ -31,11 +31,13 @@ import java.util.TimeZone;
public class SuntechProtocolDecoder extends BaseProtocolDecoder {
private int protocolType;
+ private boolean hbm;
public SuntechProtocolDecoder(SuntechProtocol protocol) {
super(protocol);
protocolType = Context.getConfig().getInteger(getProtocolName() + ".protocolType");
+ hbm = Context.getConfig().getBoolean(getProtocolName() + ".hbm");
}
public void setProtocolType(int protocolType) {
@@ -144,12 +146,16 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_INDEX, Integer.parseInt(values[index++]));
}
- if (index < values.length) {
- position.set(Position.KEY_HOURS, Integer.parseInt(values[index++]));
- }
+ if (hbm) {
+
+ if (index < values.length) {
+ position.set(Position.KEY_HOURS, Integer.parseInt(values[index++]));
+ }
+
+ if (index < values.length) {
+ position.set(Position.KEY_BATTERY, Double.parseDouble(values[index]));
+ }
- if (index < values.length) {
- position.set(Position.KEY_BATTERY, Double.parseDouble(values[index]));
}
return position;
diff --git a/src/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/org/traccar/protocol/UlbotechProtocolDecoder.java
index 19e9cd5e9..97912e128 100644
--- a/src/org/traccar/protocol/UlbotechProtocolDecoder.java
+++ b/src/org/traccar/protocol/UlbotechProtocolDecoder.java
@@ -22,7 +22,10 @@ import org.traccar.BaseProtocolDecoder;
import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.helper.BitUtil;
+import org.traccar.helper.DateBuilder;
import org.traccar.helper.ObdDecoder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
import org.traccar.helper.UnitsConverter;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
@@ -31,6 +34,7 @@ import org.traccar.model.Position;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Date;
+import java.util.regex.Pattern;
public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
@@ -161,27 +165,58 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
}
}
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+ private static final Pattern PATTERN = new PatternBuilder()
+ .text("*TS")
+ .number("dd,") // protocol version
+ .number("(d{15}),") // device id
+ .number("(dd)(dd)(dd)") // time
+ .number("(dd)(dd)(dd),") // date
+ .expression("([^#]+)") // command
+ .text("#")
+ .compile();
- ChannelBuffer buf = (ChannelBuffer) msg;
+ private Object decodeText(Channel channel, SocketAddress remoteAddress, String sentence) {
- if (buf.readUnsignedByte() != 0xF8) {
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
return null;
}
- buf.readUnsignedByte(); // version
- buf.readUnsignedByte(); // type
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ 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())
+ .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+
+ getLastLocation(position, dateBuilder.getDate());
+
+ position.set(Position.KEY_RESULT, parser.next());
+
+ return position;
+ }
+
+ private Object decodeBinary(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+
+ buf.readUnsignedByte(); // header
+ buf.readUnsignedByte(); // version
+ buf.readUnsignedByte(); // type
String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
+
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
return null;
}
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
long seconds = buf.readUnsignedInt() & 0x7fffffffL;
@@ -298,4 +333,17 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ if (buf.getUnsignedByte(buf.readerIndex()) == 0xF8) {
+ return decodeBinary(channel, remoteAddress, buf);
+ } else {
+ return decodeText(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII));
+ }
+ }
+
}
diff --git a/src/org/traccar/protocol/Xt2400Protocol.java b/src/org/traccar/protocol/Xt2400Protocol.java
new file mode 100644
index 000000000..0c5e9cd4c
--- /dev/null
+++ b/src/org/traccar/protocol/Xt2400Protocol.java
@@ -0,0 +1,41 @@
+/*
+ * 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.ConnectionlessBootstrap;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.traccar.BaseProtocol;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class Xt2400Protocol extends BaseProtocol {
+
+ public Xt2400Protocol() {
+ super("xt2400");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("objectDecoder", new Xt2400ProtocolDecoder(Xt2400Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/Xt2400ProtocolDecoder.java b/src/org/traccar/protocol/Xt2400ProtocolDecoder.java
new file mode 100644
index 000000000..a42c6175f
--- /dev/null
+++ b/src/org/traccar/protocol/Xt2400ProtocolDecoder.java
@@ -0,0 +1,187 @@
+/*
+ * 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.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
+import org.traccar.DeviceSession;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import javax.xml.bind.DatatypeConverter;
+import java.net.SocketAddress;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Xt2400ProtocolDecoder extends BaseProtocolDecoder {
+
+ public Xt2400ProtocolDecoder(Xt2400Protocol protocol) {
+ super(protocol);
+
+ String config = Context.getConfig().getString(getProtocolName() + ".config");
+ if (config != null) {
+ setConfig(config);
+ }
+ }
+
+ private static final Map<Integer, Integer> TAG_LENGTH_MAP = new HashMap<>();
+
+ static {
+ int[] l1 = {
+ 0x01, 0x02, 0x04, 0x0b, 0x0c, 0x0d, 0x12, 0x13,
+ 0x16, 0x17, 0x1c, 0x1f, 0x23, 0x2c, 0x2d, 0x30,
+ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x40, 0x41,
+ 0x53, 0x66, 0x69, 0x6a, 0x93, 0x94, 0x96
+ };
+ int[] l2 = {
+ 0x05, 0x09, 0x0a, 0x14, 0x15, 0x1d, 0x1e, 0x24,
+ 0x26, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x57, 0x58, 0x59, 0x5a, 0x6b, 0x6f, 0x7A,
+ 0x7B, 0x7C, 0x7d, 0x7E, 0x7F, 0x80, 0x81, 0x82,
+ 0x83, 0x84, 0x85, 0x86
+ };
+ int[] l4 = {
+ 0x03, 0x06, 0x07, 0x08, 0x0e, 0x0f, 0x10, 0x11,
+ 0x18, 0x19, 0x1a, 0x1b, 0x20, 0x21, 0x22, 0x2e,
+ 0x2f, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0x54, 0x55, 0x56, 0x5b, 0x5c, 0x5d,
+ 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x68, 0x6e, 0x71,
+ 0x72, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d
+ };
+ for (int i : l1) {
+ TAG_LENGTH_MAP.put(i, 1);
+ }
+ for (int i : l2) {
+ TAG_LENGTH_MAP.put(i, 2);
+ }
+ for (int i : l4) {
+ TAG_LENGTH_MAP.put(i, 4);
+ }
+ TAG_LENGTH_MAP.put(0x65, 17);
+ TAG_LENGTH_MAP.put(0x73, 16);
+ TAG_LENGTH_MAP.put(0x95, 24);
+ }
+
+ private static int getTagLength(int tag) {
+ Integer length = TAG_LENGTH_MAP.get(tag);
+ if (length == null) {
+ throw new IllegalArgumentException("Unknown tag: " + tag);
+ }
+ return length;
+ }
+
+ private Map<Short, byte[]> formats = new HashMap<>();
+
+ public void setConfig(String configString) {
+ Pattern pattern = Pattern.compile(":wycfg pcr\\[\\d+\\] ([0-9a-fA-F]{2})[0-9a-fA-F]{2}([0-9a-fA-F]+)");
+ Matcher matcher = pattern.matcher(configString);
+ while (matcher.find()) {
+ formats.put(Short.parseShort(matcher.group(1), 16), DatatypeConverter.parseHexBinary(matcher.group(2)));
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ byte[] format = null;
+ if (formats.size() > 1) {
+ format = formats.get(buf.getUnsignedByte(buf.readerIndex()));
+ } else if (!formats.isEmpty()) {
+ format = formats.values().iterator().next();
+ }
+
+ if (format == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+
+ for (byte tag : format) {
+ switch ((int) tag) {
+ case 0x03:
+ DeviceSession deviceSession = getDeviceSession(
+ channel, remoteAddress, String.valueOf(buf.readUnsignedInt()));
+ if (deviceSession == null) {
+ return null;
+ }
+ position.setDeviceId(deviceSession.getDeviceId());
+ break;
+ case 0x04:
+ position.set(Position.KEY_EVENT, buf.readUnsignedByte());
+ break;
+ case 0x05:
+ position.set(Position.KEY_INDEX, buf.readUnsignedShort());
+ break;
+ case 0x06:
+ position.setTime(new Date(buf.readUnsignedInt() * 1000));
+ break;
+ case 0x07:
+ position.setLatitude(buf.readInt() * 0.000001);
+ break;
+ case 0x08:
+ position.setLongitude(buf.readInt() * 0.000001);
+ break;
+ case 0x09:
+ position.setAltitude(buf.readShort() * 0.1);
+ break;
+ case 0x0a:
+ position.setCourse(buf.readShort() * 0.1);
+ break;
+ case 0x0b:
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte()));
+ break;
+ case 0x10:
+ position.set(Position.KEY_ODOMETER_TRIP, buf.readUnsignedInt());
+ break;
+ case 0x12:
+ position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1);
+ break;
+ case 0x13:
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ break;
+ case 0x16:
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.1);
+ break;
+ case 0x17:
+ position.set(Position.KEY_POWER, buf.readUnsignedByte() * 0.1);
+ break;
+ default:
+ buf.skipBytes(getTagLength(tag));
+ break;
+ }
+ }
+
+ if (position.getLatitude() != 0 && position.getLongitude() != 0) {
+ position.setValid(true);
+ } else {
+ getLastLocation(position, position.getDeviceTime());
+ }
+
+ return position;
+ }
+
+}
diff --git a/src/org/traccar/reports/Events.java b/src/org/traccar/reports/Events.java
index d2255684f..0706f1382 100644
--- a/src/org/traccar/reports/Events.java
+++ b/src/org/traccar/reports/Events.java
@@ -26,17 +26,8 @@ import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
import org.apache.poi.ss.util.WorkbookUtil;
-import org.joda.time.DateTime;
-import org.jxls.area.Area;
-import org.jxls.builder.xls.XlsCommentAreaBuilder;
-import org.jxls.common.CellRef;
-import org.jxls.formula.StandardFormulaProcessor;
-import org.jxls.transform.Transformer;
-import org.jxls.transform.poi.PoiTransformer;
-import org.jxls.util.TransformerFactory;
import org.traccar.Context;
import org.traccar.model.Device;
import org.traccar.model.Event;
@@ -70,13 +61,13 @@ public final class Events {
public static void getExcel(OutputStream outputStream,
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- Collection<String> types, DateTime from, DateTime to) throws SQLException, IOException {
+ Collection<String> types, Date from, Date to) throws SQLException, IOException {
ArrayList<DeviceReport> devicesEvents = new ArrayList<>();
ArrayList<String> sheetNames = new ArrayList<>();
HashMap<Long, String> geofenceNames = new HashMap<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
Context.getPermissionsManager().checkDevice(userId, deviceId);
- Collection<Event> events = Context.getDataManager().getEvents(deviceId, from.toDate(), to.toDate());
+ Collection<Event> events = Context.getDataManager().getEvents(deviceId, from, to);
boolean all = types.isEmpty() || types.contains(Event.ALL_EVENTS);
for (Iterator<Event> iterator = events.iterator(); iterator.hasNext();) {
Event event = iterator.next();
@@ -112,25 +103,13 @@ public final class Events {
String templatePath = Context.getConfig().getString("report.templatesPath",
"templates/export/");
try (InputStream inputStream = new FileInputStream(templatePath + "/events.xlsx")) {
- org.jxls.common.Context jxlsContext = PoiTransformer.createInitialContext();
+ org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
jxlsContext.putVar("devices", devicesEvents);
jxlsContext.putVar("sheetNames", sheetNames);
jxlsContext.putVar("geofenceNames", geofenceNames);
jxlsContext.putVar("from", from);
jxlsContext.putVar("to", to);
- jxlsContext.putVar("distanceUnit", ReportUtils.getDistanceUnit(userId));
- jxlsContext.putVar("speedUnit", ReportUtils.getSpeedUnit(userId));
- jxlsContext.putVar("timezone", from.getZone());
- jxlsContext.putVar("bracketsRegex", "[\\{\\}\"]");
- Transformer transformer = TransformerFactory.createTransformer(inputStream, outputStream);
- List<Area> xlsAreas = new XlsCommentAreaBuilder(transformer).build();
- for (Area xlsArea : xlsAreas) {
- xlsArea.applyAt(new CellRef(xlsArea.getStartCellRef().getCellName()), jxlsContext);
- xlsArea.setFormulaProcessor(new StandardFormulaProcessor());
- xlsArea.processFormulas();
- }
- transformer.deleteSheet(xlsAreas.get(0).getStartCellRef().getSheetName());
- transformer.write();
+ ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
}
}
}
diff --git a/src/org/traccar/reports/ReportUtils.java b/src/org/traccar/reports/ReportUtils.java
index 1402e10d4..e2a743b2f 100644
--- a/src/org/traccar/reports/ReportUtils.java
+++ b/src/org/traccar/reports/ReportUtils.java
@@ -16,14 +16,28 @@
*/
package org.traccar.reports;
+import org.apache.velocity.tools.generic.DateTool;
+import org.jxls.area.Area;
+import org.jxls.builder.xls.XlsCommentAreaBuilder;
+import org.jxls.common.CellRef;
+import org.jxls.formula.StandardFormulaProcessor;
+import org.jxls.transform.Transformer;
+import org.jxls.transform.poi.PoiTransformer;
+import org.jxls.util.TransformerFactory;
import org.traccar.Context;
import org.traccar.helper.Log;
import org.traccar.model.Position;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
public final class ReportUtils {
@@ -31,19 +45,16 @@ public final class ReportUtils {
}
public static String getDistanceUnit(long userId) {
- String unit = Context.getPermissionsManager().getUser(userId).getDistanceUnit();
- if (unit == null) {
- unit = Context.getPermissionsManager().getServer().getDistanceUnit();
- }
- return unit != null ? unit : "km";
+ return (String) Context.getPermissionsManager().lookupPreference(userId, "distanceUnit", "km");
}
public static String getSpeedUnit(long userId) {
- String unit = Context.getPermissionsManager().getUser(userId).getSpeedUnit();
- if (unit == null) {
- unit = Context.getPermissionsManager().getServer().getSpeedUnit();
- }
- return unit != null ? unit : "kn";
+ return (String) Context.getPermissionsManager().lookupPreference(userId, "speedUnit", "kn");
+ }
+
+ public static TimeZone getTimezone(long userId) {
+ String timezone = (String) Context.getPermissionsManager().lookupPreference(userId, "timezone", null);
+ return timezone != null ? TimeZone.getTimeZone(timezone) : TimeZone.getDefault();
}
public static Collection<Long> getDeviceList(Collection<Long> deviceIds, Collection<Long> groupIds) {
@@ -98,4 +109,29 @@ public final class ReportUtils {
return "-";
}
+ public static org.jxls.common.Context initializeContext(long userId) {
+ org.jxls.common.Context jxlsContext = PoiTransformer.createInitialContext();
+ jxlsContext.putVar("distanceUnit", getDistanceUnit(userId));
+ jxlsContext.putVar("speedUnit", getSpeedUnit(userId));
+ jxlsContext.putVar("webUrl", Context.getVelocityEngine().getProperty("web.url"));
+ jxlsContext.putVar("dateTool", new DateTool());
+ jxlsContext.putVar("timezone", getTimezone(userId));
+ jxlsContext.putVar("locale", Locale.getDefault());
+ jxlsContext.putVar("bracketsRegex", "[\\{\\}\"]");
+ return jxlsContext;
+ }
+
+ public static void processTemplateWithSheets(InputStream templateStream, OutputStream targetStream,
+ org.jxls.common.Context jxlsContext) throws IOException {
+ Transformer transformer = TransformerFactory.createTransformer(templateStream, targetStream);
+ List<Area> xlsAreas = new XlsCommentAreaBuilder(transformer).build();
+ for (Area xlsArea : xlsAreas) {
+ xlsArea.applyAt(new CellRef(xlsArea.getStartCellRef().getCellName()), jxlsContext);
+ xlsArea.setFormulaProcessor(new StandardFormulaProcessor());
+ xlsArea.processFormulas();
+ }
+ transformer.deleteSheet(xlsAreas.get(0).getStartCellRef().getSheetName());
+ transformer.write();
+ }
+
}
diff --git a/src/org/traccar/reports/Route.java b/src/org/traccar/reports/Route.java
index c1acaf322..aa6b7105b 100644
--- a/src/org/traccar/reports/Route.java
+++ b/src/org/traccar/reports/Route.java
@@ -24,17 +24,8 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
-import java.util.List;
import org.apache.poi.ss.util.WorkbookUtil;
-import org.joda.time.DateTime;
-import org.jxls.area.Area;
-import org.jxls.builder.xls.XlsCommentAreaBuilder;
-import org.jxls.common.CellRef;
-import org.jxls.formula.StandardFormulaProcessor;
-import org.jxls.transform.Transformer;
-import org.jxls.transform.poi.PoiTransformer;
-import org.jxls.util.TransformerFactory;
import org.traccar.Context;
import org.traccar.model.Device;
import org.traccar.model.Group;
@@ -58,13 +49,13 @@ public final class Route {
public static void getExcel(OutputStream outputStream,
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- DateTime from, DateTime to) throws SQLException, IOException {
+ Date from, Date to) throws SQLException, IOException {
ArrayList<DeviceReport> devicesRoutes = new ArrayList<>();
ArrayList<String> sheetNames = new ArrayList<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
Context.getPermissionsManager().checkDevice(userId, deviceId);
Collection<Position> positions = Context.getDataManager()
- .getPositions(deviceId, from.toDate(), to.toDate());
+ .getPositions(deviceId, from, to);
DeviceReport deviceRoutes = new DeviceReport();
Device device = Context.getIdentityManager().getDeviceById(deviceId);
deviceRoutes.setDeviceName(device.getName());
@@ -81,24 +72,12 @@ public final class Route {
String templatePath = Context.getConfig().getString("report.templatesPath",
"templates/export/");
try (InputStream inputStream = new FileInputStream(templatePath + "/route.xlsx")) {
- org.jxls.common.Context jxlsContext = PoiTransformer.createInitialContext();
+ org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
jxlsContext.putVar("devices", devicesRoutes);
jxlsContext.putVar("sheetNames", sheetNames);
jxlsContext.putVar("from", from);
jxlsContext.putVar("to", to);
- jxlsContext.putVar("distanceUnit", ReportUtils.getDistanceUnit(userId));
- jxlsContext.putVar("speedUnit", ReportUtils.getSpeedUnit(userId));
- jxlsContext.putVar("timezone", from.getZone());
- jxlsContext.putVar("bracketsRegex", "[\\{\\}\"]");
- Transformer transformer = TransformerFactory.createTransformer(inputStream, outputStream);
- List<Area> xlsAreas = new XlsCommentAreaBuilder(transformer).build();
- for (Area xlsArea : xlsAreas) {
- xlsArea.applyAt(new CellRef(xlsArea.getStartCellRef().getCellName()), jxlsContext);
- xlsArea.setFormulaProcessor(new StandardFormulaProcessor());
- xlsArea.processFormulas();
- }
- transformer.deleteSheet(xlsAreas.get(0).getStartCellRef().getSheetName());
- transformer.write();
+ ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
}
}
}
diff --git a/src/org/traccar/reports/Summary.java b/src/org/traccar/reports/Summary.java
index cacb4d1d6..abe0277da 100644
--- a/src/org/traccar/reports/Summary.java
+++ b/src/org/traccar/reports/Summary.java
@@ -25,8 +25,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
-import org.joda.time.DateTime;
-import org.jxls.transform.poi.PoiTransformer;
import org.jxls.util.JxlsHelper;
import org.traccar.Context;
import org.traccar.model.Position;
@@ -79,18 +77,15 @@ public final class Summary {
public static void getExcel(OutputStream outputStream,
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- DateTime from, DateTime to) throws SQLException, IOException {
- Collection<SummaryReport> summaries = getObjects(userId, deviceIds, groupIds, from.toDate(), to.toDate());
+ Date from, Date to) throws SQLException, IOException {
+ Collection<SummaryReport> summaries = getObjects(userId, deviceIds, groupIds, from, to);
String templatePath = Context.getConfig().getString("report.templatesPath",
"templates/export/");
try (InputStream inputStream = new FileInputStream(templatePath + "/summary.xlsx")) {
- org.jxls.common.Context jxlsContext = PoiTransformer.createInitialContext();
+ org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
jxlsContext.putVar("summaries", summaries);
jxlsContext.putVar("from", from);
jxlsContext.putVar("to", to);
- jxlsContext.putVar("distanceUnit", ReportUtils.getDistanceUnit(userId));
- jxlsContext.putVar("speedUnit", ReportUtils.getSpeedUnit(userId));
- jxlsContext.putVar("timezone", from.getZone());
JxlsHelper.getInstance().setUseFastFormulaProcessor(false)
.processTemplate(inputStream, outputStream, jxlsContext);
}
diff --git a/src/org/traccar/reports/Trips.java b/src/org/traccar/reports/Trips.java
index 45b52b63c..eeb2f2681 100644
--- a/src/org/traccar/reports/Trips.java
+++ b/src/org/traccar/reports/Trips.java
@@ -24,17 +24,8 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
-import java.util.List;
import org.apache.poi.ss.util.WorkbookUtil;
-import org.joda.time.DateTime;
-import org.jxls.area.Area;
-import org.jxls.builder.xls.XlsCommentAreaBuilder;
-import org.jxls.common.CellRef;
-import org.jxls.formula.StandardFormulaProcessor;
-import org.jxls.transform.Transformer;
-import org.jxls.transform.poi.PoiTransformer;
-import org.jxls.util.TransformerFactory;
import org.traccar.Context;
import org.traccar.model.Device;
import org.traccar.model.Group;
@@ -193,12 +184,12 @@ public final class Trips {
public static void getExcel(OutputStream outputStream,
long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
- DateTime from, DateTime to) throws SQLException, IOException {
+ Date from, Date to) throws SQLException, IOException {
ArrayList<DeviceReport> devicesTrips = new ArrayList<>();
ArrayList<String> sheetNames = new ArrayList<>();
for (long deviceId: ReportUtils.getDeviceList(deviceIds, groupIds)) {
Context.getPermissionsManager().checkDevice(userId, deviceId);
- Collection<TripReport> trips = detectTrips(deviceId, from.toDate(), to.toDate());
+ Collection<TripReport> trips = detectTrips(deviceId, from, to);
DeviceReport deviceTrips = new DeviceReport();
Device device = Context.getIdentityManager().getDeviceById(deviceId);
deviceTrips.setDeviceName(device.getName());
@@ -215,23 +206,12 @@ public final class Trips {
String templatePath = Context.getConfig().getString("report.templatesPath",
"templates/export/");
try (InputStream inputStream = new FileInputStream(templatePath + "/trips.xlsx")) {
- org.jxls.common.Context jxlsContext = PoiTransformer.createInitialContext();
+ org.jxls.common.Context jxlsContext = ReportUtils.initializeContext(userId);
jxlsContext.putVar("devices", devicesTrips);
jxlsContext.putVar("sheetNames", sheetNames);
jxlsContext.putVar("from", from);
jxlsContext.putVar("to", to);
- jxlsContext.putVar("distanceUnit", ReportUtils.getDistanceUnit(userId));
- jxlsContext.putVar("speedUnit", ReportUtils.getSpeedUnit(userId));
- jxlsContext.putVar("timezone", from.getZone());
- Transformer transformer = TransformerFactory.createTransformer(inputStream, outputStream);
- List<Area> xlsAreas = new XlsCommentAreaBuilder(transformer).build();
- for (Area xlsArea : xlsAreas) {
- xlsArea.applyAt(new CellRef(xlsArea.getStartCellRef().getCellName()), jxlsContext);
- xlsArea.setFormulaProcessor(new StandardFormulaProcessor());
- xlsArea.processFormulas();
- }
- transformer.deleteSheet(xlsAreas.get(0).getStartCellRef().getSheetName());
- transformer.write();
+ ReportUtils.processTemplateWithSheets(inputStream, outputStream, jxlsContext);
}
}
diff --git a/templates/export/events.xlsx b/templates/export/events.xlsx
index 80a54fa57..f3151d11c 100644
--- a/templates/export/events.xlsx
+++ b/templates/export/events.xlsx
Binary files differ
diff --git a/templates/export/route.xlsx b/templates/export/route.xlsx
index 79a2649b7..52c1f8582 100644
--- a/templates/export/route.xlsx
+++ b/templates/export/route.xlsx
Binary files differ
diff --git a/templates/export/summary.xlsx b/templates/export/summary.xlsx
index 53539ed8b..db32d077a 100644
--- a/templates/export/summary.xlsx
+++ b/templates/export/summary.xlsx
Binary files differ
diff --git a/templates/export/trips.xlsx b/templates/export/trips.xlsx
index 20130f44a..0e0ab4494 100644
--- a/templates/export/trips.xlsx
+++ b/templates/export/trips.xlsx
Binary files differ
diff --git a/templates/mail/alarm.vm b/templates/mail/alarm.vm
index b64b2126a..8f1164291 100644
--- a/templates/mail/alarm.vm
+++ b/templates/mail/alarm.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Alarm: $position.getString("alarm")<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude&deg;, $position.longitude&deg;#{end}</a><br>
</body>
</html>
diff --git a/templates/mail/commandResult.vm b/templates/mail/commandResult.vm
index 4c330584d..5b6d8ef3e 100644
--- a/templates/mail/commandResult.vm
+++ b/templates/mail/commandResult.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Result: $position.getString("result")<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a>
</body>
</html>
diff --git a/templates/mail/deviceMoving.vm b/templates/mail/deviceMoving.vm
index 9ad2d8bdc..6e98af1de 100644
--- a/templates/mail/deviceMoving.vm
+++ b/templates/mail/deviceMoving.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Moving<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude&deg;, $position.longitude&deg;#{end}</a><br>
</body>
</html>
diff --git a/templates/mail/deviceOffline.vm b/templates/mail/deviceOffline.vm
index ee7e96f05..1f6d02fb2 100644
--- a/templates/mail/deviceOffline.vm
+++ b/templates/mail/deviceOffline.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Offline<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a>
</body>
</html>
diff --git a/templates/mail/deviceOnline.vm b/templates/mail/deviceOnline.vm
index 379b5cc80..9211eff11 100644
--- a/templates/mail/deviceOnline.vm
+++ b/templates/mail/deviceOnline.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Online<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a>
</body>
</html>
diff --git a/templates/mail/deviceOverspeed.vm b/templates/mail/deviceOverspeed.vm
index a8a58a181..3b203ddcd 100644
--- a/templates/mail/deviceOverspeed.vm
+++ b/templates/mail/deviceOverspeed.vm
@@ -11,7 +11,7 @@
<body>
Device: $device.name<br>
Exceeds the speed: $speedString<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude&deg;, $position.longitude&deg;#{end}</a><br>
</body>
</html>
diff --git a/templates/mail/deviceStopped.vm b/templates/mail/deviceStopped.vm
index 273e1c988..2ec0f8db9 100644
--- a/templates/mail/deviceStopped.vm
+++ b/templates/mail/deviceStopped.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Stopped<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude&deg;, $position.longitude&deg;#{end}</a><br>
</body>
</html>
diff --git a/templates/mail/deviceUnknown.vm b/templates/mail/deviceUnknown.vm
index 40b8fbfa7..34fa01a8a 100644
--- a/templates/mail/deviceUnknown.vm
+++ b/templates/mail/deviceUnknown.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Status is unknown<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Link: <a href="$webUrl?eventId=$event.id">$webUrl?eventId=$event.id</a>
</body>
</html>
diff --git a/templates/mail/geofenceEnter.vm b/templates/mail/geofenceEnter.vm
index 75d16617f..e83a0de62 100644
--- a/templates/mail/geofenceEnter.vm
+++ b/templates/mail/geofenceEnter.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Has entered geofence: $geofence.name<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude&deg;, $position.longitude&deg;#{end}</a><br>
</body>
</html>
diff --git a/templates/mail/geofenceExit.vm b/templates/mail/geofenceExit.vm
index 6d4d1639d..3557f6eb2 100644
--- a/templates/mail/geofenceExit.vm
+++ b/templates/mail/geofenceExit.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Has exited geofence: $geofence.name<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude&deg;, $position.longitude&deg;#{end}</a><br>
</body>
</html>
diff --git a/templates/mail/ignitionOff.vm b/templates/mail/ignitionOff.vm
index 3a3212b12..0281eef02 100644
--- a/templates/mail/ignitionOff.vm
+++ b/templates/mail/ignitionOff.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Ignition OFF<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude&deg;, $position.longitude&deg;#{end}</a><br>
</body>
</html>
diff --git a/templates/mail/ignitionOn.vm b/templates/mail/ignitionOn.vm
index bbe6c40f6..27135a7f0 100644
--- a/templates/mail/ignitionOn.vm
+++ b/templates/mail/ignitionOn.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Ignition ON<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude&deg;, $position.longitude&deg;#{end}</a><br>
</body>
</html>
diff --git a/templates/mail/maintenance.vm b/templates/mail/maintenance.vm
index c94c35cc4..7f69b6c0d 100644
--- a/templates/mail/maintenance.vm
+++ b/templates/mail/maintenance.vm
@@ -4,7 +4,7 @@
<body>
Device: $device.name<br>
Maintenance is required<br>
-Time: $event.serverTime<br>
+Time: $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)<br>
Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude&deg;, $position.longitude&deg;#{end}</a><br>
</body>
</html>
diff --git a/templates/sms/alarm.vm b/templates/sms/alarm.vm
index e35a573b3..389341cf1 100644
--- a/templates/sms/alarm.vm
+++ b/templates/sms/alarm.vm
@@ -1 +1 @@
-$device.name alarm: $position.getString("alarm") at $event.serverTime
+$device.name alarm: $position.getString("alarm") at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/commandResult.vm b/templates/sms/commandResult.vm
index 38fddcf25..4a327d850 100644
--- a/templates/sms/commandResult.vm
+++ b/templates/sms/commandResult.vm
@@ -1 +1 @@
-$device.name command result received: $position.getString("result") at $event.serverTime
+$device.name command result received: $position.getString("result") at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/deviceMoving.vm b/templates/sms/deviceMoving.vm
index febae6331..aaa72ab0d 100644
--- a/templates/sms/deviceMoving.vm
+++ b/templates/sms/deviceMoving.vm
@@ -1 +1 @@
-$device.name moving at $event.serverTime
+$device.name moving at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/deviceOffline.vm b/templates/sms/deviceOffline.vm
index 490502946..8c4a02335 100644
--- a/templates/sms/deviceOffline.vm
+++ b/templates/sms/deviceOffline.vm
@@ -1 +1 @@
-$device.name offline at $event.serverTime
+$device.name offline at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/deviceOnline.vm b/templates/sms/deviceOnline.vm
index e8bc0a5bf..62854feb0 100644
--- a/templates/sms/deviceOnline.vm
+++ b/templates/sms/deviceOnline.vm
@@ -1 +1 @@
-$device.name online at $event.serverTime
+$device.name online at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/deviceOverspeed.vm b/templates/sms/deviceOverspeed.vm
index 63f967bc3..3c9eae628 100644
--- a/templates/sms/deviceOverspeed.vm
+++ b/templates/sms/deviceOverspeed.vm
@@ -5,4 +5,4 @@
#else
#set($speedString = "$position.speed kn")
#end
-$device.name exceeds the speed $speedString at $event.serverTime
+$device.name exceeds the speed $speedString at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/deviceStopped.vm b/templates/sms/deviceStopped.vm
index cf9118cac..c2437b5f6 100644
--- a/templates/sms/deviceStopped.vm
+++ b/templates/sms/deviceStopped.vm
@@ -1 +1 @@
-$device.name stopped at $event.serverTime
+$device.name stopped at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/deviceUnknown.vm b/templates/sms/deviceUnknown.vm
index 533219799..07f39f3a9 100644
--- a/templates/sms/deviceUnknown.vm
+++ b/templates/sms/deviceUnknown.vm
@@ -1 +1 @@
-$device.name status is unknown at $event.serverTime
+$device.name status is unknown at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/geofenceEnter.vm b/templates/sms/geofenceEnter.vm
index 5a216f361..9dd861931 100644
--- a/templates/sms/geofenceEnter.vm
+++ b/templates/sms/geofenceEnter.vm
@@ -1 +1 @@
-$device.name has entered geofence $geofence.name at $event.serverTime
+$device.name has entered geofence $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/geofenceExit.vm b/templates/sms/geofenceExit.vm
index fd8a8409d..8553458f7 100644
--- a/templates/sms/geofenceExit.vm
+++ b/templates/sms/geofenceExit.vm
@@ -1 +1 @@
-$device.name has exited geofence $geofence.name at $event.serverTime
+$device.name has exited geofence $geofence.name at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/ignitionOff.vm b/templates/sms/ignitionOff.vm
index 6f90eb552..ddf860413 100644
--- a/templates/sms/ignitionOff.vm
+++ b/templates/sms/ignitionOff.vm
@@ -1 +1 @@
-$device.name ignition OFF at $event.serverTime
+$device.name ignition OFF at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/ignitionOn.vm b/templates/sms/ignitionOn.vm
index 9c0d79888..00b365be9 100644
--- a/templates/sms/ignitionOn.vm
+++ b/templates/sms/ignitionOn.vm
@@ -1 +1 @@
-$device.name ignition ON at $event.serverTime
+$device.name ignition ON at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/templates/sms/maintenance.vm b/templates/sms/maintenance.vm
index f2e6940f6..554b74400 100644
--- a/templates/sms/maintenance.vm
+++ b/templates/sms/maintenance.vm
@@ -1 +1 @@
-$device.name maintenance is required at $event.serverTime
+$device.name maintenance is required at $dateTool.format("YYYY-MM-dd HH:mm:ss", $event.serverTime, $locale, $timezone)
diff --git a/test/org/traccar/protocol/AplicomProtocolDecoderTest.java b/test/org/traccar/protocol/AplicomProtocolDecoderTest.java
index 38ac1c43f..70e4e3e36 100644
--- a/test/org/traccar/protocol/AplicomProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/AplicomProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class AplicomProtocolDecoderTest extends ProtocolTest {
AplicomProtocolDecoder decoder = new AplicomProtocolDecoder(new AplicomProtocol());
verifyAttributes(decoder, binary(
+ "46c30144f667c1711f00340007ff750058b8f77701037c06b8000000330033000000000b760000425e0100640000b3a90185d5823155000131070204000219641004"));
+
+ verifyAttributes(decoder, binary(
"46c30144f667c1711f00340007ff75005891601401025707b50236003b003b003500000a9300006bd50100640000a5250167d2f9034c01010107020400021a901004"));
verifyAttributes(decoder, binary(
diff --git a/test/org/traccar/protocol/H02ProtocolDecoderTest.java b/test/org/traccar/protocol/H02ProtocolDecoderTest.java
index 90a1470c6..ad3a2e589 100644
--- a/test/org/traccar/protocol/H02ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/H02ProtocolDecoderTest.java
@@ -10,6 +10,18 @@ public class H02ProtocolDecoderTest extends ProtocolTest {
H02ProtocolDecoder decoder = new H02ProtocolDecoder(new H02Protocol());
+ verifyPosition(decoder, binary(
+ "2421305109380127171003170520046500100286297e003085ffffdfffff03440069129344006400001151415a20"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,2130510938,V1,012632,A,0520.0663,N,10028.6324,E,0.286,023,100317,FFFFDFFF,69129336,0,100.0,18,5141,5A20#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4210209006,V1,201812,A,2608.9437,N,08016.2521,W,000.80,000,150317,EFE7F9FF,310,260,0,0,6#"));
+
+ verifyPosition(decoder, buffer(
+ "*HQ,4210209006,V1,201844,A,2608.9437,N,08016.2521,W,000.80,000,150317,FFFFF9FF,310,260,0,0,6#"));
+
verifyPosition(decoder, buffer(
"*HQ,4109179024,V19,103732,V,3853.2770,S,06205.8678,W,000.00,000,100217,,5492932630888,8954314165044716555?,FFFFFBFF#"));
diff --git a/test/org/traccar/protocol/StarLinkProtocolDecoderTest.java b/test/org/traccar/protocol/StarLinkProtocolDecoderTest.java
index 33ee49812..d102ba426 100644
--- a/test/org/traccar/protocol/StarLinkProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/StarLinkProtocolDecoderTest.java
@@ -10,17 +10,23 @@ public class StarLinkProtocolDecoderTest extends ProtocolTest {
StarLinkProtocolDecoder decoder = new StarLinkProtocolDecoder(new StarLinkProtocol());
- verifyNothing(decoder, text(
- "$SLU005F20,06,22743,170116091944,01,170116091944,+3206.0991,+03452.0605,003.6,008,064675,1,1,0,0,0,0,0,0,10424,2521,14.156,01.163,,1,1,1,4*BE"));
+ verifyPosition(decoder, text(
+ "$SLU0330D5,06,3556,170314063523,19,170314061634,+3211.7187,+03452.8106,000.0,332,015074,1,1,0,0,0,0,0,0,10443,32722,12.870,03.790,,0,0*FC"));
+
+ verifyPosition(decoder, text(
+ "$SLU0330D5,06,3555,170314063453,20,170314061634,+3211.7187,+03452.8106,000.0,332,015074,1,1,0,0,0,0,0,0,10443,32722,12.838,03.790,,0,0,1,,1122*74"));
- verifyNothing(decoder, text(
- "$SLU005F20,06,22718,170116091422,01,170116091422,+3205.1777,+03450.7595,046.8,359,064671,1,1,0,0,0,0,0,0,10424,64072,14.148,01.161,,1,1,1,4*03"));
+ verifyPosition(decoder, text(
+ "$SLU006968,06,375153,170117051824,01,170117051823,+3203.2073,+03448.1360,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,12.655,04.085,,0,0,0,99*45"));
- verifyNothing(decoder, text(
- "$SLU005F20,06,22695,170116090730,24,170116090730,+3203.6062,+03449.6945,013.9,181,064666,1,1,0,0,0,0,0,0,10422,30631,14.089,01.163,,1,1*43"));
+ verifyPosition(decoder, text(
+ "$SLU006968,06,375155,170117052615,24,170117052613,+3203.2079,+03448.1369,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,14.290,04.083,,1,1*5B"));
+
+ verifyPosition(decoder, text(
+ "$SLU006968,06,375156,170117052616,34,170117052614,+3203.2079,+03448.1369,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,14.277,04.084,1,1,1,1*F3"));
verifyPosition(decoder, text(
- "$SLU0004D2,06,32,071106135931,01,071106135930,+3159.4376,+03445.3298,021.3,087,000554,31071,11704,13.45,3.87*3E"));
+ "$SLU006968,06,375154,170117052613,04,170117052612,+3203.2079,+03448.1369,000.0,300,085725,1,1,0,0,0,0,0,0,10422,36201,14.287,04.084,,1,0*5B"));
}
diff --git a/test/org/traccar/protocol/SuntechProtocolDecoderTest.java b/test/org/traccar/protocol/SuntechProtocolDecoderTest.java
index 0adc0f2b7..316118882 100644
--- a/test/org/traccar/protocol/SuntechProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/SuntechProtocolDecoderTest.java
@@ -11,6 +11,15 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(new SuntechProtocol());
verifyPosition(decoder, text(
+ "ST910;Location;205576803;500;20170312;12:56:52;-22.846014;-046.322176;000.000;000.00;0;3.8;0;0;0019"));
+
+ verifyPosition(decoder, text(
+ "ST300STT;100850000;01;010;20081017;07:41:56;00100;+37.478519;+126.886819;000.012;000.00;9;1;0;15.30;001100;1;0072;0;4.5;1;1750;012497F1160000;1;004f001454;450;00;-320;20;255;1"));
+
+ verifyPosition(decoder, text(
+ "ST300STT;205589913;05;527;20170304;02:21:33;be139;-25.398868;-049.325636;000.476;000.00;6;1;427;12.57;100000010;1;0172;017.159;0;002.327;12;4.0"));
+
+ verifyPosition(decoder, text(
"SA200STT;638947;803;20170117;07:40:44;5d309;-01.287213;-047.917462;000.035;000.00;10;1;2036194;12.57;000000;1;0376;010360;4.2;1"));
verifyPosition(decoder, text(
diff --git a/test/org/traccar/protocol/UlbotechProtocolDecoderTest.java b/test/org/traccar/protocol/UlbotechProtocolDecoderTest.java
index 9a70080ae..c76912f18 100644
--- a/test/org/traccar/protocol/UlbotechProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/UlbotechProtocolDecoderTest.java
@@ -10,8 +10,8 @@ public class UlbotechProtocolDecoderTest extends ProtocolTest {
UlbotechProtocolDecoder decoder = new UlbotechProtocolDecoder(new UlbotechProtocol());
- verifyPosition(decoder, binary(
- "f8010108675210214191372010e042010e0292dcd601a999c9000c00900269030400420000040400d87d6e0506034f1754220b070d31056d410c0000310d00312f4a1005f000000200947df8"));
+ verifyAttributes(decoder, buffer(
+ "*TS01,868323025245751,134955140317,WFE:0#"));
verifyPosition(decoder, binary(
"f8010103515810532780699f7e2e3f010e015ee4c906bde45c00000000008b0304004000000404002c776005060373193622110b00240b00fee8ffff807dffff606d0b00fee9af000000af0000000b00feee7d78807dffffffff100101cc2af8"));
diff --git a/test/org/traccar/protocol/Xt2400ProtocolDecoderTest.java b/test/org/traccar/protocol/Xt2400ProtocolDecoderTest.java
new file mode 100644
index 000000000..3cc0c22ec
--- /dev/null
+++ b/test/org/traccar/protocol/Xt2400ProtocolDecoderTest.java
@@ -0,0 +1,20 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class Xt2400ProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ Xt2400ProtocolDecoder decoder = new Xt2400ProtocolDecoder(new Xt2400Protocol());
+
+ decoder.setConfig("\n:wycfg pcr[0] 000f01030406070809570a131217141005\n");
+
+ verifyPosition(decoder, binary(
+ "0009c4fb9b0b58a771e4020742d9f8f1c4c300bc0000000011077c0015000000000001"));
+
+ }
+
+}