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