aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pom.xml6
-rw-r--r--src/org/traccar/Context.java16
-rw-r--r--src/org/traccar/notification/MailMessage.java36
-rw-r--r--src/org/traccar/notification/NotificationFormatter.java227
-rw-r--r--src/org/traccar/notification/NotificationMail.java10
-rw-r--r--src/org/traccar/protocol/At2000Protocol.java7
-rw-r--r--src/org/traccar/protocol/At2000ProtocolDecoder.java6
-rw-r--r--src/org/traccar/protocol/GranitProtocolDecoder.java25
-rw-r--r--src/org/traccar/protocol/XexunProtocol.java9
-rw-r--r--src/org/traccar/protocol/XexunProtocolEncoder.java42
-rw-r--r--templates/mail/alarm.vm10
-rw-r--r--templates/mail/commandResult.vm9
-rw-r--r--templates/mail/deviceMoving.vm10
-rw-r--r--templates/mail/deviceOffline.vm9
-rw-r--r--templates/mail/deviceOnline.vm9
-rw-r--r--templates/mail/deviceOverspeed.vm17
-rw-r--r--templates/mail/deviceStopped.vm10
-rw-r--r--templates/mail/deviceUnknown.vm9
-rw-r--r--templates/mail/geofenceEnter.vm10
-rw-r--r--templates/mail/geofenceExit.vm10
-rw-r--r--templates/mail/ignitionOff.vm10
-rw-r--r--templates/mail/ignitionOn.vm10
-rw-r--r--templates/mail/maintenance.vm10
-rw-r--r--templates/mail/unknown.vm7
-rw-r--r--test/org/traccar/ProtocolTest.java4
-rw-r--r--test/org/traccar/protocol/At2000ProtocolDecoderTest.java11
-rw-r--r--test/org/traccar/protocol/GranitProtocolDecoderTest.java18
-rw-r--r--test/org/traccar/protocol/SuntechProtocolDecoderTest.java3
28 files changed, 333 insertions, 227 deletions
diff --git a/pom.xml b/pom.xml
index f71fe0c1a..ef44d4786 100644
--- a/pom.xml
+++ b/pom.xml
@@ -138,7 +138,11 @@
<artifactId>jxls-poi</artifactId>
<version>1.0.11</version>
</dependency>
-
+ <dependency>
+ <groupId>org.apache.velocity</groupId>
+ <artifactId>velocity</artifactId>
+ <version>1.7</version>
+ </dependency>
</dependencies>
<build>
diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java
index 581f00082..ef515ad2f 100644
--- a/src/org/traccar/Context.java
+++ b/src/org/traccar/Context.java
@@ -17,6 +17,9 @@ package org.traccar;
import com.ning.http.client.AsyncHttpClient;
+import java.util.Properties;
+
+import org.apache.velocity.app.VelocityEngine;
import org.traccar.database.AliasesManager;
import org.traccar.database.ConnectionManager;
import org.traccar.database.DataManager;
@@ -125,6 +128,12 @@ public final class Context {
return notificationManager;
}
+ private static VelocityEngine velocityEngine;
+
+ public static VelocityEngine getVelocityEngine() {
+ return velocityEngine;
+ }
+
private static final AsyncHttpClient ASYNC_HTTP_CLIENT = new AsyncHttpClient();
public static AsyncHttpClient getAsyncHttpClient() {
@@ -242,6 +251,13 @@ public final class Context {
if (config.getBoolean("event.enable")) {
notificationManager = new NotificationManager(dataManager);
+ Properties velocityProperties = new Properties();
+ velocityProperties.setProperty("file.resource.loader.path",
+ Context.getConfig().getString("mail.templatesPath", "templates/mail") + "/");
+ velocityProperties.setProperty("runtime.log.logsystem.class",
+ "org.apache.velocity.runtime.log.NullLogChute");
+ velocityEngine = new VelocityEngine();
+ velocityEngine.init(velocityProperties);
}
serverManager = new ServerManager();
diff --git a/src/org/traccar/notification/MailMessage.java b/src/org/traccar/notification/MailMessage.java
new file mode 100644
index 000000000..0fce43740
--- /dev/null
+++ b/src/org/traccar/notification/MailMessage.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 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;
+
+public class MailMessage {
+
+ private String subject;
+ private String body;
+
+ public MailMessage(String subject, String body) {
+ this.subject = subject;
+ this.body = body;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public String getBody() {
+ return body;
+ }
+}
diff --git a/src/org/traccar/notification/NotificationFormatter.java b/src/org/traccar/notification/NotificationFormatter.java
index 6753873ed..2d580922c 100644
--- a/src/org/traccar/notification/NotificationFormatter.java
+++ b/src/org/traccar/notification/NotificationFormatter.java
@@ -15,223 +15,48 @@
*/
package org.traccar.notification;
-import java.text.DecimalFormat;
-import java.util.Formatter;
-import java.util.Locale;
+import java.io.StringWriter;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.exception.ResourceNotFoundException;
import org.traccar.Context;
-import org.traccar.helper.UnitsConverter;
+import org.traccar.helper.Log;
import org.traccar.model.Device;
import org.traccar.model.Event;
import org.traccar.model.Position;
+import org.traccar.reports.ReportUtils;
public final class NotificationFormatter {
private NotificationFormatter() {
}
- public static final String TITLE_TEMPLATE_TYPE_COMMAND_RESULT = "%1$s: command result received";
- public static final String MESSAGE_TEMPLATE_TYPE_COMMAND_RESULT = "Device: %1$s%n"
- + "Result: %3$s%n"
- + "Time: %2$tc%n";
-
- public static final String TITLE_TEMPLATE_TYPE_DEVICE_ONLINE = "%1$s: online";
- public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_ONLINE = "Device: %1$s%n"
- + "Online%n"
- + "Time: %2$tc%n";
- public static final String TITLE_TEMPLATE_TYPE_DEVICE_UNKNOWN = "%1$s: status is unknown";
- public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_UNKNOWN = "Device: %1$s%n"
- + "Status is unknown%n"
- + "Time: %2$tc%n";
- public static final String TITLE_TEMPLATE_TYPE_DEVICE_OFFLINE = "%1$s: offline";
- public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_OFFLINE = "Device: %1$s%n"
- + "Offline%n"
- + "Time: %2$tc%n";
-
- public static final String TITLE_TEMPLATE_TYPE_DEVICE_MOVING = "%1$s: moving";
- public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_MOVING = "Device: %1$s%n"
- + "Moving%n"
- + "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
- + "Time: %2$tc%n";
- public static final String TITLE_TEMPLATE_TYPE_DEVICE_STOPPED = "%1$s: stopped";
- public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_STOPPED = "Device: %1$s%n"
- + "Stopped%n"
- + "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
- + "Time: %2$tc%n";
-
- public static final String TITLE_TEMPLATE_TYPE_DEVICE_OVERSPEED = "%1$s: exceeds the speed";
- public static final String MESSAGE_TEMPLATE_TYPE_DEVICE_OVERSPEED = "Device: %1$s%n"
- + "Exceeds the speed: %5$s%n"
- + "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
- + "Time: %2$tc%n";
-
- public static final String TITLE_TEMPLATE_TYPE_GEOFENCE_ENTER = "%1$s: has entered geofence";
- public static final String MESSAGE_TEMPLATE_TYPE_GEOFENCE_ENTER = "Device: %1$s%n"
- + "Has entered geofence: %5$s%n"
- + "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
- + "Time: %2$tc%n";
- public static final String TITLE_TEMPLATE_TYPE_GEOFENCE_EXIT = "%1$s: has exited geofence";
- public static final String MESSAGE_TEMPLATE_TYPE_GEOFENCE_EXIT = "Device: %1$s%n"
- + "Has exited geofence: %5$s%n"
- + "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
- + "Time: %2$tc%n";
-
- public static final String TITLE_TEMPLATE_TYPE_ALARM = "%1$s: alarm!";
- public static final String MESSAGE_TEMPLATE_TYPE_ALARM = "Device: %1$s%n"
- + "Alarm: %5$s%n"
- + "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
- + "Time: %2$tc%n";
-
- public static final String TITLE_TEMPLATE_TYPE_IGNITION_ON = "%1$s: ignition ON";
- public static final String MESSAGE_TEMPLATE_TYPE_IGNITION_ON = "Device: %1$s%n"
- + "Ignition ON%n"
- + "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
- + "Time: %2$tc%n";
- public static final String TITLE_TEMPLATE_TYPE_IGNITION_OFF = "%1$s: ignition OFF";
- public static final String MESSAGE_TEMPLATE_TYPE_IGNITION_OFF = "Device: %1$s%n"
- + "Ignition OFF%n"
- + "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
- + "Time: %2$tc%n";
- public static final String TITLE_TEMPLATE_TYPE_MAINTENANCE = "%1$s: maintenance is required";
- public static final String MESSAGE_TEMPLATE_TYPE_MAINTENANCE = "Device: %1$s%n"
- + "Maintenance is required%n"
- + "Point: https://www.openstreetmap.org/?mlat=%3$f&mlon=%4$f#map=16/%3$f/%4$f%n"
- + "Time: %2$tc%n";
-
- public static String formatTitle(long userId, Event event, Position position) {
+ public static MailMessage formatMessage(long userId, Event event, Position position) {
Device device = Context.getIdentityManager().getDeviceById(event.getDeviceId());
- StringBuilder stringBuilder = new StringBuilder();
- Formatter formatter = new Formatter(stringBuilder, Locale.getDefault());
- switch (event.getType()) {
- case Event.TYPE_COMMAND_RESULT:
- formatter.format(TITLE_TEMPLATE_TYPE_COMMAND_RESULT, device.getName());
- break;
- case Event.TYPE_DEVICE_ONLINE:
- formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_ONLINE, device.getName());
- break;
- case Event.TYPE_DEVICE_UNKNOWN:
- formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_UNKNOWN, device.getName());
- break;
- case Event.TYPE_DEVICE_OFFLINE:
- formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_OFFLINE, device.getName());
- break;
- case Event.TYPE_DEVICE_MOVING:
- formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_MOVING, device.getName());
- break;
- case Event.TYPE_DEVICE_STOPPED:
- formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_STOPPED, device.getName());
- break;
- case Event.TYPE_DEVICE_OVERSPEED:
- formatter.format(TITLE_TEMPLATE_TYPE_DEVICE_OVERSPEED, device.getName());
- break;
- case Event.TYPE_GEOFENCE_ENTER:
- formatter.format(TITLE_TEMPLATE_TYPE_GEOFENCE_ENTER, device.getName());
- break;
- case Event.TYPE_GEOFENCE_EXIT:
- formatter.format(TITLE_TEMPLATE_TYPE_GEOFENCE_EXIT, device.getName());
- break;
- case Event.TYPE_ALARM:
- formatter.format(TITLE_TEMPLATE_TYPE_ALARM, device.getName());
- break;
- case Event.TYPE_IGNITION_ON:
- formatter.format(TITLE_TEMPLATE_TYPE_IGNITION_ON, device.getName());
- break;
- case Event.TYPE_IGNITION_OFF:
- formatter.format(TITLE_TEMPLATE_TYPE_IGNITION_OFF, device.getName());
- break;
- case Event.TYPE_MAINTENANCE:
- formatter.format(TITLE_TEMPLATE_TYPE_MAINTENANCE, device.getName());
- break;
- default:
- formatter.format("Unknown type");
- break;
+ VelocityContext velocityContext = new VelocityContext();
+ velocityContext.put("device", device);
+ velocityContext.put("event", event);
+ if (position != null) {
+ velocityContext.put("position", position);
+ velocityContext.put("speedUnits", ReportUtils.getSpeedUnit(userId));
}
- String result = formatter.toString();
- formatter.close();
- return result;
- }
-
- public static String formatMessage(long userId, Event event, Position position) {
- Device device = Context.getIdentityManager().getDeviceById(event.getDeviceId());
- StringBuilder stringBuilder = new StringBuilder();
- Formatter formatter = new Formatter(stringBuilder, Locale.getDefault());
-
- switch (event.getType()) {
- case Event.TYPE_COMMAND_RESULT:
- formatter.format(MESSAGE_TEMPLATE_TYPE_COMMAND_RESULT, device.getName(), event.getServerTime(),
- position.getAttributes().get(Position.KEY_RESULT));
- break;
- case Event.TYPE_DEVICE_ONLINE:
- formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_ONLINE, device.getName(), event.getServerTime());
- break;
- case Event.TYPE_DEVICE_UNKNOWN:
- formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_UNKNOWN, device.getName(), event.getServerTime());
- break;
- case Event.TYPE_DEVICE_OFFLINE:
- formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_OFFLINE, device.getName(), event.getServerTime());
- break;
- case Event.TYPE_DEVICE_MOVING:
- formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_MOVING, device.getName(), position.getFixTime(),
- position.getLatitude(), position.getLongitude());
- break;
- case Event.TYPE_DEVICE_STOPPED:
- formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_STOPPED, device.getName(), position.getFixTime(),
- position.getLatitude(), position.getLongitude());
- break;
- case Event.TYPE_DEVICE_OVERSPEED:
- formatter.format(MESSAGE_TEMPLATE_TYPE_DEVICE_OVERSPEED, device.getName(), position.getFixTime(),
- position.getLatitude(), position.getLongitude(), formatSpeed(userId, position.getSpeed()));
- break;
- case Event.TYPE_GEOFENCE_ENTER:
- formatter.format(MESSAGE_TEMPLATE_TYPE_GEOFENCE_ENTER, device.getName(), position.getFixTime(),
- position.getLatitude(), position.getLongitude(),
- Context.getGeofenceManager().getGeofence(event.getGeofenceId()).getName());
- break;
- case Event.TYPE_GEOFENCE_EXIT:
- formatter.format(MESSAGE_TEMPLATE_TYPE_GEOFENCE_EXIT, device.getName(), position.getFixTime(),
- position.getLatitude(), position.getLongitude(),
- Context.getGeofenceManager().getGeofence(event.getGeofenceId()).getName());
- break;
- case Event.TYPE_ALARM:
- formatter.format(MESSAGE_TEMPLATE_TYPE_ALARM, device.getName(), event.getServerTime(),
- position.getLatitude(), position.getLongitude(),
- position.getAttributes().get(Position.KEY_ALARM));
- break;
- case Event.TYPE_IGNITION_ON:
- formatter.format(MESSAGE_TEMPLATE_TYPE_IGNITION_ON, device.getName(), position.getFixTime(),
- position.getLatitude(), position.getLongitude());
- break;
- case Event.TYPE_IGNITION_OFF:
- formatter.format(MESSAGE_TEMPLATE_TYPE_IGNITION_OFF, device.getName(), position.getFixTime(),
- position.getLatitude(), position.getLongitude());
- break;
- case Event.TYPE_MAINTENANCE:
- formatter.format(MESSAGE_TEMPLATE_TYPE_MAINTENANCE, device.getName(), position.getFixTime(),
- position.getLatitude(), position.getLongitude());
- break;
- default:
- formatter.format("Unknown type");
- break;
+ if (event.getGeofenceId() != 0) {
+ velocityContext.put("geofence", Context.getGeofenceManager().getGeofence(event.getGeofenceId()));
}
- String result = formatter.toString();
- formatter.close();
- return result;
- }
- private static String formatSpeed(long userId, double speed) {
- DecimalFormat df = new DecimalFormat("#.##");
- String result = df.format(speed) + " kn";
- switch (Context.getPermissionsManager().getUser(userId).getSpeedUnit()) {
- case "kmh":
- result = df.format(UnitsConverter.kphFromKnots(speed)) + " km/h";
- break;
- case "mph":
- result = df.format(UnitsConverter.mphFromKnots(speed)) + " mph";
- break;
- default:
- break;
+ Template template = null;
+ try {
+ template = Context.getVelocityEngine().getTemplate(event.getType() + ".vm");
+ } catch (ResourceNotFoundException error) {
+ Log.warning(error);
+ template = Context.getVelocityEngine().getTemplate("unknown.vm");
}
- return result;
+
+ StringWriter writer = new StringWriter();
+ template.merge(velocityContext, writer);
+ String subject = (String) velocityContext.get("subject");
+ return new MailMessage(subject, writer.toString());
}
}
diff --git a/src/org/traccar/notification/NotificationMail.java b/src/org/traccar/notification/NotificationMail.java
index 17e4d6be4..7b7ef6e74 100644
--- a/src/org/traccar/notification/NotificationMail.java
+++ b/src/org/traccar/notification/NotificationMail.java
@@ -61,6 +61,11 @@ public final class NotificationMail {
properties.put("mail.smtp.ssl.trust", sslTrust);
}
+ String sslProtocols = provider.getString("mail.smtp.ssl.protocols");
+ if (sslProtocols != null) {
+ properties.put("mail.smtp.ssl.protocols", sslProtocols);
+ }
+
properties.put("mail.smtp.auth", provider.getString("mail.smtp.auth"));
String username = provider.getString("mail.smtp.username");
@@ -101,8 +106,9 @@ public final class NotificationMail {
}
message.addRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
- message.setSubject(NotificationFormatter.formatTitle(userId, event, position));
- message.setText(NotificationFormatter.formatMessage(userId, event, position));
+ MailMessage mailMessage = NotificationFormatter.formatMessage(userId, event, position);
+ message.setSubject(mailMessage.getSubject());
+ message.setContent(mailMessage.getBody(), "text/html; charset=utf-8");
Transport transport = session.getTransport();
try {
diff --git a/src/org/traccar/protocol/At2000Protocol.java b/src/org/traccar/protocol/At2000Protocol.java
index 418619cb4..35aa0b469 100644
--- a/src/org/traccar/protocol/At2000Protocol.java
+++ b/src/org/traccar/protocol/At2000Protocol.java
@@ -20,6 +20,7 @@ import org.jboss.netty.channel.ChannelPipeline;
import org.traccar.BaseProtocol;
import org.traccar.TrackerServer;
+import java.nio.ByteOrder;
import java.util.List;
public class At2000Protocol extends BaseProtocol {
@@ -30,13 +31,15 @@ public class At2000Protocol extends BaseProtocol {
@Override
public void initTrackerServers(List<TrackerServer> serverList) {
- serverList.add(new TrackerServer(new ServerBootstrap(), getName()) {
+ TrackerServer server = new TrackerServer(new ServerBootstrap(), getName()) {
@Override
protected void addSpecificHandlers(ChannelPipeline pipeline) {
pipeline.addLast("frameDecoder", new At2000FrameDecoder());
pipeline.addLast("objectDecoder", new At2000ProtocolDecoder(At2000Protocol.this));
}
- });
+ };
+ server.setEndianness(ByteOrder.LITTLE_ENDIAN);
+ serverList.add(server);
}
}
diff --git a/src/org/traccar/protocol/At2000ProtocolDecoder.java b/src/org/traccar/protocol/At2000ProtocolDecoder.java
index 17da0eef7..e9c26d406 100644
--- a/src/org/traccar/protocol/At2000ProtocolDecoder.java
+++ b/src/org/traccar/protocol/At2000ProtocolDecoder.java
@@ -50,11 +50,11 @@ public class At2000ProtocolDecoder extends BaseProtocolDecoder {
private static void sendResponse(Channel channel) {
if (channel != null) {
- ChannelBuffer response = ChannelBuffers.directBuffer(BLOCK_LENGTH);
+ ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 2 * BLOCK_LENGTH);
response.writeByte(MSG_ACKNOWLEDGEMENT);
- response.writeMedium(1);
+ response.writeMedium(ChannelBuffers.swapMedium(1));
response.writeByte(0x00); // success
- response.writerIndex(BLOCK_LENGTH);
+ response.writerIndex(2 * BLOCK_LENGTH);
channel.write(response);
}
}
diff --git a/src/org/traccar/protocol/GranitProtocolDecoder.java b/src/org/traccar/protocol/GranitProtocolDecoder.java
index 5fa786e4d..6e8bc24bf 100644
--- a/src/org/traccar/protocol/GranitProtocolDecoder.java
+++ b/src/org/traccar/protocol/GranitProtocolDecoder.java
@@ -61,7 +61,7 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
private static void sendResponseCurrent(Channel channel, int deviceId, long time) {
ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0);
response.writeBytes("BB+UGRC~".getBytes(StandardCharsets.US_ASCII));
- response.writeShort(6); //binary length
+ response.writeShort(6); // length
response.writeInt((int) time);
response.writeShort(deviceId);
appendChecksum(response, 16);
@@ -71,7 +71,7 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
private static void sendResponseArchive(Channel channel, int deviceId, int packNum) {
ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 0);
response.writeBytes("BB+ARCF~".getBytes(StandardCharsets.US_ASCII));
- response.writeShort(4); //binary length
+ response.writeShort(4); // length
response.writeShort(packNum);
response.writeShort(deviceId);
appendChecksum(response, 14);
@@ -95,14 +95,19 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
int latDegrees = buf.readUnsignedByte();
int lonMinutes = buf.readUnsignedShort();
int latMinutes = buf.readUnsignedShort();
+
double latitude = latDegrees + latMinutes / 60000.0;
double longitude = lonDegrees + lonMinutes / 60000.0;
- if (!BitUtil.check(flags, 4)) {
- latitude = -latitude;
- }
- if (!BitUtil.check(flags, 5)) {
- longitude = -longitude;
+
+ if (position.getValid()) {
+ if (!BitUtil.check(flags, 4)) {
+ latitude = -latitude;
+ }
+ if (!BitUtil.check(flags, 5)) {
+ longitude = -longitude;
+ }
}
+
position.setLongitude(longitude);
position.setLatitude(latitude);
@@ -135,11 +140,11 @@ public class GranitProtocolDecoder extends BaseProtocolDecoder {
position.setAltitude(buf.readUnsignedByte() * 10);
- short diOut = buf.readUnsignedByte();
+ int output = buf.readUnsignedByte();
for (int i = 0; i < 8; i++) {
- position.set(Position.PREFIX_IO + (i + 1), BitUtil.check(diOut, i));
+ position.set(Position.PREFIX_IO + (i + 1), BitUtil.check(output, i));
}
- buf.skipBytes(1); //StatMess
+ buf.readUnsignedByte(); // status message buffer
}
@Override
diff --git a/src/org/traccar/protocol/XexunProtocol.java b/src/org/traccar/protocol/XexunProtocol.java
index 2aea2f246..a52d9ff45 100644
--- a/src/org/traccar/protocol/XexunProtocol.java
+++ b/src/org/traccar/protocol/XexunProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import org.jboss.netty.handler.codec.string.StringEncoder;
import org.traccar.BaseProtocol;
import org.traccar.Context;
import org.traccar.TrackerServer;
+import org.traccar.model.Command;
import java.util.List;
@@ -30,6 +31,9 @@ public class XexunProtocol extends BaseProtocol {
public XexunProtocol() {
super("xexun");
+ setSupportedCommands(
+ Command.TYPE_ENGINE_STOP,
+ Command.TYPE_ENGINE_RESUME);
}
@Override
@@ -43,8 +47,9 @@ public class XexunProtocol extends BaseProtocol {
} else {
pipeline.addLast("frameDecoder", new XexunFrameDecoder());
}
- pipeline.addLast("stringDecoder", new StringDecoder());
pipeline.addLast("stringEncoder", new StringEncoder());
+ pipeline.addLast("stringDecoder", new StringDecoder());
+ pipeline.addLast("objectEncoder", new XexunProtocolEncoder());
pipeline.addLast("objectDecoder", new XexunProtocolDecoder(XexunProtocol.this, full));
}
});
diff --git a/src/org/traccar/protocol/XexunProtocolEncoder.java b/src/org/traccar/protocol/XexunProtocolEncoder.java
new file mode 100644
index 000000000..cdf3ac6f7
--- /dev/null
+++ b/src/org/traccar/protocol/XexunProtocolEncoder.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 Anton Tananaev (anton@traccar.org)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.traccar.protocol;
+
+import org.traccar.StringProtocolEncoder;
+import org.traccar.helper.Log;
+import org.traccar.model.Command;
+
+public class XexunProtocolEncoder extends StringProtocolEncoder {
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ initDevicePassword(command, "123456");
+
+ switch (command.getType()) {
+ case Command.TYPE_ENGINE_STOP:
+ return formatCommand(command, "powercar{%s} 11", Command.KEY_DEVICE_PASSWORD);
+ case Command.TYPE_ENGINE_RESUME:
+ return formatCommand(command, "powercar{%s} 00", Command.KEY_DEVICE_PASSWORD);
+ default:
+ Log.warning(new UnsupportedOperationException(command.getType()));
+ break;
+ }
+
+ return null;
+ }
+
+}
diff --git a/templates/mail/alarm.vm b/templates/mail/alarm.vm
new file mode 100644
index 000000000..57b6ba9be
--- /dev/null
+++ b/templates/mail/alarm.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: alarm!")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Alarm: $position.getString("alarm")<br>
+Point: <a href="https://www.openstreetmap.org/?mlat=$position.latitude&mlon=$position.longitude#map=16/$position.latitude/$position.longitude">#if($position.address)$position.address#{else}$position.latitude°, $position.longitude°#end</a><br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/commandResult.vm b/templates/mail/commandResult.vm
new file mode 100644
index 000000000..084152158
--- /dev/null
+++ b/templates/mail/commandResult.vm
@@ -0,0 +1,9 @@
+#set($subject = "$device.name: command result received")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Result: $position.getString("result")<br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/deviceMoving.vm b/templates/mail/deviceMoving.vm
new file mode 100644
index 000000000..2a3c703b5
--- /dev/null
+++ b/templates/mail/deviceMoving.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: moving")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Moving<br>
+Point: <a href="https://www.openstreetmap.org/?mlat=$position.latitude&mlon=$position.longitude#map=16/$position.latitude/$position.longitude">#if($position.address)$position.address#{else}$position.latitude°, $position.longitude°#end</a><br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/deviceOffline.vm b/templates/mail/deviceOffline.vm
new file mode 100644
index 000000000..a62133b71
--- /dev/null
+++ b/templates/mail/deviceOffline.vm
@@ -0,0 +1,9 @@
+#set($subject = "$device.name: offline")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Offline<br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/deviceOnline.vm b/templates/mail/deviceOnline.vm
new file mode 100644
index 000000000..c38393062
--- /dev/null
+++ b/templates/mail/deviceOnline.vm
@@ -0,0 +1,9 @@
+#set($subject = "$device.name: online")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Online<br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/deviceOverspeed.vm b/templates/mail/deviceOverspeed.vm
new file mode 100644
index 000000000..5f8c65096
--- /dev/null
+++ b/templates/mail/deviceOverspeed.vm
@@ -0,0 +1,17 @@
+#set($subject = "$device.name: exceeds the speed")
+#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
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Exceeds the speed: $speedString<br>
+Point: <a href="https://www.openstreetmap.org/?mlat=$position.latitude&mlon=$position.longitude#map=16/$position.latitude/$position.longitude">#if($position.address)$position.address#{else}$position.latitude°, $position.longitude°#end</a><br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/deviceStopped.vm b/templates/mail/deviceStopped.vm
new file mode 100644
index 000000000..003ce2e13
--- /dev/null
+++ b/templates/mail/deviceStopped.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: stopped")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Stopped<br>
+Point: <a href="https://www.openstreetmap.org/?mlat=$position.latitude&mlon=$position.longitude#map=16/$position.latitude/$position.longitude">#if($position.address)$position.address#{else}$position.latitude°, $position.longitude°#end</a><br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/deviceUnknown.vm b/templates/mail/deviceUnknown.vm
new file mode 100644
index 000000000..75203bdfc
--- /dev/null
+++ b/templates/mail/deviceUnknown.vm
@@ -0,0 +1,9 @@
+#set($subject = "$device.name: status is unknown")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Status is unknown<br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/geofenceEnter.vm b/templates/mail/geofenceEnter.vm
new file mode 100644
index 000000000..32ee153bc
--- /dev/null
+++ b/templates/mail/geofenceEnter.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: has entered geofence")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Has entered geofence: $geofence.name<br>
+Point: <a href="https://www.openstreetmap.org/?mlat=$position.latitude&mlon=$position.longitude#map=16/$position.latitude/$position.longitude">#if($position.address)$position.address#{else}$position.latitude°, $position.longitude°#end</a><br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/geofenceExit.vm b/templates/mail/geofenceExit.vm
new file mode 100644
index 000000000..b01653e49
--- /dev/null
+++ b/templates/mail/geofenceExit.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: has exited geofence")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Has exited geofence: $geofence.name<br>
+Point: <a href="https://www.openstreetmap.org/?mlat=$position.latitude&mlon=$position.longitude#map=16/$position.latitude/$position.longitude">#if($position.address)$position.address#{else}$position.latitude°, $position.longitude°#end</a><br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/ignitionOff.vm b/templates/mail/ignitionOff.vm
new file mode 100644
index 000000000..b45ecfca1
--- /dev/null
+++ b/templates/mail/ignitionOff.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: ignition OFF")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Ignition OFF<br>
+Point: <a href="https://www.openstreetmap.org/?mlat=$position.latitude&mlon=$position.longitude#map=16/$position.latitude/$position.longitude">#if($position.address)$position.address#{else}$position.latitude°, $position.longitude°#end</a><br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/ignitionOn.vm b/templates/mail/ignitionOn.vm
new file mode 100644
index 000000000..b2f1041bb
--- /dev/null
+++ b/templates/mail/ignitionOn.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: ignition ON")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Ignition ON<br>
+Point: <a href="https://www.openstreetmap.org/?mlat=$position.latitude&mlon=$position.longitude#map=16/$position.latitude/$position.longitude">#if($position.address)$position.address#{else}$position.latitude°, $position.longitude°#end</a><br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/maintenance.vm b/templates/mail/maintenance.vm
new file mode 100644
index 000000000..c7bc1a960
--- /dev/null
+++ b/templates/mail/maintenance.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: maintenance is required")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Maintenance is required<br>
+Point: <a href="https://www.openstreetmap.org/?mlat=$position.latitude&mlon=$position.longitude#map=16/$position.latitude/$position.longitude">#if($position.address)$position.address#{else}$position.latitude°, $position.longitude°#end</a><br>
+Time: $event.serverTime<br>
+</body>
+</html>
diff --git a/templates/mail/unknown.vm b/templates/mail/unknown.vm
new file mode 100644
index 000000000..566ce0d42
--- /dev/null
+++ b/templates/mail/unknown.vm
@@ -0,0 +1,7 @@
+#set($subject = "Unknown type")
+<!DOCTYPE html>
+<html>
+<body>
+Unknown type
+</body>
+</html>
diff --git a/test/org/traccar/ProtocolTest.java b/test/org/traccar/ProtocolTest.java
index 756393288..93f3150c7 100644
--- a/test/org/traccar/ProtocolTest.java
+++ b/test/org/traccar/ProtocolTest.java
@@ -135,7 +135,9 @@ public class ProtocolTest extends BaseTest {
if (expected != null) {
if (expected.getFixTime() != null) {
- Assert.assertEquals("time", expected.getFixTime(), position.getFixTime());
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Assert.assertEquals("time", dateFormat.format(expected.getFixTime()), dateFormat.format(position.getFixTime()));
}
Assert.assertEquals("valid", expected.getValid(), position.getValid());
Assert.assertEquals("latitude", expected.getLatitude(), position.getLatitude(), 0.00001);
diff --git a/test/org/traccar/protocol/At2000ProtocolDecoderTest.java b/test/org/traccar/protocol/At2000ProtocolDecoderTest.java
index e2b848b45..837d4c3b6 100644
--- a/test/org/traccar/protocol/At2000ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/At2000ProtocolDecoderTest.java
@@ -3,6 +3,8 @@ package org.traccar.protocol;
import org.junit.Test;
import org.traccar.ProtocolTest;
+import java.nio.ByteOrder;
+
import static org.junit.Assume.assumeTrue;
public class At2000ProtocolDecoderTest extends ProtocolTest {
@@ -14,13 +16,16 @@ public class At2000ProtocolDecoderTest extends ProtocolTest {
At2000ProtocolDecoder decoder = new At2000ProtocolDecoder(new At2000Protocol());
- verifyNothing(decoder, binary(
+ verifyNothing(decoder, binary(ByteOrder.LITTLE_ENDIAN,
+ "01012f0000000000000000000000000000333537343534303731363237353938d74dcd195c521a246fb00f16346c7f001919957babc40f84152b60ddeb7ab47a"));
+
+ verifyNothing(decoder, binary(ByteOrder.LITTLE_ENDIAN,
"01012f00000000000000000000000000003335363137333036343430373439320fad981997ae8e031fe10c0ea7641903ca32c0331df467233d2a9cd886fbeef8"));
- verifyPosition(decoder, binary(
+ verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN,
"893f0000000000000000000000000000e048b1a31deba3f5dbe8877f574877e6ed4d022b6611a10d80dfc4c0c11fa8aacf4a9de61528327e2b66843dd9c5d3a7cc9ee1d9c71a34bb482145d88b4fda3e"));
- verifyNothing(decoder, binary(
+ verifyNothing(decoder, binary(ByteOrder.LITTLE_ENDIAN,
"01012f00000000000000000000000000003335373435343037313632363831345612da3748bede02ea4faf04ac02f420c0ff37719eccf2864fa2b8191abf8242"));
}
diff --git a/test/org/traccar/protocol/GranitProtocolDecoderTest.java b/test/org/traccar/protocol/GranitProtocolDecoderTest.java
index 1fea1d98c..6e85b5cfc 100644
--- a/test/org/traccar/protocol/GranitProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/GranitProtocolDecoderTest.java
@@ -13,6 +13,20 @@ public class GranitProtocolDecoderTest extends ProtocolTest {
GranitProtocolDecoder decoder = new GranitProtocolDecoder(new GranitProtocol());
verifyPositions(decoder, binary(ByteOrder.LITTLE_ENDIAN,
+ "2b444441547e8400c500040130050c43495808002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000008002839aee3150200000000640000000000000014002a37420d0a"));
+
+ verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN,
+ "2b525243427e1a00c5008443495808002839aee315020000000064000000000000002a37410d0a"),
+ position("2016-12-08 11:27:00.000", false, 57.00888, 40.97143));
+
+ verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN,
+ "2b525243427e1a00c500ec904858b842283997e30002000000005e000000000d00002a32390d0a"),
+ position("2016-12-07 22:45:00.000", true, 57.00853, 40.97105));
+
+ verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN,
+ "2b525243427e1a00c500009148580800283997e30002000000005f000000000000002a33410d0a"));
+
+ verifyPositions(decoder, binary(ByteOrder.LITTLE_ENDIAN,
"2b444441547e84003b6d0401b10e9217445800b051398f35d34a313b000072000000010b000080b051398f35d34a313b000072000000010b0000f0b051390f33314c303b900371000000010b0000f0b05139cd31e54c2f3cd0016f000000010b0000f0b051396831204d303d950071000000010b0000f0b051397530aa4d323c610171000000010b00000a002a30420d0a"));
verifyPosition(decoder, binary(ByteOrder.LITTLE_ENDIAN,
@@ -24,11 +38,11 @@ public class GranitProtocolDecoderTest extends ProtocolTest {
verifyPositions(decoder, binary(ByteOrder.LITTLE_ENDIAN,
"2b444441547e84003e290401d41680747c57f8a03c38987f50e6005300006c000000001c0000f8b03c38987f50e6005300006c000000001c0000fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe14002a4346"));
- //+IDNT: Navigator.04x Firmware version 0712GLN *21
+ // +IDNT: Navigator.04x Firmware version 0712GLN *21
verifyAttributes(decoder, binary(ByteOrder.LITTLE_ENDIAN,
"2b49444e543a204e6176696761746f722e30347820204669726d776172652076657273696f6e202030373132474c4e202a3231"));
- //ERROR WRONG CHECKSUM_1
+ // ERROR WRONG CHECKSUM_1
verifyAttributes(decoder, binary(ByteOrder.LITTLE_ENDIAN,
"4552524f522057524f4e4720434845434b53554d5f31"));
diff --git a/test/org/traccar/protocol/SuntechProtocolDecoderTest.java b/test/org/traccar/protocol/SuntechProtocolDecoderTest.java
index e64041d29..78e1fb1de 100644
--- a/test/org/traccar/protocol/SuntechProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/SuntechProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class SuntechProtocolDecoderTest extends ProtocolTest {
SuntechProtocolDecoder decoder = new SuntechProtocolDecoder(new SuntechProtocol());
verifyPosition(decoder, text(
+ "ST910;Location;560266;500;20161207;21:33:11;af910be101;-25.504234;-049.278003;000.080;000.00;1;10054889;70;1;1;1311;02;724;06;-317;3041;2;10;92"));
+
+ verifyPosition(decoder, text(
"ST300ALT;205174410;14;712;20110101;00:00:07;00000;+20.593923;-100.336716;000.000;000.00;0;0;0;16.57;000000;81;000000;4.0;0;0.00;0000;0000;0;0"));
verifyNothing(decoder, text(