aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules1
-rw-r--r--pom.xml8
-rw-r--r--schema/changelog-3.9.xml37
-rw-r--r--schema/changelog-master.xml1
-rw-r--r--setup/default.xml6
-rwxr-xr-xsetup/package.sh2
-rw-r--r--setup/traccar.iss2
-rw-r--r--src/org/traccar/BaseProtocolDecoder.java6
-rw-r--r--src/org/traccar/Context.java31
-rw-r--r--src/org/traccar/api/resource/EventResource.java1
-rw-r--r--src/org/traccar/api/resource/NotificationResource.java2
-rw-r--r--src/org/traccar/api/resource/PositionResource.java3
-rw-r--r--src/org/traccar/api/resource/ReportResource.java4
-rw-r--r--src/org/traccar/database/NotificationManager.java25
-rw-r--r--src/org/traccar/geocode/NominatimReverseGeocoder.java4
-rw-r--r--src/org/traccar/helper/UnitsConverter.java9
-rw-r--r--src/org/traccar/model/Notification.java19
-rw-r--r--src/org/traccar/model/Position.java1
-rw-r--r--src/org/traccar/notification/MailMessage.java36
-rw-r--r--src/org/traccar/notification/NotificationFormatter.java230
-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/CalAmpProtocolDecoder.java15
-rw-r--r--src/org/traccar/protocol/GoSafeProtocolDecoder.java36
-rw-r--r--src/org/traccar/protocol/Gps103ProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/GranitProtocolDecoder.java25
-rw-r--r--src/org/traccar/protocol/H02ProtocolDecoder.java21
-rw-r--r--src/org/traccar/protocol/OigoProtocolDecoder.java98
-rw-r--r--src/org/traccar/protocol/Pt502ProtocolDecoder.java7
-rw-r--r--src/org/traccar/protocol/XexunProtocol.java9
-rw-r--r--src/org/traccar/protocol/XexunProtocolEncoder.java42
-rw-r--r--src/org/traccar/reports/Trips.java39
-rw-r--r--templates/mail/alarm.vm10
-rw-r--r--templates/mail/commandResult.vm10
-rw-r--r--templates/mail/deviceMoving.vm10
-rw-r--r--templates/mail/deviceOffline.vm10
-rw-r--r--templates/mail/deviceOnline.vm10
-rw-r--r--templates/mail/deviceOverspeed.vm17
-rw-r--r--templates/mail/deviceStopped.vm10
-rw-r--r--templates/mail/deviceUnknown.vm10
-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/CalAmpProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/GoSafeProtocolDecoderTest.java6
-rw-r--r--test/org/traccar/protocol/Gps103ProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/GranitProtocolDecoderTest.java22
-rw-r--r--test/org/traccar/protocol/H02ProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/OigoProtocolDecoderTest.java15
-rw-r--r--test/org/traccar/protocol/SuntechProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/protocol/UproProtocolDecoderTest.java3
-rw-r--r--test/org/traccar/reports/TripsTest.java69
-rwxr-xr-xtools/test-integration.py9
m---------traccar-web0
60 files changed, 748 insertions, 292 deletions
diff --git a/.gitmodules b/.gitmodules
index 3c5e3a5c7..5e245cc5b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,3 +2,4 @@
path = traccar-web
url = ../traccar-web.git
branch = master
+ ignore = all
diff --git a/pom.xml b/pom.xml
index f71fe0c1a..45df20efc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.traccar</groupId>
<artifactId>traccar</artifactId>
- <version>3.8-SNAPSHOT</version>
+ <version>3.9-SNAPSHOT</version>
<name>traccar</name>
<url>https://www.traccar.org</url>
@@ -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/schema/changelog-3.9.xml b/schema/changelog-3.9.xml
new file mode 100644
index 000000000..93fa6123b
--- /dev/null
+++ b/schema/changelog-3.9.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<databaseChangeLog
+ xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
+ http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd"
+ logicalFilePath="changelog-3.9">
+
+ <changeSet author="author" id="changelog-3.9">
+
+ <addColumn tableName="notifications">
+ <column name="web" type="BOOLEAN" defaultValueBoolean="false" />
+ <column name="mail" type="BOOLEAN" defaultValueBoolean="false" />
+ </addColumn>
+
+ <update tableName="notifications">
+ <column name="web" valueBoolean="true" />
+ <where>attributes = '{"web":"true"}'</where>
+ </update>
+
+ <update tableName="notifications">
+ <column name="mail" valueBoolean="true" />
+ <where>attributes = '{"mail":"true"}'</where>
+ </update>
+
+ <update tableName="notifications">
+ <column name="web" valueBoolean="true" />
+ <column name="mail" valueBoolean="true" />
+ <where>attributes = '{"web":"true","mail":"true"}'</where>
+ </update>
+
+ <update tableName="notifications">
+ <column name="attributes" value="{}" />
+ </update>
+
+ </changeSet>
+</databaseChangeLog>
diff --git a/schema/changelog-master.xml b/schema/changelog-master.xml
index 341714ca8..448015568 100644
--- a/schema/changelog-master.xml
+++ b/schema/changelog-master.xml
@@ -10,4 +10,5 @@
<include file="changelog-3.6.xml" relativeToChangelogFile="true" />
<include file="changelog-3.7.xml" relativeToChangelogFile="true" />
<include file="changelog-3.8.xml" relativeToChangelogFile="true" />
+ <include file="changelog-3.9.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
diff --git a/setup/default.xml b/setup/default.xml
index 5c6f8fe90..ad23d7bd3 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -264,14 +264,16 @@
</entry>
<entry key='database.insertNotification'>
- INSERT INTO notifications (userId, type, attributes)
- VALUES (:userId, :type, :attributes)
+ INSERT INTO notifications (userId, type, web, mail, attributes)
+ VALUES (:userId, :type, :web, :mail, :attributes)
</entry>
<entry key='database.updateNotification'>
UPDATE notifications SET
userId = :userId,
type = :type,
+ web = :web,
+ mail = :mail,
attributes = :attributes
WHERE id = :id
</entry>
diff --git a/setup/package.sh b/setup/package.sh
index a6b54717d..404c7b86b 100755
--- a/setup/package.sh
+++ b/setup/package.sh
@@ -22,7 +22,7 @@ check_requirement () {
fi
}
-check_requirement "ls ../../ext-6.0.1" "Missing ../../ext-6.0.1 (https://www.sencha.com/legal/GPL/)"
+check_requirement "ls ../../ext-6.2.0" "Missing ../../ext-6.2.0 (https://www.sencha.com/legal/GPL/)"
check_requirement "ls yajsw-*.zip" "Missing yajsw-*.zip (https://sourceforge.net/projects/yajsw/files/)"
check_requirement "ls innosetup-*.exe" "Missing isetup-*.exe (http://www.jrsoftware.org/isdl.php)"
check_requirement "which sencha" "Missing sencha cmd package (https://www.sencha.com/products/extjs/cmd-download/)"
diff --git a/setup/traccar.iss b/setup/traccar.iss
index 87c2e2fba..50eae41b0 100644
--- a/setup/traccar.iss
+++ b/setup/traccar.iss
@@ -1,6 +1,6 @@
[Setup]
AppName=Traccar
-AppVersion=3.8
+AppVersion=3.9
DefaultDirName={pf}\Traccar
AlwaysRestart=yes
OutputBaseFilename=traccar-setup
diff --git a/src/org/traccar/BaseProtocolDecoder.java b/src/org/traccar/BaseProtocolDecoder.java
index d8130c79e..ea0905bf2 100644
--- a/src/org/traccar/BaseProtocolDecoder.java
+++ b/src/org/traccar/BaseProtocolDecoder.java
@@ -36,6 +36,12 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
Device device = new Device();
device.setName(uniqueId);
device.setUniqueId(uniqueId);
+ device.setCategory(Context.getConfig().getString("database.registerUnknown.defaultCategory"));
+
+ long defaultGroupId = Context.getConfig().getLong("database.registerUnknown.defaultGroupId");
+ if (defaultGroupId != 0) {
+ device.setGroupId(defaultGroupId);
+ }
try {
Context.getDeviceManager().addDevice(device);
diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java
index 581f00082..2b8860187 100644
--- a/src/org/traccar/Context.java
+++ b/src/org/traccar/Context.java
@@ -17,6 +17,11 @@ package org.traccar;
import com.ning.http.client.AsyncHttpClient;
+import java.net.InetAddress;
+import java.util.Properties;
+
+import org.apache.velocity.app.VelocityEngine;
+import org.eclipse.jetty.util.URIUtil;
import org.traccar.database.AliasesManager;
import org.traccar.database.ConnectionManager;
import org.traccar.database.DataManager;
@@ -125,6 +130,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() {
@@ -181,7 +192,11 @@ public final class Context {
int cacheSize = config.getInteger("geocoder.cacheSize");
switch (type) {
case "nominatim":
- reverseGeocoder = new NominatimReverseGeocoder(url, cacheSize);
+ if (key != null) {
+ reverseGeocoder = new NominatimReverseGeocoder(url, key, cacheSize);
+ } else {
+ reverseGeocoder = new NominatimReverseGeocoder(url, cacheSize);
+ }
break;
case "gisgraphy":
reverseGeocoder = new GisgraphyReverseGeocoder(url, cacheSize);
@@ -242,6 +257,20 @@ 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");
+
+ String address = config.getString("web.address", InetAddress.getLocalHost().getHostAddress());
+ int port = config.getInteger("web.port", 8082);
+ String webUrl = URIUtil.newURI("http", address, port, "", "");
+ webUrl = Context.getConfig().getString("web.url", webUrl);
+ velocityProperties.setProperty("web.url", webUrl);
+
+ velocityEngine = new VelocityEngine();
+ velocityEngine.init(velocityProperties);
}
serverManager = new ServerManager();
diff --git a/src/org/traccar/api/resource/EventResource.java b/src/org/traccar/api/resource/EventResource.java
index c0a8f968d..0ef5456af 100644
--- a/src/org/traccar/api/resource/EventResource.java
+++ b/src/org/traccar/api/resource/EventResource.java
@@ -29,4 +29,5 @@ public class EventResource extends BaseResource {
}
return event;
}
+
}
diff --git a/src/org/traccar/api/resource/NotificationResource.java b/src/org/traccar/api/resource/NotificationResource.java
index 6c9725f37..03f7e4ba0 100644
--- a/src/org/traccar/api/resource/NotificationResource.java
+++ b/src/org/traccar/api/resource/NotificationResource.java
@@ -49,7 +49,7 @@ public class NotificationResource extends BaseResource {
userId = getUserId();
}
Context.getPermissionsManager().checkUser(getUserId(), userId);
- return Context.getNotificationManager().getUserNotifications(userId);
+ return Context.getNotificationManager().getAllUserNotifications(userId);
}
@POST
diff --git a/src/org/traccar/api/resource/PositionResource.java b/src/org/traccar/api/resource/PositionResource.java
index 84f406bee..9d3cd9ae6 100644
--- a/src/org/traccar/api/resource/PositionResource.java
+++ b/src/org/traccar/api/resource/PositionResource.java
@@ -37,6 +37,7 @@ import java.util.Collection;
import java.util.List;
@Path("positions")
+@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PositionResource extends BaseResource {
@@ -46,7 +47,6 @@ public class PositionResource extends BaseResource {
public static final String CONTENT_DISPOSITION_VALUE_GPX = "attachment; filename=positions.gpx";
@GET
- @Produces(MediaType.APPLICATION_JSON)
public Collection<Position> getJson(
@QueryParam("deviceId") long deviceId, @QueryParam("id") List<Long> positionIds,
@QueryParam("from") String from, @QueryParam("to") String to)
@@ -92,4 +92,5 @@ public class PositionResource extends BaseResource {
deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to)));
return Response.ok(gpx.build()).header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_GPX).build();
}
+
}
diff --git a/src/org/traccar/api/resource/ReportResource.java b/src/org/traccar/api/resource/ReportResource.java
index e37d6f01d..a1b35d64e 100644
--- a/src/org/traccar/api/resource/ReportResource.java
+++ b/src/org/traccar/api/resource/ReportResource.java
@@ -27,6 +27,7 @@ import org.traccar.reports.model.TripReport;
import org.traccar.reports.Route;
@Path("reports")
+@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ReportResource extends BaseResource {
@@ -35,7 +36,6 @@ public class ReportResource extends BaseResource {
@Path("route")
@GET
- @Produces(MediaType.APPLICATION_JSON)
public Collection<Position> getRoute(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
@@ -59,7 +59,6 @@ public class ReportResource extends BaseResource {
@Path("events")
@GET
- @Produces(MediaType.APPLICATION_JSON)
public Collection<Event> getEvents(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("type") final List<String> types,
@@ -85,7 +84,6 @@ public class ReportResource extends BaseResource {
@Path("summary")
@GET
- @Produces(MediaType.APPLICATION_JSON)
public Collection<SummaryReport> getSummary(
@QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds,
@QueryParam("from") String from, @QueryParam("to") String to) throws SQLException {
diff --git a/src/org/traccar/database/NotificationManager.java b/src/org/traccar/database/NotificationManager.java
index 7e79e289f..ee804f5cd 100644
--- a/src/org/traccar/database/NotificationManager.java
+++ b/src/org/traccar/database/NotificationManager.java
@@ -59,10 +59,10 @@ public class NotificationManager {
&& Context.getGeofenceManager().checkGeofence(userId, event.getGeofenceId())) {
Notification notification = getUserNotificationByType(userId, event.getType());
if (notification != null) {
- if (notification.getAttributes().containsKey("web")) {
+ if (notification.getWeb()) {
Context.getConnectionManager().updateEvent(userId, event, position);
}
- if (notification.getAttributes().containsKey("mail")) {
+ if (notification.getMail()) {
NotificationMail.sendMailAsync(userId, event, position);
}
}
@@ -130,8 +130,9 @@ public class NotificationManager {
public void updateNotification(Notification notification) {
Notification cachedNotification = getUserNotificationByType(notification.getUserId(), notification.getType());
if (cachedNotification != null) {
- if (!cachedNotification.getAttributes().equals(notification.getAttributes())) {
- if (notification.getAttributes().isEmpty()) {
+ if (cachedNotification.getWeb() != notification.getWeb()
+ || cachedNotification.getMail() != notification.getMail()) {
+ if (!notification.getWeb() && !notification.getMail()) {
try {
dataManager.removeNotification(cachedNotification);
} catch (SQLException error) {
@@ -146,6 +147,8 @@ public class NotificationManager {
} else {
notificationsLock.writeLock().lock();
try {
+ cachedNotification.setWeb(notification.getWeb());
+ cachedNotification.setMail(notification.getMail());
cachedNotification.setAttributes(notification.getAttributes());
} finally {
notificationsLock.writeLock().unlock();
@@ -159,7 +162,7 @@ public class NotificationManager {
} else {
notification.setId(cachedNotification.getId());
}
- } else if (!notification.getAttributes().isEmpty()) {
+ } else if (notification.getWeb() || notification.getMail()) {
try {
dataManager.addNotification(notification);
} catch (SQLException error) {
@@ -193,4 +196,16 @@ public class NotificationManager {
return notifications;
}
+ public Collection<Notification> getAllUserNotifications(long userId) {
+ Map<String, Notification> notifications = new HashMap<>();
+ for (Notification notification : getAllNotifications()) {
+ notification.setUserId(userId);
+ notifications.put(notification.getType(), notification);
+ }
+ for (Notification notification : getUserNotifications(userId)) {
+ notifications.put(notification.getType(), notification);
+ }
+ return notifications.values();
+ }
+
}
diff --git a/src/org/traccar/geocode/NominatimReverseGeocoder.java b/src/org/traccar/geocode/NominatimReverseGeocoder.java
index 0fbeaef83..b393f6490 100644
--- a/src/org/traccar/geocode/NominatimReverseGeocoder.java
+++ b/src/org/traccar/geocode/NominatimReverseGeocoder.java
@@ -27,6 +27,10 @@ public class NominatimReverseGeocoder extends JsonReverseGeocoder {
super(url + "?format=json&lat=%f&lon=%f&zoom=18&addressdetails=1", cacheSize);
}
+ public NominatimReverseGeocoder(String url, String key, int cacheSize) {
+ super(url + "?format=json&lat=%f&lon=%f&zoom=18&addressdetails=1&key=" + key, cacheSize);
+ }
+
@Override
public Address parseAddress(JsonObject json) {
JsonObject result = json.getJsonObject("address");
diff --git a/src/org/traccar/helper/UnitsConverter.java b/src/org/traccar/helper/UnitsConverter.java
index 00b00861e..e0d94c6dc 100644
--- a/src/org/traccar/helper/UnitsConverter.java
+++ b/src/org/traccar/helper/UnitsConverter.java
@@ -21,6 +21,7 @@ public final class UnitsConverter {
private static final double KNOTS_TO_MPH_RATIO = 0.868976;
private static final double KNOTS_TO_MPS_RATIO = 1.94384;
private static final double KNOTS_TO_CPS_RATIO = 0.0194384449;
+ private static final double METERS_TO_FEET_RATIO = 0.3048;
private UnitsConverter() {
}
@@ -53,4 +54,12 @@ public final class UnitsConverter {
return value * KNOTS_TO_CPS_RATIO;
}
+ public static double feetFromMeters(double value) {
+ return value / METERS_TO_FEET_RATIO;
+ }
+
+ public static double metersFromFeet(double value) {
+ return value * METERS_TO_FEET_RATIO;
+ }
+
}
diff --git a/src/org/traccar/model/Notification.java b/src/org/traccar/model/Notification.java
index 64e1ac60c..dd5f66f15 100644
--- a/src/org/traccar/model/Notification.java
+++ b/src/org/traccar/model/Notification.java
@@ -37,4 +37,23 @@ public class Notification extends Extensible {
this.type = type;
}
+ private boolean web;
+
+ public boolean getWeb() {
+ return web;
+ }
+
+ public void setWeb(boolean web) {
+ this.web = web;
+ }
+
+ private boolean mail;
+
+ public boolean getMail() {
+ return mail;
+ }
+
+ public void setMail(boolean mail) {
+ this.mail = mail;
+ }
}
diff --git a/src/org/traccar/model/Position.java b/src/org/traccar/model/Position.java
index b2965c89c..8ca2588e2 100644
--- a/src/org/traccar/model/Position.java
+++ b/src/org/traccar/model/Position.java
@@ -59,6 +59,7 @@ public class Position extends Message {
public static final String KEY_MOTION = "motion";
public static final String KEY_ARMED = "armed";
public static final String KEY_ACCURACY = "accuracy";
+ public static final String KEY_GEOFENCE = "geofence";
public static final String KEY_DTCS = "dtcs";
public static final String KEY_OBD_SPEED = "obdSpeed";
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..819c387d1 100644
--- a/src/org/traccar/notification/NotificationFormatter.java
+++ b/src/org/traccar/notification/NotificationFormatter.java
@@ -15,223 +15,49 @@
*/
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;
+ velocityContext.put("webUrl", Context.getVelocityEngine().getProperty("web.url"));
+
+ 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/CalAmpProtocolDecoder.java b/src/org/traccar/protocol/CalAmpProtocolDecoder.java
index 510684411..ee4cc65b4 100644
--- a/src/org/traccar/protocol/CalAmpProtocolDecoder.java
+++ b/src/org/traccar/protocol/CalAmpProtocolDecoder.java
@@ -149,19 +149,8 @@ public class CalAmpProtocolDecoder extends BaseProtocolDecoder {
int content = buf.readUnsignedByte();
if (BitUtil.check(content, 0)) {
-
- int length = buf.readUnsignedByte();
- long id = 0;
- for (int i = 0; i < length; i++) {
- int b = buf.readUnsignedByte();
- id = id * 10 + (b >> 4);
- if ((b & 0xf) != 0xf) {
- id = id * 10 + (b & 0xf);
- }
- }
-
- getDeviceSession(channel, remoteAddress, String.valueOf(id));
-
+ String id = ChannelBuffers.hexDump(buf.readBytes(buf.readUnsignedByte()));
+ getDeviceSession(channel, remoteAddress, id);
}
if (BitUtil.check(content, 1)) {
diff --git a/src/org/traccar/protocol/GoSafeProtocolDecoder.java b/src/org/traccar/protocol/GoSafeProtocolDecoder.java
index a258e922c..a7f1024c6 100644
--- a/src/org/traccar/protocol/GoSafeProtocolDecoder.java
+++ b/src/org/traccar/protocol/GoSafeProtocolDecoder.java
@@ -90,13 +90,14 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder {
.groupBegin()
.text("DTT:")
.number("(x+);") // status
- .expression("[^;]*;")
- .number("x+;") // geo-fence 0-119
- .number("x+;") // geo-fence 120-155
- .number("x+,?") // event status
+ .number("(x+)?;") // io
+ .number("(x+);") // geo-fence 0-119
+ .number("(x+);") // geo-fence 120-155
+ .number("(x+)") // event status
+ .number("(?:;(x+))?,?") // packet type
.groupEnd("?")
.groupBegin()
- .text("ETD:").expression("[^,]*,?")
+ .text("ETD:").expression("([^,]+),?")
.groupEnd("?")
.groupBegin()
.text("OBD:")
@@ -108,6 +109,9 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder {
.groupBegin()
.text("TRU:").expression("[^,]*,?")
.groupEnd("?")
+ .groupBegin()
+ .text("TAG:").expression("([^,]+),?")
+ .groupEnd("?")
.compile();
private static final Pattern PATTERN_OLD = new PatternBuilder()
@@ -122,7 +126,7 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder {
.number("([EW])(d+.d+);") // longitude
.number("(d+)?;") // speed
.number("(d+);") // course
- .number("(d+.?d*)").optional() // hdop
+ .number("(d+.?d*)").optional() // hdop
.number("(dd)(dd)(dd)") // date
.any()
.compile();
@@ -162,16 +166,28 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_POWER, parser.next());
position.set(Position.KEY_BATTERY, parser.next());
- String status = parser.next();
- if (status != null) {
- position.set(Position.KEY_IGNITION, BitUtil.check(Integer.parseInt(status, 16), 13));
+ if (parser.hasNext(6)) {
+ long status = parser.nextLong(16);
+ position.set(Position.KEY_IGNITION, BitUtil.check(status, 13));
position.set(Position.KEY_STATUS, status);
+ position.set("ioStatus", parser.next());
+ position.set(Position.KEY_GEOFENCE, parser.next() + parser.next());
+ position.set("eventStatus", parser.next());
+ position.set("packetType", parser.next());
+ }
+
+ if (parser.hasNext()) {
+ position.set("eventData", parser.next());
}
if (parser.hasNext()) {
position.set("obd", parser.next());
}
+ if (parser.hasNext()) {
+ position.set("tagData", parser.next());
+ }
+
return position;
}
@@ -211,7 +227,7 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder {
position.setValid(parser.next().equals("A"));
position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG));
- position.setSpeed(parser.nextDouble());
+ position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble()));
position.setCourse(parser.nextDouble());
position.set(Position.KEY_HDOP, parser.next());
diff --git a/src/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/org/traccar/protocol/Gps103ProtocolDecoder.java
index 57dc784d3..d929ae917 100644
--- a/src/org/traccar/protocol/Gps103ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gps103ProtocolDecoder.java
@@ -119,6 +119,8 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_POWER_OFF;
case "door alarm":
return Position.ALARM_DOOR;
+ case "ac alarm":
+ return Position.ALARM_POWER_CUT;
default:
return null;
}
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/H02ProtocolDecoder.java b/src/org/traccar/protocol/H02ProtocolDecoder.java
index 45a978571..2c7852d16 100644
--- a/src/org/traccar/protocol/H02ProtocolDecoder.java
+++ b/src/org/traccar/protocol/H02ProtocolDecoder.java
@@ -75,7 +75,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED);
}
}
- position.set(Position.KEY_IGNITION, BitUtil.check(status, 10));
+ position.set(Position.KEY_IGNITION, !BitUtil.check(status, 10));
position.set(Position.KEY_STATUS, status);
}
@@ -167,6 +167,12 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
.number("(?:(dd)(dd)(dd))?,") // date (ddmmyy)
.any()
.number("(x{8})") // status
+ .groupBegin()
+ .number(", *(x+),") // mcc
+ .number(" *(x+),") // mnc
+ .number(" *(x+),") // lac
+ .number(" *(x+)") // cid
+ .groupEnd("?")
.any()
.compile();
@@ -237,6 +243,19 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder {
processStatus(position, parser.nextLong(16));
+ if (parser.hasNext(4)) {
+ int mcc = parser.nextInt(16);
+ int mnc = parser.nextInt(16);
+ int lac = parser.nextInt(16);
+ int cid = parser.nextInt(16);
+ if (mcc != 0 && mnc != 0 && lac != 0 && cid != 0) {
+ position.set(Position.KEY_MCC, mcc);
+ position.set(Position.KEY_MNC, mnc);
+ position.set(Position.KEY_LAC, lac);
+ position.set(Position.KEY_CID, cid);
+ }
+ }
+
return position;
}
diff --git a/src/org/traccar/protocol/OigoProtocolDecoder.java b/src/org/traccar/protocol/OigoProtocolDecoder.java
index 15a215c4f..9e6a9a82e 100644
--- a/src/org/traccar/protocol/OigoProtocolDecoder.java
+++ b/src/org/traccar/protocol/OigoProtocolDecoder.java
@@ -34,15 +34,12 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
- public static final int MSG_LOCATION = 0x00;
- public static final int MSG_REMOTE_START = 0x10;
- public static final int MSG_ACKNOWLEDGEMENT = 0xE0;
+ public static final int MSG_AR_LOCATION = 0x00;
+ public static final int MSG_AR_REMOTE_START = 0x10;
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+ public static final int MSG_ACKNOWLEDGEMENT = 0xE0;
- ChannelBuffer buf = (ChannelBuffer) msg;
+ private Position decodeArMessage(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
buf.skipBytes(1); // header
buf.readUnsignedShort(); // length
@@ -67,7 +64,7 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
break;
}
- if (deviceSession == null || type != MSG_LOCATION) {
+ if (deviceSession == null || type != MSG_AR_LOCATION) {
return null;
}
@@ -154,4 +151,89 @@ public class OigoProtocolDecoder extends BaseProtocolDecoder {
return position;
}
+ private double convertCoordinate(long value) {
+ boolean negative = value < 0;
+ value = Math.abs(value);
+ double minutes = (value % 100000) * 0.001;
+ value /= 100000;
+ double degrees = value + minutes / 60;
+ return negative ? -degrees : degrees;
+ }
+
+ private Position decodeMgMessage(Channel channel, SocketAddress remoteAddress, ChannelBuffer buf) {
+
+ buf.readUnsignedByte(); // tag
+ int flags = buf.getUnsignedByte(buf.readerIndex());
+
+ DeviceSession deviceSession;
+ if (BitUtil.check(flags, 6)) {
+ buf.readUnsignedByte(); // flags
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ } else {
+ String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1);
+ deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ }
+
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position();
+ position.setProtocol(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ buf.skipBytes(8); // imsi
+
+ int date = buf.readUnsignedShort();
+
+ DateBuilder dateBuilder = new DateBuilder()
+ .setDate(2010 + BitUtil.from(date, 12), BitUtil.between(date, 8, 12), BitUtil.to(date, 8))
+ .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), 0);
+
+ position.setValid(true);
+ position.setLatitude(convertCoordinate(buf.readInt()));
+ position.setLongitude(convertCoordinate(buf.readInt()));
+
+ position.setAltitude(UnitsConverter.metersFromFeet(buf.readShort()));
+ position.setCourse(buf.readUnsignedShort());
+ position.setSpeed(UnitsConverter.knotsFromMph(buf.readUnsignedByte()));
+
+ position.set(Position.KEY_POWER, buf.readUnsignedByte() * 100 + "mV");
+ position.set(Position.PREFIX_IO + 1, buf.readUnsignedByte());
+
+ dateBuilder.setSecond(buf.readUnsignedByte());
+ position.setTime(dateBuilder.getDate());
+
+ position.set(Position.KEY_GSM, buf.readUnsignedByte());
+
+ int index = buf.readUnsignedByte();
+
+ position.set(Position.KEY_VERSION, buf.readUnsignedByte());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.set(Position.KEY_ODOMETER, (long) (buf.readUnsignedInt() * 1609.34));
+
+ if (channel != null && BitUtil.check(flags, 7)) {
+ ChannelBuffer response = ChannelBuffers.dynamicBuffer();
+ response.writeByte(MSG_ACKNOWLEDGEMENT);
+ response.writeByte(index);
+ response.writeByte(0x00);
+ channel.write(response, remoteAddress);
+ }
+
+ return position;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ if (buf.getUnsignedByte(buf.readerIndex()) == 0x7e) {
+ return decodeArMessage(channel, remoteAddress, buf);
+ } else {
+ return decodeMgMessage(channel, remoteAddress, buf);
+ }
+ }
+
}
diff --git a/src/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/org/traccar/protocol/Pt502ProtocolDecoder.java
index 2d3cb9101..f3d9e8380 100644
--- a/src/org/traccar/protocol/Pt502ProtocolDecoder.java
+++ b/src/org/traccar/protocol/Pt502ProtocolDecoder.java
@@ -29,6 +29,10 @@ import java.util.regex.Pattern;
public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
+ private static final int MAX_CHUNK_SIZE = 960;
+
+ private byte[] photo;
+
public Pt502ProtocolDecoder(Pt502Protocol protocol) {
super(protocol);
}
@@ -92,7 +96,8 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder {
String type = parser.next();
if (type.startsWith("PHO") && channel != null) {
- channel.write("#PHD0," + type.substring(3) + "\r\n");
+ photo = new byte[Integer.parseInt(type.substring(3))];
+ channel.write("#PHD0," + Math.min(photo.length, MAX_CHUNK_SIZE) + "\r\n");
}
position.set(Position.KEY_ALARM, decodeAlarm(type));
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/src/org/traccar/reports/Trips.java b/src/org/traccar/reports/Trips.java
index d4e25f5e5..a52d48f16 100644
--- a/src/org/traccar/reports/Trips.java
+++ b/src/org/traccar/reports/Trips.java
@@ -46,7 +46,8 @@ public final class Trips {
private Trips() {
}
- private static TripReport calculateTrip(ArrayList<Position> positions, int startIndex, int endIndex) {
+ private static TripReport calculateTrip(
+ ArrayList<Position> positions, int startIndex, int endIndex, boolean ignoreOdometer) {
Position startTrip = positions.get(startIndex);
Position endTrip = positions.get(endIndex);
@@ -79,8 +80,6 @@ public final class Trips {
trip.setEndTime(endTrip.getFixTime());
trip.setEndAddress(endTrip.getAddress());
- boolean ignoreOdometer = Context.getDeviceManager()
- .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
trip.setDistance(ReportUtils.calculateDistance(startTrip, endTrip, !ignoreOdometer));
trip.setDuration(tripDuration);
trip.setAverageSpeed(speedSum / (endIndex - startIndex));
@@ -90,15 +89,14 @@ public final class Trips {
return trip;
}
- private static Collection<TripReport> detectTrips(long deviceId, Date from, Date to) throws SQLException {
- double speedThreshold = Context.getConfig().getDouble("event.motion.speedThreshold", 0.01);
- long minimalTripDuration = Context.getConfig().getLong("report.trip.minimalTripDuration", 300) * 1000;
- double minimalTripDistance = Context.getConfig().getLong("report.trip.minimalTripDistance", 500);
- long minimalParkingDuration = Context.getConfig().getLong("report.trip.minimalParkingDuration", 300) * 1000;
- boolean greedyParking = Context.getConfig().getBoolean("report.trip.greedyParking");
+ protected static Collection<TripReport> detectTrips(
+ double speedThreshold, double minimalTripDistance,
+ long minimalTripDuration, long minimalParkingDuration, boolean greedyParking, boolean ignoreOdometer,
+ Collection<Position> positionCollection) {
+
Collection<TripReport> result = new ArrayList<>();
- ArrayList<Position> positions = new ArrayList<>(Context.getDataManager().getPositions(deviceId, from, to));
+ ArrayList<Position> positions = new ArrayList<>(positionCollection);
if (positions != null && !positions.isEmpty()) {
int previousStartParkingIndex = 0;
int startParkingIndex = -1;
@@ -150,7 +148,8 @@ public final class Trips {
if ((parkingDuration >= minimalParkingDuration || isLast)
&& previousEndParkingIndex < startParkingIndex) {
if (!tripFiltered) {
- result.add(calculateTrip(positions, previousEndParkingIndex, startParkingIndex));
+ result.add(calculateTrip(
+ positions, previousEndParkingIndex, startParkingIndex, ignoreOdometer));
}
previousEndParkingIndex = endParkingIndex;
skipped = false;
@@ -161,9 +160,26 @@ public final class Trips {
}
}
}
+
return result;
}
+ private static Collection<TripReport> detectTrips(long deviceId, Date from, Date to) throws SQLException {
+ double speedThreshold = Context.getConfig().getDouble("event.motion.speedThreshold", 0.01);
+ long minimalTripDuration = Context.getConfig().getLong("report.trip.minimalTripDuration", 300) * 1000;
+ double minimalTripDistance = Context.getConfig().getLong("report.trip.minimalTripDistance", 500);
+ long minimalParkingDuration = Context.getConfig().getLong("report.trip.minimalParkingDuration", 300) * 1000;
+ boolean greedyParking = Context.getConfig().getBoolean("report.trip.greedyParking");
+
+ boolean ignoreOdometer = Context.getDeviceManager()
+ .lookupAttributeBoolean(deviceId, "report.ignoreOdometer", false, true);
+
+ return detectTrips(
+ speedThreshold, minimalTripDistance, minimalTripDuration,
+ minimalParkingDuration, greedyParking, ignoreOdometer,
+ Context.getDataManager().getPositions(deviceId, from, to));
+ }
+
public static Collection<TripReport> getObjects(long userId, Collection<Long> deviceIds, Collection<Long> groupIds,
Date from, Date to) throws SQLException {
ArrayList<TripReport> result = new ArrayList<>();
@@ -217,4 +233,5 @@ public final class Trips {
transformer.write();
}
}
+
}
diff --git a/templates/mail/alarm.vm b/templates/mail/alarm.vm
new file mode 100644
index 000000000..1d2b4fdf8
--- /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>
+Time: $event.serverTime<br>
+Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude°, $position.longitude°#{end}</a><br>
+</body>
+</html>
diff --git a/templates/mail/commandResult.vm b/templates/mail/commandResult.vm
new file mode 100644
index 000000000..4c330584d
--- /dev/null
+++ b/templates/mail/commandResult.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: command result received")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Result: $position.getString("result")<br>
+Time: $event.serverTime<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
new file mode 100644
index 000000000..a946753e4
--- /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>
+Time: $event.serverTime<br>
+Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude°, $position.longitude°#{end}</a><br>
+</body>
+</html>
diff --git a/templates/mail/deviceOffline.vm b/templates/mail/deviceOffline.vm
new file mode 100644
index 000000000..ee7e96f05
--- /dev/null
+++ b/templates/mail/deviceOffline.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: offline")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Offline<br>
+Time: $event.serverTime<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
new file mode 100644
index 000000000..379b5cc80
--- /dev/null
+++ b/templates/mail/deviceOnline.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: online")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Online<br>
+Time: $event.serverTime<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
new file mode 100644
index 000000000..7b99c6a06
--- /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>
+Time: $event.serverTime<br>
+Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude°, $position.longitude°#{end}</a><br>
+</body>
+</html>
diff --git a/templates/mail/deviceStopped.vm b/templates/mail/deviceStopped.vm
new file mode 100644
index 000000000..c36e6f1b6
--- /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>
+Time: $event.serverTime<br>
+Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude°, $position.longitude°#{end}</a><br>
+</body>
+</html>
diff --git a/templates/mail/deviceUnknown.vm b/templates/mail/deviceUnknown.vm
new file mode 100644
index 000000000..40b8fbfa7
--- /dev/null
+++ b/templates/mail/deviceUnknown.vm
@@ -0,0 +1,10 @@
+#set($subject = "$device.name: status is unknown")
+<!DOCTYPE html>
+<html>
+<body>
+Device: $device.name<br>
+Status is unknown<br>
+Time: $event.serverTime<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
new file mode 100644
index 000000000..cef24723a
--- /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>
+Time: $event.serverTime<br>
+Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude°, $position.longitude°#{end}</a><br>
+</body>
+</html>
diff --git a/templates/mail/geofenceExit.vm b/templates/mail/geofenceExit.vm
new file mode 100644
index 000000000..e696e6556
--- /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>
+Time: $event.serverTime<br>
+Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude°, $position.longitude°#{end}</a><br>
+</body>
+</html>
diff --git a/templates/mail/ignitionOff.vm b/templates/mail/ignitionOff.vm
new file mode 100644
index 000000000..229405cca
--- /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>
+Time: $event.serverTime<br>
+Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude°, $position.longitude°#{end}</a><br>
+</body>
+</html>
diff --git a/templates/mail/ignitionOn.vm b/templates/mail/ignitionOn.vm
new file mode 100644
index 000000000..2aeea0132
--- /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>
+Time: $event.serverTime<br>
+Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude°, $position.longitude°#{end}</a><br>
+</body>
+</html>
diff --git a/templates/mail/maintenance.vm b/templates/mail/maintenance.vm
new file mode 100644
index 000000000..4184d138f
--- /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>
+Time: $event.serverTime<br>
+Point: <a href="$webUrl?eventId=$event.id">#{if}($position.address)$position.address#{else}$position.latitude°, $position.longitude°#{end}</a><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/CalAmpProtocolDecoderTest.java b/test/org/traccar/protocol/CalAmpProtocolDecoderTest.java
index 8d7d5f9fe..b6fb5bd0e 100644
--- a/test/org/traccar/protocol/CalAmpProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/CalAmpProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class CalAmpProtocolDecoderTest extends ProtocolTest {
CalAmpProtocolDecoder decoder = new CalAmpProtocolDecoder(new CalAmpProtocol());
verifyPosition(decoder, binary(
+ "83092701131797081078220107010200dc583d4d3f583d4d3f19c70502cd1d512d00005f180000008500ec0800101eff980f090100313102000000000000000000"));
+
+ verifyPosition(decoder, binary(
"8305133303910501010102004557E5AB2457E3B3E01FD828DBFE9E3465000028C90000004201310704001EFFA12F0B22081BCA05000000000000000F87000E8E2F00EA029E0000082D"));
verifyPosition(decoder, binary(
diff --git a/test/org/traccar/protocol/GoSafeProtocolDecoderTest.java b/test/org/traccar/protocol/GoSafeProtocolDecoderTest.java
index fb0dec4fb..b8f98d8b9 100644
--- a/test/org/traccar/protocol/GoSafeProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/GoSafeProtocolDecoderTest.java
@@ -10,6 +10,12 @@ public class GoSafeProtocolDecoderTest extends ProtocolTest {
GoSafeProtocolDecoder decoder = new GoSafeProtocolDecoder(new GoSafeProtocol());
+ verifyPositions(decoder, text(
+ "*GS56,357330051092344,123918301116,10,GPS:L;9;N47.582920;W122.238720;0;0;102;0.99,GSM:0;0;310;410;A7DB;385C;-86,COT:76506,ADC:0.82;3.77,DTT:2184;;0;0;10000;0$000000000000,86,GPS:A;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:0.00;3.75,DTT:0;;0;0;40;0$000000000000,86,GPS:A;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:0.00;3.74,DTT:0;;0;0;40;0$000000000000,93,GPS:A;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:0.00;3.73,DTT:8000;;0;0;80000;0$000000000000,13,GPS:L;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:11.09;3.79,DTT:2004;;0;0;80000;0$000000000000,90,GPS:L;6;N47.582912;W122.238840;0;0;88;2.20,COT:76506,ADC:11.13;3.79,DTT:23004;;0;0;10000;0$000000000000,,GPS:L;6;N47.582912;W122.238840;0;0;88;2.20,GSM:5;2;310;410;A7DB;385C;-89,COT:76506,ADC:14.12;3.81,DTT:23184;;0;0;0;6#"));
+
+ verifyPositions(decoder, text(
+ "*GS26,356449061139936,022918011216,,SYS:G737IC;V1.13;V1.0.5,GPS:A;9;N42.651728;W70.623520;0;0;48;1.50,ADC:4.08,DTT:3900C;;0;0;0;1,#"));
+
verifyNotNull(decoder, text(
"*GS56,356449063230915,052339180916,,SYS:G7S;V1.08;V1.2,GPS:V;4;N24.730006;E46.637816;14;0;630,GSM:;;420;4;5655;507A;-70,COT:75242;2-8-17,ADC:13.22;0.08,DTT:23004;;0;0;0;1#"));
diff --git a/test/org/traccar/protocol/Gps103ProtocolDecoderTest.java b/test/org/traccar/protocol/Gps103ProtocolDecoderTest.java
index 2640051fb..93817b575 100644
--- a/test/org/traccar/protocol/Gps103ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Gps103ProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class Gps103ProtocolDecoderTest extends ProtocolTest {
Gps103ProtocolDecoder decoder = new Gps103ProtocolDecoder(new Gps103Protocol());
verifyAttributes(decoder, text(
+ "imei:862106021237716,ac alarm,1611291645,,F,204457.000,A,1010.2783,N,06441.0274,W,0.00,,;"));
+
+ verifyAttributes(decoder, text(
"imei:359710049057798,OBD,161003192752,1785,,,0,54,96.47%,75,20.00%,1892,0.00,P0134,P0571,,;"));
verifyAttributes(decoder, text(
diff --git a/test/org/traccar/protocol/GranitProtocolDecoderTest.java b/test/org/traccar/protocol/GranitProtocolDecoderTest.java
index 1e6a5e611..6e85b5cfc 100644
--- a/test/org/traccar/protocol/GranitProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/GranitProtocolDecoderTest.java
@@ -12,6 +12,23 @@ 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,
"2b525243427e1a003e2934757c57b8b03c38d279b4e61e9bd7006b000000001c00002a4533"));
@@ -21,13 +38,14 @@ 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/H02ProtocolDecoderTest.java b/test/org/traccar/protocol/H02ProtocolDecoderTest.java
index b2e336076..d1b2d3198 100644
--- a/test/org/traccar/protocol/H02ProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/H02ProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class H02ProtocolDecoderTest extends ProtocolTest {
H02ProtocolDecoder decoder = new H02ProtocolDecoder(new H02Protocol());
+ verifyPosition(decoder, buffer(
+ "*HQ,353588020068342,V1,084436,A,3257.01525,N,00655.03865,W,57.78,40,011216,FFFBFFFF,25c,a, 154,b04c#"));
+
verifyNothing(decoder, buffer(
"*HQ,356803210091319,BS,,2d4,a,1b63,1969,26,1b63,10b2,31,0,0,25,,ffffffff,60#"));
diff --git a/test/org/traccar/protocol/OigoProtocolDecoderTest.java b/test/org/traccar/protocol/OigoProtocolDecoderTest.java
index 14c34ae7c..452e40a78 100644
--- a/test/org/traccar/protocol/OigoProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/OigoProtocolDecoderTest.java
@@ -11,6 +11,21 @@ public class OigoProtocolDecoderTest extends ProtocolTest {
OigoProtocolDecoder decoder = new OigoProtocolDecoder(new OigoProtocol());
verifyPosition(decoder, binary(
+ "0103537820628365110310410790660962521813380026EE4EFF8593AA0065003E00794C020600100500000000"));
+
+ verifyPosition(decoder, binary(
+ "0E03537820628344660204043255862749531B100E0026EE3AFF8593A3FFFE00BF00044C20090710C300000000"));
+
+ verifyPosition(decoder, binary(
+ "00035378206638500203340201271426226b190203001ac000ff72eedd00370097238b4c34116a130b000094d9"));
+
+ verifyPosition(decoder, binary(
+ "1d035378206638500203340201271426226b19020c001ab144ff72f74d005f0097298a4c1d066d130b000094de"));
+
+ verifyPosition(decoder, binary(
+ "00035378206638500203340201271426226b191016001c04e5ff760081013d002900814c1a0f5e130b00009576"));
+
+ verifyPosition(decoder, binary(
"7e004200000014631000258257000000ffff02d0690e000220690e0002200696dbd204bdfde31a070000b307101135de106e05f500000000010908010402200104ffff8001"));
verifyPosition(decoder, binary(
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(
diff --git a/test/org/traccar/protocol/UproProtocolDecoderTest.java b/test/org/traccar/protocol/UproProtocolDecoderTest.java
index 3af62da08..270caeab5 100644
--- a/test/org/traccar/protocol/UproProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/UproProtocolDecoderTest.java
@@ -11,6 +11,9 @@ public class UproProtocolDecoderTest extends ProtocolTest {
UproProtocolDecoder decoder = new UproProtocolDecoder(new UproProtocol());
verifyPosition(decoder, binary(
+ "2a4d473230313836383530303032303030343836372c414226413035303032343138313438373536303636303131373732323030303031313132313626583331302c3236302c34383837322c353639312c37333b34383837322c3732322c38363b34383837322c353639332c38383b34383837322c323336332c39303b34383837322c323336322c393726423030303030303030303026573030264e3230265a31342659313430303323"));
+
+ verifyPosition(decoder, binary(
"2a4d473230303639333530323030303033353537332c42412641303834313237333332363334353230373033383933373630303030303235313131362642303130303030303030302647303036323030264d393930264e3235264f3035303026433030313a363b363926510411058c0c125c0d0a2fff4237ee614d66454c140826555f50000000000300000000000000000026543139333723"));
verifyPosition(decoder, buffer(
diff --git a/test/org/traccar/reports/TripsTest.java b/test/org/traccar/reports/TripsTest.java
new file mode 100644
index 000000000..7b860b63d
--- /dev/null
+++ b/test/org/traccar/reports/TripsTest.java
@@ -0,0 +1,69 @@
+package org.traccar.reports;
+
+import org.junit.Test;
+import org.traccar.model.Position;
+import org.traccar.reports.model.TripReport;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.TimeZone;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+public class TripsTest {
+
+ private Date date(String time) throws ParseException {
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return dateFormat.parse(time);
+ }
+
+ private Position position(String time, double speed, double totalDistance) throws ParseException {
+
+ Position position = new Position();
+
+ if (time != null) {
+ position.setTime(date(time));
+ }
+ position.setValid(true);
+ position.setSpeed(speed);
+ position.set(Position.KEY_TOTAL_DISTANCE, totalDistance);
+
+ return position;
+ }
+
+ @Test
+ public void testDetectTripsSimple() throws ParseException {
+
+ Collection<Position> data = Arrays.asList(
+ position("2016-01-01 00:00:00.000", 0, 0),
+ position("2016-01-01 00:01:00.000", 0, 0),
+ position("2016-01-01 00:02:00.000", 10, 0),
+ position("2016-01-01 00:03:00.000", 10, 1000),
+ position("2016-01-01 00:04:00.000", 10, 2000),
+ position("2016-01-01 00:05:00.000", 0, 3000),
+ position("2016-01-01 00:06:00.000", 0, 3000));
+
+ Collection<TripReport> result = Trips.detectTrips(0.01, 500, 300000, 300000, false, false, data);
+
+ assertNotNull(result);
+ assertFalse(result.isEmpty());
+
+ TripReport item = result.iterator().next();
+
+ assertEquals(date("2016-01-01 00:02:00.000"), item.getStartTime());
+ assertEquals(date("2016-01-01 00:05:00.000"), item.getEndTime());
+ assertEquals(180000, item.getDuration());
+ assertEquals(10, item.getAverageSpeed(), 0.01);
+ assertEquals(10, item.getMaxSpeed(), 0.01);
+ assertEquals(3000, item.getDistance(), 0.01);
+
+ }
+
+}
diff --git a/tools/test-integration.py b/tools/test-integration.py
index 4b1bca0cc..c3885095f 100755
--- a/tools/test-integration.py
+++ b/tools/test-integration.py
@@ -89,20 +89,20 @@ def login():
def remove_devices(cookie):
request = urllib2.Request(baseUrl + '/api/devices')
- request.add_header('cookie', cookie)
+ request.add_header('Cookie', cookie)
response = urllib2.urlopen(request)
data = json.load(response)
if debug:
print '\ndevices: %s\n' % repr(data)
for device in data:
request = urllib2.Request(baseUrl + '/api/devices/' + str(device['id']))
- request.add_header('cookie', cookie)
+ request.add_header('Cookie', cookie)
request.get_method = lambda: 'DELETE'
response = urllib2.urlopen(request)
def add_device(cookie, unique_id):
request = urllib2.Request(baseUrl + '/api/devices')
- request.add_header('cookie', cookie)
+ request.add_header('Cookie', cookie)
request.add_header('Content-Type', 'application/json')
device = { 'name' : unique_id, 'uniqueId' : unique_id }
response = urllib2.urlopen(request, json.dumps(device))
@@ -118,8 +118,9 @@ def send_message(port, message):
def get_protocols(cookie, device_id):
params = { 'deviceId' : device_id, 'from' : '2000-01-01T00:00:00.000Z', 'to' : '2050-01-01T00:00:00.000Z' }
request = urllib2.Request(baseUrl + '/api/positions?' + urllib.urlencode(params))
- request.add_header('cookie', cookie)
+ request.add_header('Cookie', cookie)
request.add_header('Content-Type', 'application/json')
+ request.add_header('Accept', 'application/json')
response = urllib2.urlopen(request)
protocols = []
for position in json.load(response):
diff --git a/traccar-web b/traccar-web
-Subproject 9dc4e2a38b872f4dd0ba51a821fc2052ab9323f
+Subproject d1fc289c4f57c2f0ea6865c92d6551aa828b2af