diff options
Diffstat (limited to 'src')
26 files changed, 825 insertions, 240 deletions
diff --git a/src/org/traccar/BaseHttpProtocolDecoder.java b/src/org/traccar/BaseHttpProtocolDecoder.java new file mode 100644 index 000000000..934a1b81e --- /dev/null +++ b/src/org/traccar/BaseHttpProtocolDecoder.java @@ -0,0 +1,39 @@ +/* + * 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; + +import org.jboss.netty.channel.Channel; +import org.jboss.netty.handler.codec.http.DefaultHttpResponse; +import org.jboss.netty.handler.codec.http.HttpHeaders; +import org.jboss.netty.handler.codec.http.HttpResponse; +import org.jboss.netty.handler.codec.http.HttpResponseStatus; +import org.jboss.netty.handler.codec.http.HttpVersion; + +public abstract class BaseHttpProtocolDecoder extends BaseProtocolDecoder { + + public BaseHttpProtocolDecoder(Protocol protocol) { + super(protocol); + } + + public void sendResponse(Channel channel, HttpResponseStatus status) { + if (channel != null) { + HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status); + response.headers().add(HttpHeaders.Names.CONTENT_LENGTH, 0); + channel.write(response); + } + } + +} diff --git a/src/org/traccar/Context.java b/src/org/traccar/Context.java index 511ec980e..3681cc2cc 100644 --- a/src/org/traccar/Context.java +++ b/src/org/traccar/Context.java @@ -71,6 +71,8 @@ import org.traccar.geolocation.GeolocationProvider; import org.traccar.geolocation.MozillaGeolocationProvider; import org.traccar.geolocation.OpenCellIdGeolocationProvider; import org.traccar.notification.EventForwarder; +import org.traccar.notification.JsonTypeEventForwarder; +import org.traccar.notification.MultiPartEventForwarder; import org.traccar.reports.model.TripsConfig; import org.traccar.smpp.SmppClient; import org.traccar.web.WebServer; @@ -323,6 +325,9 @@ public final class Context { objectMapper = new ObjectMapper(); objectMapper.setConfig( objectMapper.getSerializationConfig().without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)); + if (Context.getConfig().getBoolean("mapper.prettyPrintedJson")) { + objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + } if (config.hasKey("database.url")) { dataManager = new DataManager(config); @@ -349,24 +354,7 @@ public final class Context { } if (config.getBoolean("geolocation.enable")) { - String type = config.getString("geolocation.type", "mozilla"); - String url = config.getString("geolocation.url"); - String key = config.getString("geolocation.key"); - - switch (type) { - case "google": - geolocationProvider = new GoogleGeolocationProvider(key); - break; - case "opencellid": - geolocationProvider = new OpenCellIdGeolocationProvider(key); - break; - case "unwired": - geolocationProvider = new UnwiredGeolocationProvider(url, key); - break; - default: - geolocationProvider = new MozillaGeolocationProvider(key); - break; - } + initGeolocationModule(); } if (config.getBoolean("web.enable")) { @@ -380,39 +368,17 @@ public final class Context { tripsConfig = initTripsConfig(); if (config.getBoolean("event.enable")) { - geofenceManager = new GeofenceManager(dataManager); - calendarManager = new CalendarManager(dataManager); - notificationManager = new NotificationManager(dataManager); - Properties velocityProperties = new Properties(); - velocityProperties.setProperty("file.resource.loader.path", - Context.getConfig().getString("templates.rootPath", "templates") + "/"); - velocityProperties.setProperty("runtime.log.logsystem.class", - "org.apache.velocity.runtime.log.NullLogChute"); - - String address; - try { - address = config.getString("web.address", InetAddress.getLocalHost().getHostAddress()); - } catch (UnknownHostException e) { - address = "localhost"; - } - - String webUrl = URIUtil.newURI("http", address, config.getInteger("web.port", 8082), "", ""); - webUrl = Context.getConfig().getString("web.url", webUrl); - velocityProperties.setProperty("web.url", webUrl); - - velocityEngine = new VelocityEngine(); - velocityEngine.init(velocityProperties); - - motionEventHandler = new MotionEventHandler(tripsConfig); - overspeedEventHandler = new OverspeedEventHandler( - Context.getConfig().getLong("event.overspeed.minimalDuration") * 1000, - Context.getConfig().getBoolean("event.overspeed.notRepeat")); + initEventsModule(); } serverManager = new ServerManager(); if (config.getBoolean("event.forward.enable")) { - eventForwarder = new EventForwarder(); + if (Context.getConfig().getBoolean("event.forward.payloadAsParamMode")) { + eventForwarder = new MultiPartEventForwarder(); + } else { + eventForwarder = new JsonTypeEventForwarder(); + } } attributesManager = new AttributesManager(dataManager); @@ -429,6 +395,59 @@ public final class Context { } + private static void initGeolocationModule() { + + String type = config.getString("geolocation.type", "mozilla"); + String url = config.getString("geolocation.url"); + String key = config.getString("geolocation.key"); + + switch (type) { + case "google": + geolocationProvider = new GoogleGeolocationProvider(key); + break; + case "opencellid": + geolocationProvider = new OpenCellIdGeolocationProvider(key); + break; + case "unwired": + geolocationProvider = new UnwiredGeolocationProvider(url, key); + break; + default: + geolocationProvider = new MozillaGeolocationProvider(key); + break; + } + } + + private static void initEventsModule() { + + geofenceManager = new GeofenceManager(dataManager); + calendarManager = new CalendarManager(dataManager); + notificationManager = new NotificationManager(dataManager); + Properties velocityProperties = new Properties(); + velocityProperties.setProperty("file.resource.loader.path", + Context.getConfig().getString("templates.rootPath", "templates") + "/"); + velocityProperties.setProperty("runtime.log.logsystem.class", + "org.apache.velocity.runtime.log.NullLogChute"); + + String address; + try { + address = config.getString("web.address", InetAddress.getLocalHost().getHostAddress()); + } catch (UnknownHostException e) { + address = "localhost"; + } + + String webUrl = URIUtil.newURI("http", address, config.getInteger("web.port", 8082), "", ""); + webUrl = Context.getConfig().getString("web.url", webUrl); + velocityProperties.setProperty("web.url", webUrl); + + velocityEngine = new VelocityEngine(); + velocityEngine.init(velocityProperties); + + motionEventHandler = new MotionEventHandler(tripsConfig); + overspeedEventHandler = new OverspeedEventHandler( + Context.getConfig().getLong("event.overspeed.minimalDuration") * 1000, + Context.getConfig().getBoolean("event.overspeed.notRepeat")); + } + public static void init(IdentityManager testIdentityManager) { config = new Config(); objectMapper = new ObjectMapper(); diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java index abc424ff9..f813426d3 100644 --- a/src/org/traccar/database/DataManager.java +++ b/src/org/traccar/database/DataManager.java @@ -307,7 +307,7 @@ public class DataManager { .executeQuerySingle(User.class); LdapProvider ldapProvider = Context.getLdapProvider(); if (user != null) { - if (ldapProvider != null && ldapProvider.login(user.getLogin(), password) + if (ldapProvider != null && user.getLogin() != null && ldapProvider.login(user.getLogin(), password) || !forceLdap && user.isPasswordValid(password)) { return user; } diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java index 07b60ba58..0e9097d25 100644 --- a/src/org/traccar/database/PermissionsManager.java +++ b/src/org/traccar/database/PermissionsManager.java @@ -255,7 +255,8 @@ public class PermissionsManager { } if (before.getReadonly() != after.getReadonly() || before.getDeviceReadonly() != after.getDeviceReadonly() - || before.getDisabled() != after.getDisabled()) { + || before.getDisabled() != after.getDisabled() + || before.getLimitCommands() != after.getLimitCommands()) { if (userId == after.getId()) { checkAdmin(userId); } diff --git a/src/org/traccar/notification/EventForwarder.java b/src/org/traccar/notification/EventForwarder.java index ac37f980c..68c3ea221 100644 --- a/src/org/traccar/notification/EventForwarder.java +++ b/src/org/traccar/notification/EventForwarder.java @@ -17,6 +17,9 @@ package org.traccar.notification; import com.fasterxml.jackson.core.JsonProcessingException; import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder; +import java.util.Arrays; +import java.util.List; + import org.traccar.Context; import org.traccar.helper.Log; import org.traccar.model.Device; @@ -27,15 +30,16 @@ import org.traccar.model.Position; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; +import com.ning.http.client.FluentCaseInsensitiveStringsMap; -public final class EventForwarder { +public abstract class EventForwarder { - private String url; - private String header; + private final String url; + private final String header; public EventForwarder() { url = Context.getConfig().getString("event.forward.url", "http://localhost/"); - header = Context.getConfig().getString("event.forward.header", ""); + header = Context.getConfig().getString("event.forward.header"); } private static final String KEY_POSITION = "position"; @@ -43,26 +47,39 @@ public final class EventForwarder { private static final String KEY_GEOFENCE = "geofence"; private static final String KEY_DEVICE = "device"; - public void forwardEvent(Event event, Position position) { + public final void forwardEvent(Event event, Position position) { BoundRequestBuilder requestBuilder = Context.getAsyncHttpClient().preparePost(url); + requestBuilder.setBodyEncoding(StandardCharsets.UTF_8.name()); - requestBuilder.addHeader("Content-Type", "application/json; charset=utf-8"); - if (!header.equals("")) { - String[] headerLines = header.split("\\r?\\n"); - for (String headerLine: headerLines) { - String[] splitedLine = headerLine.split(":", 2); - if (splitedLine.length == 2) { - requestBuilder.setHeader(splitedLine[0].trim(), splitedLine[1].trim()); - } - } + requestBuilder.addHeader("Content-Type", getContentType()); + + if (header != null && !header.isEmpty()) { + FluentCaseInsensitiveStringsMap params = new FluentCaseInsensitiveStringsMap(); + params.putAll(splitIntoKeyValues(header, ":")); + requestBuilder.setHeaders(params); } - requestBuilder.setBody(preparePayload(event, position)); + setContent(event, position, requestBuilder); requestBuilder.execute(); } - private byte[] preparePayload(Event event, Position position) { + protected Map<String, List<String>> splitIntoKeyValues(String params, String separator) { + + String[] splitedLine; + Map<String, List<String>> paramsMap = new HashMap<>(); + String[] paramsLines = params.split("\\r?\\n"); + + for (String paramLine: paramsLines) { + splitedLine = paramLine.split(separator, 2); + if (splitedLine.length == 2) { + paramsMap.put(splitedLine[0].trim(), Arrays.asList(splitedLine[1].trim())); + } + } + return paramsMap; + } + + protected String prepareJsonPayload(Event event, Position position) { Map<String, Object> data = new HashMap<>(); data.put(KEY_EVENT, event); if (position != null) { @@ -81,11 +98,14 @@ public final class EventForwarder { } } try { - return Context.getObjectMapper().writeValueAsString(data).getBytes(StandardCharsets.UTF_8); + return Context.getObjectMapper().writeValueAsString(data); } catch (JsonProcessingException e) { Log.warning(e); return null; } } + protected abstract String getContentType(); + protected abstract void setContent(Event event, Position position, BoundRequestBuilder requestBuilder); + } diff --git a/src/org/traccar/notification/JsonTypeEventForwarder.java b/src/org/traccar/notification/JsonTypeEventForwarder.java new file mode 100644 index 000000000..c1e4220d0 --- /dev/null +++ b/src/org/traccar/notification/JsonTypeEventForwarder.java @@ -0,0 +1,20 @@ +package org.traccar.notification;
+
+import org.traccar.model.Event;
+import org.traccar.model.Position;
+
+import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder;
+
+public class JsonTypeEventForwarder extends EventForwarder {
+
+ @Override
+ protected String getContentType() {
+ return "application/json; charset=utf-8";
+ }
+
+ @Override
+ protected void setContent(Event event, Position position, BoundRequestBuilder requestBuilder) {
+ requestBuilder.setBody(prepareJsonPayload(event, position));
+ }
+
+}
diff --git a/src/org/traccar/notification/MultiPartEventForwarder.java b/src/org/traccar/notification/MultiPartEventForwarder.java new file mode 100644 index 000000000..f4c36d3e4 --- /dev/null +++ b/src/org/traccar/notification/MultiPartEventForwarder.java @@ -0,0 +1,46 @@ +package org.traccar.notification; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.traccar.Context; +import org.traccar.model.Event; +import org.traccar.model.Position; + +import com.ning.http.client.AsyncHttpClient.BoundRequestBuilder; +import com.ning.http.client.multipart.StringPart; + +public class MultiPartEventForwarder extends EventForwarder { + + private final String payloadParamName; + private final String additionalParams; + + public MultiPartEventForwarder() { + payloadParamName = Context.getConfig().getString("event.forward.paramMode.payloadParamName", "payload"); + additionalParams = Context.getConfig().getString("event.forward.paramMode.additionalParams"); + } + + @Override + protected String getContentType() { + return "multipart/form-data"; + } + + @Override + protected void setContent(Event event, Position position, BoundRequestBuilder requestBuilder) { + + if (additionalParams != null && !additionalParams.isEmpty()) { + Map<String, List<String>> paramsToAdd = splitIntoKeyValues(additionalParams, "="); + + for (Entry<String, List<String>> param : paramsToAdd.entrySet()) { + for (String singleParamValue : param.getValue()) { + requestBuilder.addBodyPart(new StringPart(param.getKey(), singleParamValue, null, + StandardCharsets.UTF_8)); + } + } + } + requestBuilder.addBodyPart(new StringPart(payloadParamName, + prepareJsonPayload(event, position), "application/json", StandardCharsets.UTF_8)); + } +} diff --git a/src/org/traccar/notification/NotificationFormatter.java b/src/org/traccar/notification/NotificationFormatter.java index 8da819430..114825a83 100644 --- a/src/org/traccar/notification/NotificationFormatter.java +++ b/src/org/traccar/notification/NotificationFormatter.java @@ -18,6 +18,7 @@ package org.traccar.notification; import java.io.StringWriter; import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; import java.util.Locale; import org.apache.velocity.Template; @@ -39,6 +40,7 @@ public final class NotificationFormatter { } public static VelocityContext prepareContext(long userId, Event event, Position position) { + User user = Context.getPermissionsManager().getUser(userId); Device device = Context.getIdentityManager().getById(event.getDeviceId()); @@ -68,31 +70,46 @@ public final class NotificationFormatter { } public static Template getTemplate(Event event, String path) { + + String templateFilePath; Template template; + try { - template = Context.getVelocityEngine().getTemplate(path + event.getType() + ".vm", - StandardCharsets.UTF_8.name()); + templateFilePath = Paths.get(path, event.getType() + ".vm").toString(); + template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); } catch (ResourceNotFoundException error) { Log.warning(error); - template = Context.getVelocityEngine().getTemplate(path + "unknown.vm", - StandardCharsets.UTF_8.name()); + templateFilePath = Paths.get(path, "unknown.vm").toString(); + template = Context.getVelocityEngine().getTemplate(templateFilePath, StandardCharsets.UTF_8.name()); } return template; } public static MailMessage formatMailMessage(long userId, Event event, Position position) { + String templatePath = Context.getConfig().getString("mail.templatesPath", "mail"); VelocityContext velocityContext = prepareContext(userId, event, position); - StringWriter writer = new StringWriter(); - getTemplate(event, Context.getConfig().getString("mail.templatesPath", "mail") + "/") - .merge(velocityContext, writer); - return new MailMessage((String) velocityContext.get("subject"), writer.toString()); + String formattedMessage = formatMessage(velocityContext, userId, event, position, templatePath); + + return new MailMessage((String) velocityContext.get("subject"), formattedMessage); } public static String formatSmsMessage(long userId, Event event, Position position) { - VelocityContext velocityContext = prepareContext(userId, event, position); + String templatePath = Context.getConfig().getString("sms.templatesPath", "sms"); + + return formatMessage(null, userId, event, position, templatePath); + } + + private static String formatMessage(VelocityContext vc, Long userId, Event event, Position position, + String templatePath) { + + VelocityContext velocityContext = vc; + if (velocityContext == null) { + velocityContext = prepareContext(userId, event, position); + } StringWriter writer = new StringWriter(); - getTemplate(event, Context.getConfig().getString("sms.templatesPath", "sms") + "/") - .merge(velocityContext, writer); + getTemplate(event, templatePath).merge(velocityContext, writer); + return writer.toString(); } + } diff --git a/src/org/traccar/protocol/DmtHttpProtocolDecoder.java b/src/org/traccar/protocol/DmtHttpProtocolDecoder.java index 1bd808a1f..dbcc7a6f8 100644 --- a/src/org/traccar/protocol/DmtHttpProtocolDecoder.java +++ b/src/org/traccar/protocol/DmtHttpProtocolDecoder.java @@ -16,13 +16,9 @@ package org.traccar.protocol; import org.jboss.netty.channel.Channel; -import org.jboss.netty.handler.codec.http.DefaultHttpResponse; -import org.jboss.netty.handler.codec.http.HttpHeaders; import org.jboss.netty.handler.codec.http.HttpRequest; -import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseStatus; -import org.jboss.netty.handler.codec.http.HttpVersion; -import org.traccar.BaseProtocolDecoder; +import org.traccar.BaseHttpProtocolDecoder; import org.traccar.DeviceSession; import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; @@ -40,20 +36,12 @@ import java.util.LinkedList; import java.util.List; import java.util.TimeZone; -public class DmtHttpProtocolDecoder extends BaseProtocolDecoder { +public class DmtHttpProtocolDecoder extends BaseHttpProtocolDecoder { public DmtHttpProtocolDecoder(DmtHttpProtocol protocol) { super(protocol); } - private void sendResponse(Channel channel, HttpResponseStatus status) { - if (channel != null) { - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status); - response.headers().add(HttpHeaders.Names.CONTENT_LENGTH, 0); - channel.write(response); - } - } - @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { diff --git a/src/org/traccar/protocol/EelinkProtocol.java b/src/org/traccar/protocol/EelinkProtocol.java index 5499094d9..3d0a006de 100644 --- a/src/org/traccar/protocol/EelinkProtocol.java +++ b/src/org/traccar/protocol/EelinkProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package org.traccar.protocol; +import org.jboss.netty.bootstrap.ConnectionlessBootstrap; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.handler.codec.frame.LengthFieldBasedFrameDecoder; @@ -30,6 +31,7 @@ public class EelinkProtocol extends BaseProtocol { super("eelink"); setSupportedDataCommands( Command.TYPE_CUSTOM, + Command.TYPE_POSITION_SINGLE, Command.TYPE_ENGINE_STOP, Command.TYPE_ENGINE_RESUME, Command.TYPE_REBOOT_DEVICE); @@ -41,7 +43,14 @@ public class EelinkProtocol extends BaseProtocol { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 3, 2)); - pipeline.addLast("objectEncoder", new EelinkProtocolEncoder()); + pipeline.addLast("objectEncoder", new EelinkProtocolEncoder(false)); + pipeline.addLast("objectDecoder", new EelinkProtocolDecoder(EelinkProtocol.this)); + } + }); + serverList.add(new TrackerServer(new ConnectionlessBootstrap(), getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("objectEncoder", new EelinkProtocolEncoder(true)); pipeline.addLast("objectDecoder", new EelinkProtocolDecoder(EelinkProtocol.this)); } }); diff --git a/src/org/traccar/protocol/EelinkProtocolDecoder.java b/src/org/traccar/protocol/EelinkProtocolDecoder.java index 8d0f8016a..98a9f7d6d 100644 --- a/src/org/traccar/protocol/EelinkProtocolDecoder.java +++ b/src/org/traccar/protocol/EelinkProtocolDecoder.java @@ -18,16 +18,21 @@ package org.traccar.protocol; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.socket.DatagramChannel; import org.traccar.BaseProtocolDecoder; import org.traccar.DeviceSession; import org.traccar.helper.BitUtil; +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; 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 EelinkProtocolDecoder extends BaseProtocolDecoder { @@ -55,14 +60,10 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_CAMERA_INFO = 0x1E; public static final int MSG_CAMERA_DATA = 0x1F; - private void sendResponse(Channel channel, int type, int index) { + private void sendResponse(Channel channel, SocketAddress remoteAddress, String uniqueId, int type, int index) { if (channel != null) { - ChannelBuffer response = ChannelBuffers.buffer(7); - response.writeByte(0x67); response.writeByte(0x67); // header - response.writeByte(type); - response.writeShort(2); // length - response.writeShort(index); - channel.write(response); + channel.write(EelinkProtocolEncoder.encodeContent( + channel instanceof DatagramChannel, uniqueId, type, index, null), remoteAddress); } } @@ -189,6 +190,8 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort())); position.setCourse(buf.readUnsignedShort()); position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + } else { + getLastLocation(position, position.getDeviceTime()); } if (BitUtil.check(flags, 1)) { @@ -259,36 +262,104 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { return position; } + private static final Pattern PATTERN = new PatternBuilder() + .text("Lat:") + .number("([NS])(d+.d+)") // latitude + .any() + .text("Lon:") + .number("([EW])(d+.d+)") // longitude + .any() + .text("Course:") + .number("(d+.d+)") // course + .any() + .text("Speed:") + .number("(d+.d+)") // speed + .any() + .expression("Date ?Time:") + .number("(dddd)-(dd)-(dd) ") // date + .number("(dd):(dd):(dd)") // time + .compile(); + + private Position decodeResult(DeviceSession deviceSession, ChannelBuffer buf, int index) { + + Position position = new Position(); + position.setDeviceId(deviceSession.getDeviceId()); + position.setProtocol(getProtocolName()); + + position.set(Position.KEY_INDEX, index); + + buf.readUnsignedByte(); // type + buf.readUnsignedInt(); // uid + + String sentence = buf.toString(StandardCharsets.UTF_8); + + Parser parser = new Parser(PATTERN, sentence); + if (parser.matches()) { + + position.setValid(true); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG)); + position.setCourse(parser.nextDouble()); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setTime(parser.nextDateTime()); + + } else { + + getLastLocation(position, null); + + position.set(Position.KEY_RESULT, sentence); + + } + + return position; + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; + String uniqueId = null; + DeviceSession deviceSession; + + if (buf.getByte(0) == 'E' && buf.getByte(1) == 'L') { + buf.skipBytes(2 + 2 + 2); // udp header + uniqueId = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1); + deviceSession = getDeviceSession(channel, remoteAddress, uniqueId); + } else { + deviceSession = getDeviceSession(channel, remoteAddress); + } + buf.skipBytes(2); // header int type = buf.readUnsignedByte(); buf.readShort(); // length int index = buf.readUnsignedShort(); if (type != MSG_GPS && type != MSG_DATA) { - sendResponse(channel, type, index); + sendResponse(channel, remoteAddress, uniqueId, type, index); } if (type == MSG_LOGIN) { - getDeviceSession(channel, remoteAddress, ChannelBuffers.hexDump(buf.readBytes(8)).substring(1)); + if (deviceSession == null) { + getDeviceSession(channel, remoteAddress, ChannelBuffers.hexDump(buf.readBytes(8)).substring(1)); + } } else { - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); if (deviceSession == null) { return null; } if (type == MSG_GPS || type == MSG_ALARM || type == MSG_STATE || type == MSG_SMS) { + return decodeOld(deviceSession, buf, type, index); + } else if (type >= MSG_NORMAL && type <= MSG_OBD_CODE) { + return decodeNew(deviceSession, buf, index); + } else if (type == MSG_HEARTBEAT && buf.readableBytes() >= 2) { Position position = new Position(); @@ -301,6 +372,10 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { return position; + } else if (type == MSG_DOWNLINK) { + + return decodeResult(deviceSession, buf, index); + } } diff --git a/src/org/traccar/protocol/EelinkProtocolEncoder.java b/src/org/traccar/protocol/EelinkProtocolEncoder.java index 5a28733f7..76865a039 100644 --- a/src/org/traccar/protocol/EelinkProtocolEncoder.java +++ b/src/org/traccar/protocol/EelinkProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,25 +21,68 @@ import org.traccar.BaseProtocolEncoder; import org.traccar.helper.Log; import org.traccar.model.Command; +import javax.xml.bind.DatatypeConverter; +import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; public class EelinkProtocolEncoder extends BaseProtocolEncoder { - private ChannelBuffer encodeContent(String content) { + private boolean connectionless; + + public EelinkProtocolEncoder(boolean connectionless) { + this.connectionless = connectionless; + } + + public static int checksum(ByteBuffer buf) { + int sum = 0; + while (buf.hasRemaining()) { + sum = (((sum << 1) | (sum >> 15)) + (buf.get() & 0xFF)) & 0xFFFF; + } + return sum; + } + + public static ChannelBuffer encodeContent( + boolean connectionless, String uniqueId, int type, int index, ChannelBuffer content) { ChannelBuffer buf = ChannelBuffers.dynamicBuffer(); + if (connectionless) { + buf.writeBytes(ChannelBuffers.wrappedBuffer(DatatypeConverter.parseHexBinary('0' + uniqueId))); + } + buf.writeByte(0x67); buf.writeByte(0x67); - buf.writeByte(EelinkProtocolDecoder.MSG_DOWNLINK); - buf.writeShort(2 + 1 + 4 + content.length()); // length - buf.writeShort(0); // index + buf.writeByte(type); + buf.writeShort(2 + (content != null ? content.readableBytes() : 0)); // length + buf.writeShort(index); + + if (content != null) { + buf.writeBytes(content); + } + + ChannelBuffer result = ChannelBuffers.dynamicBuffer(); + + if (connectionless) { + result.writeByte('E'); + result.writeByte('L'); + result.writeShort(2 + buf.readableBytes()); // length + result.writeShort(checksum(buf.toByteBuffer())); + } + + result.writeBytes(buf); + + return result; + } + + private ChannelBuffer encodeContent(long deviceId, String content) { + + ChannelBuffer buf = ChannelBuffers.dynamicBuffer(); buf.writeByte(0x01); // command buf.writeInt(0); // server id buf.writeBytes(content.getBytes(StandardCharsets.UTF_8)); - return buf; + return encodeContent(connectionless, getUniqueId(deviceId), EelinkProtocolDecoder.MSG_DOWNLINK, 0, buf); } @Override @@ -47,13 +90,15 @@ public class EelinkProtocolEncoder extends BaseProtocolEncoder { switch (command.getType()) { case Command.TYPE_CUSTOM: - return encodeContent(command.getString(Command.KEY_DATA)); + return encodeContent(command.getDeviceId(), command.getString(Command.KEY_DATA)); + case Command.TYPE_POSITION_SINGLE: + return encodeContent(command.getDeviceId(), "WHERE#"); case Command.TYPE_ENGINE_STOP: - return encodeContent("RELAY,1#"); + return encodeContent(command.getDeviceId(), "RELAY,1#"); case Command.TYPE_ENGINE_RESUME: - return encodeContent("RELAY,0#"); + return encodeContent(command.getDeviceId(), "RELAY,0#"); case Command.TYPE_REBOOT_DEVICE: - return encodeContent("RESET#"); + return encodeContent(command.getDeviceId(), "RESET#"); default: Log.warning(new UnsupportedOperationException(command.getType())); break; diff --git a/src/org/traccar/protocol/FlespiProtocolDecoder.java b/src/org/traccar/protocol/FlespiProtocolDecoder.java index 799d78ecb..d122df383 100644 --- a/src/org/traccar/protocol/FlespiProtocolDecoder.java +++ b/src/org/traccar/protocol/FlespiProtocolDecoder.java @@ -16,13 +16,9 @@ package org.traccar.protocol; import org.jboss.netty.channel.Channel; -import org.jboss.netty.handler.codec.http.DefaultHttpResponse; import org.jboss.netty.handler.codec.http.HttpRequest; -import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseStatus; -import org.jboss.netty.handler.codec.http.HttpVersion; -import org.jboss.netty.handler.codec.http.HttpHeaders; -import org.traccar.BaseProtocolDecoder; +import org.traccar.BaseHttpProtocolDecoder; import org.traccar.DeviceSession; import org.traccar.model.Position; @@ -34,11 +30,11 @@ import javax.json.JsonString; import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.LinkedList; import java.util.Date; +import java.util.LinkedList; +import java.util.List; -public class FlespiProtocolDecoder extends BaseProtocolDecoder { +public class FlespiProtocolDecoder extends BaseHttpProtocolDecoder { public FlespiProtocolDecoder(FlespiProtocol protocol) { super(protocol); @@ -72,14 +68,6 @@ public class FlespiProtocolDecoder extends BaseProtocolDecoder { return positions; } - private void sendResponse(Channel channel, HttpResponseStatus status) { - if (channel != null) { - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status); - response.headers().add(HttpHeaders.Names.CONTENT_LENGTH, 0); - channel.write(response); - } - } - private void decodePosition(JsonObject object, Position position) { position.setProtocol(getProtocolName()); diff --git a/src/org/traccar/protocol/GoSafeProtocolDecoder.java b/src/org/traccar/protocol/GoSafeProtocolDecoder.java index f9aaae0ce..62f6212e2 100644 --- a/src/org/traccar/protocol/GoSafeProtocolDecoder.java +++ b/src/org/traccar/protocol/GoSafeProtocolDecoder.java @@ -168,10 +168,18 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_BATTERY, parser.nextDouble()); if (parser.hasNext(6)) { - long status = parser.nextLong(16, 0); - position.set(Position.KEY_IGNITION, BitUtil.check(status, 13)); - position.set(Position.KEY_STATUS, status); - position.set("ioStatus", parser.next()); + position.set(Position.KEY_STATUS, parser.nextHexLong()); + Integer io = parser.nextHexInt(); + if (io != null) { + position.set(Position.KEY_IGNITION, BitUtil.check(io, 0)); + position.set(Position.PREFIX_IN + 1, BitUtil.check(io, 1)); + position.set(Position.PREFIX_IN + 2, BitUtil.check(io, 2)); + position.set(Position.PREFIX_IN + 3, BitUtil.check(io, 3)); + position.set(Position.PREFIX_IN + 4, BitUtil.check(io, 4)); + position.set(Position.PREFIX_OUT + 1, BitUtil.check(io, 5)); + position.set(Position.PREFIX_OUT + 2, BitUtil.check(io, 6)); + position.set(Position.PREFIX_OUT + 3, BitUtil.check(io, 7)); + } position.set(Position.KEY_GEOFENCE, parser.next() + parser.next()); position.set("eventStatus", parser.next()); position.set("packetType", parser.next()); diff --git a/src/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/org/traccar/protocol/Gt06ProtocolDecoder.java index 177c0b653..2018fb6a6 100644 --- a/src/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -36,6 +36,7 @@ import org.traccar.model.WifiAccessPoint; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; +import java.util.Calendar; import java.util.HashMap; import java.util.Map; import java.util.TimeZone; @@ -78,6 +79,9 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_LBS_STATUS = 0x19; public static final int MSG_GPS_PHONE = 0x1A; public static final int MSG_GPS_LBS_EXTEND = 0x1E; + public static final int MSG_HEARTBEAT = 0x23; + public static final int MSG_ADDRESS_REQUEST = 0x2A; + public static final int MSG_ADDRESS_RESPONSE = 0x97; public static final int MSG_AZ735_GPS = 0x32; public static final int MSG_AZ735_ALARM = 0x33; public static final int MSG_X1_GPS = 0x34; @@ -87,27 +91,69 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_COMMAND_0 = 0x80; public static final int MSG_COMMAND_1 = 0x81; public static final int MSG_COMMAND_2 = 0x82; + public static final int MSG_TIME_REQUEST = 0x8A; public static final int MSG_INFO = 0x94; public static final int MSG_STRING_INFO = 0x21; + public static final int MSG_GPS_2 = 0xA0; + public static final int MSG_LBS_2 = 0xA1; + public static final int MSG_WIFI_3 = 0xA2; + public static final int MSG_FENCE_SINGLE = 0xA3; + public static final int MSG_FENCE_MULTI = 0xA4; + public static final int MSG_LBS_ALARM = 0xA5; + public static final int MSG_LBS_ADDRESS = 0xA7; private static boolean isSupported(int type) { return hasGps(type) || hasLbs(type) || hasStatus(type); } private static boolean hasGps(int type) { - return type == MSG_GPS || type == MSG_GPS_LBS_1 || type == MSG_GPS_LBS_2 - || type == MSG_GPS_LBS_STATUS_1 || type == MSG_GPS_LBS_STATUS_2 || type == MSG_GPS_LBS_STATUS_3 - || type == MSG_GPS_PHONE || type == MSG_GPS_LBS_EXTEND; + switch (type) { + case MSG_GPS: + case MSG_GPS_LBS_1: + case MSG_GPS_LBS_2: + case MSG_GPS_LBS_STATUS_1: + case MSG_GPS_LBS_STATUS_2: + case MSG_GPS_LBS_STATUS_3: + case MSG_GPS_PHONE: + case MSG_GPS_LBS_EXTEND: + case MSG_GPS_2: + case MSG_FENCE_SINGLE: + return true; + default: + return false; + } } private static boolean hasLbs(int type) { - return type == MSG_LBS || type == MSG_LBS_STATUS || type == MSG_GPS_LBS_1 || type == MSG_GPS_LBS_2 - || type == MSG_GPS_LBS_STATUS_1 || type == MSG_GPS_LBS_STATUS_2 || type == MSG_GPS_LBS_STATUS_3; + switch (type) { + case MSG_LBS: + case MSG_LBS_STATUS: + case MSG_GPS_LBS_1: + case MSG_GPS_LBS_2: + case MSG_GPS_LBS_STATUS_1: + case MSG_GPS_LBS_STATUS_2: + case MSG_GPS_LBS_STATUS_3: + case MSG_GPS_2: + case MSG_FENCE_SINGLE: + case MSG_LBS_ALARM: + case MSG_LBS_ADDRESS: + return true; + default: + return false; + } } private static boolean hasStatus(int type) { - return type == MSG_STATUS || type == MSG_LBS_STATUS - || type == MSG_GPS_LBS_STATUS_1 || type == MSG_GPS_LBS_STATUS_2 || type == MSG_GPS_LBS_STATUS_3; + switch (type) { + case MSG_STATUS: + case MSG_LBS_STATUS: + case MSG_GPS_LBS_STATUS_1: + case MSG_GPS_LBS_STATUS_2: + case MSG_GPS_LBS_STATUS_3: + return true; + default: + return false; + } } private void sendResponse(Channel channel, boolean extended, int type, ChannelBuffer content) { @@ -348,6 +394,44 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { sendResponse(channel, false, type, null); } + } else if (type == MSG_HEARTBEAT) { + + Position position = new Position(); + position.setDeviceId(deviceSession.getDeviceId()); + position.setProtocol(getProtocolName()); + + getLastLocation(position, null); + + int status = buf.readUnsignedByte(); + position.set(Position.KEY_ARMED, BitUtil.check(status, 0)); + position.set(Position.KEY_IGNITION, BitUtil.check(status, 1)); + position.set(Position.KEY_CHARGE, BitUtil.check(status, 2)); + + sendResponse(channel, false, type, null); + + return position; + + } else if (type == MSG_ADDRESS_REQUEST) { + + String response = "NA&&NA&&0##"; + ChannelBuffer content = ChannelBuffers.dynamicBuffer(); + content.writeByte(response.length()); + content.writeInt(0); + content.writeBytes(response.getBytes(StandardCharsets.US_ASCII)); + sendResponse(channel, true, MSG_ADDRESS_RESPONSE, content); + + } else if (type == MSG_TIME_REQUEST) { + + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + ChannelBuffer content = ChannelBuffers.dynamicBuffer(); + content.writeByte(calendar.get(Calendar.YEAR) - 2000); + content.writeByte(calendar.get(Calendar.MONTH) + 1); + content.writeByte(calendar.get(Calendar.DAY_OF_MONTH)); + content.writeByte(calendar.get(Calendar.HOUR_OF_DAY)); + content.writeByte(calendar.get(Calendar.MINUTE)); + content.writeByte(calendar.get(Calendar.SECOND)); + sendResponse(channel, false, MSG_TIME_REQUEST, content); + } else if (type == MSG_X1_GPS) { Position position = new Position(); @@ -385,48 +469,53 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } else if (type == MSG_WIFI || type == MSG_WIFI_2) { - Position position = new Position(); - position.setDeviceId(deviceSession.getDeviceId()); - position.setProtocol(getProtocolName()); - - DateBuilder dateBuilder = new DateBuilder() - .setYear(BcdUtil.readInteger(buf, 2)) - .setMonth(BcdUtil.readInteger(buf, 2)) - .setDay(BcdUtil.readInteger(buf, 2)) - .setHour(BcdUtil.readInteger(buf, 2)) - .setMinute(BcdUtil.readInteger(buf, 2)) - .setSecond(BcdUtil.readInteger(buf, 2)); - getLastLocation(position, dateBuilder.getDate()); + return decodeWifi(buf, deviceSession); - Network network = new Network(); + } else { - int wifiCount = buf.getByte(2); - for (int i = 0; i < wifiCount; i++) { - String mac = String.format("%02x:%02x:%02x:%02x:%02x:%02x", - buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte(), - buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); - network.addWifiAccessPoint(WifiAccessPoint.from(mac, buf.readUnsignedByte())); - } + return decodeBasicOther(channel, buf, deviceSession, type, dataLength); - int cellCount = buf.readUnsignedByte(); - int mcc = buf.readUnsignedShort(); - int mnc = buf.readUnsignedByte(); - for (int i = 0; i < cellCount; i++) { - network.addCellTower(CellTower.from( - mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedShort(), buf.readUnsignedByte())); - } + } - position.setNetwork(network); + return null; + } - return position; + private Object decodeWifi(ChannelBuffer buf, DeviceSession deviceSession) throws Exception { - } else { + Position position = new Position(); + position.setDeviceId(deviceSession.getDeviceId()); + position.setProtocol(getProtocolName()); - return decodeBasicOther(channel, buf, deviceSession, type, dataLength); + DateBuilder dateBuilder = new DateBuilder() + .setYear(BcdUtil.readInteger(buf, 2)) + .setMonth(BcdUtil.readInteger(buf, 2)) + .setDay(BcdUtil.readInteger(buf, 2)) + .setHour(BcdUtil.readInteger(buf, 2)) + .setMinute(BcdUtil.readInteger(buf, 2)) + .setSecond(BcdUtil.readInteger(buf, 2)); + getLastLocation(position, dateBuilder.getDate()); + + Network network = new Network(); + + int wifiCount = buf.getByte(2); + for (int i = 0; i < wifiCount; i++) { + String mac = String.format("%02x:%02x:%02x:%02x:%02x:%02x", + buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte(), + buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + network.addWifiAccessPoint(WifiAccessPoint.from(mac, buf.readUnsignedByte())); + } + int cellCount = buf.readUnsignedByte(); + int mcc = buf.readUnsignedShort(); + int mnc = buf.readUnsignedByte(); + for (int i = 0; i < cellCount; i++) { + network.addCellTower(CellTower.from( + mcc, mnc, buf.readUnsignedShort(), buf.readUnsignedShort(), buf.readUnsignedByte())); } - return null; + position.setNetwork(network); + + return position; } private Object decodeBasicOther(Channel channel, ChannelBuffer buf, @@ -436,7 +525,10 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { position.setDeviceId(deviceSession.getDeviceId()); position.setProtocol(getProtocolName()); - if (type == MSG_LBS_MULTIPLE || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI) { + if (type == MSG_LBS_MULTIPLE || type == MSG_LBS_EXTEND || type == MSG_LBS_WIFI + || type == MSG_LBS_2 || type == MSG_WIFI_3) { + + boolean longFormat = type == MSG_LBS_2 || type == MSG_WIFI_3; DateBuilder dateBuilder = new DateBuilder(timeZone) .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) @@ -448,8 +540,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { int mnc = buf.readUnsignedByte(); Network network = new Network(); for (int i = 0; i < 7; i++) { - int lac = buf.readUnsignedShort(); - int cid = buf.readUnsignedMedium(); + int lac = longFormat ? buf.readInt() : buf.readUnsignedShort(); + int cid = longFormat ? (int) buf.readLong() : buf.readUnsignedMedium(); int rssi = -buf.readUnsignedByte(); if (lac > 0) { network.addCellTower(CellTower.from(mcc, mnc, lac, cid, rssi)); @@ -458,7 +550,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // time leads - if (type != MSG_LBS_MULTIPLE) { + if (type != MSG_LBS_MULTIPLE && type != MSG_LBS_2) { int wifiCount = buf.readUnsignedByte(); for (int i = 0; i < wifiCount; i++) { String mac = ChannelBuffers.hexDump(buf.readBytes(6)).replaceAll("(..)", "$1:"); diff --git a/src/org/traccar/protocol/Jt600FrameDecoder.java b/src/org/traccar/protocol/Jt600FrameDecoder.java index 5606ae1fc..261f46fe8 100644 --- a/src/org/traccar/protocol/Jt600FrameDecoder.java +++ b/src/org/traccar/protocol/Jt600FrameDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 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. @@ -37,7 +37,7 @@ public class Jt600FrameDecoder extends FrameDecoder { if (type == '$') { boolean longFormat = buf.getUnsignedByte(buf.readerIndex() + 1) == 0x75; int length = buf.getUnsignedShort(buf.readerIndex() + (longFormat ? 8 : 7)) + 10; - if (length >= buf.readableBytes()) { + if (length <= buf.readableBytes()) { return buf.readBytes(length); } } else if (type == '(') { diff --git a/src/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/org/traccar/protocol/Jt600ProtocolDecoder.java index 58835c7d6..e935c1449 100644 --- a/src/org/traccar/protocol/Jt600ProtocolDecoder.java +++ b/src/org/traccar/protocol/Jt600ProtocolDecoder.java @@ -63,8 +63,9 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { return null; } + int protocolVersion = 0; if (longFormat) { - buf.readUnsignedByte(); // protocol + protocolVersion = buf.readUnsignedByte(); } int version = BitUtil.from(buf.readUnsignedByte(), 4); @@ -122,6 +123,11 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { cellTower.setSignalStrength((int) buf.readUnsignedByte()); position.setNetwork(new Network(cellTower)); + if (protocolVersion == 0x17) { + buf.readUnsignedByte(); // geofence id + buf.skipBytes(3); // reserved + } + } else if (version == 1) { position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); diff --git a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java index 05994b697..8e6e56e80 100644 --- a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java +++ b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java @@ -123,7 +123,7 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder { position.setAltitude(parser.nextDouble(0)); - position.set(Position.KEY_BATTERY, parser.nextInt(0)); + position.set(Position.KEY_BATTERY_LEVEL, parser.nextInt(0)); } @Override diff --git a/src/org/traccar/protocol/OsmAndProtocolDecoder.java b/src/org/traccar/protocol/OsmAndProtocolDecoder.java index 15a71c88b..68c143257 100644 --- a/src/org/traccar/protocol/OsmAndProtocolDecoder.java +++ b/src/org/traccar/protocol/OsmAndProtocolDecoder.java @@ -16,15 +16,11 @@ package org.traccar.protocol; import org.jboss.netty.channel.Channel; -import org.jboss.netty.handler.codec.http.DefaultHttpResponse; -import org.jboss.netty.handler.codec.http.HttpHeaders; import org.jboss.netty.handler.codec.http.HttpRequest; -import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseStatus; -import org.jboss.netty.handler.codec.http.HttpVersion; import org.jboss.netty.handler.codec.http.QueryStringDecoder; import org.joda.time.format.ISODateTimeFormat; -import org.traccar.BaseProtocolDecoder; +import org.traccar.BaseHttpProtocolDecoder; import org.traccar.DeviceSession; import org.traccar.model.Position; @@ -36,20 +32,12 @@ import java.util.Date; import java.util.List; import java.util.Map; -public class OsmAndProtocolDecoder extends BaseProtocolDecoder { +public class OsmAndProtocolDecoder extends BaseHttpProtocolDecoder { public OsmAndProtocolDecoder(OsmAndProtocol protocol) { super(protocol); } - private void sendResponse(Channel channel, HttpResponseStatus status) { - if (channel != null) { - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status); - response.headers().add(HttpHeaders.Names.CONTENT_LENGTH, 0); - channel.write(response); - } - } - @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { diff --git a/src/org/traccar/protocol/OwnTracksProtocolDecoder.java b/src/org/traccar/protocol/OwnTracksProtocolDecoder.java index f3284d9e2..49d1cff96 100644 --- a/src/org/traccar/protocol/OwnTracksProtocolDecoder.java +++ b/src/org/traccar/protocol/OwnTracksProtocolDecoder.java @@ -17,39 +17,26 @@ package org.traccar.protocol; import org.jboss.netty.channel.Channel; -import org.jboss.netty.handler.codec.http.DefaultHttpResponse; -import org.jboss.netty.handler.codec.http.HttpHeaders; import org.jboss.netty.handler.codec.http.HttpRequest; -import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseStatus; -import org.jboss.netty.handler.codec.http.HttpVersion; -import org.traccar.BaseProtocolDecoder; +import org.traccar.BaseHttpProtocolDecoder; import org.traccar.DeviceSession; -import org.traccar.model.Position; import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; +import javax.json.Json; +import javax.json.JsonObject; +import java.io.StringReader; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; -import java.io.StringReader; -import javax.json.Json; -import javax.json.JsonObject; - -public class OwnTracksProtocolDecoder extends BaseProtocolDecoder { +public class OwnTracksProtocolDecoder extends BaseHttpProtocolDecoder { public OwnTracksProtocolDecoder(OwnTracksProtocol protocol) { super(protocol); } - private void sendResponse(Channel channel, HttpResponseStatus status) { - if (channel != null) { - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status); - response.headers().add(HttpHeaders.Names.CONTENT_LENGTH, 0); - channel.write(response); - } - } - @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { diff --git a/src/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/org/traccar/protocol/Pt502ProtocolDecoder.java index 1d976dcd5..330ba7643 100644 --- a/src/org/traccar/protocol/Pt502ProtocolDecoder.java +++ b/src/org/traccar/protocol/Pt502ProtocolDecoder.java @@ -78,6 +78,8 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { return Position.ALARM_VIBRATION;
case "PMA":
return Position.ALARM_MOVEMENT;
+ case "CPA":
+ return Position.ALARM_POWER_CUT;
default:
return null;
}
diff --git a/src/org/traccar/protocol/SigfoxProtocol.java b/src/org/traccar/protocol/SigfoxProtocol.java new file mode 100644 index 000000000..9feaea12d --- /dev/null +++ b/src/org/traccar/protocol/SigfoxProtocol.java @@ -0,0 +1,47 @@ +/* + * 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.ServerBootstrap; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.handler.codec.http.HttpChunkAggregator; +import org.jboss.netty.handler.codec.http.HttpRequestDecoder; +import org.jboss.netty.handler.codec.http.HttpResponseEncoder; +import org.traccar.BaseProtocol; +import org.traccar.TrackerServer; + +import java.util.List; + +public class SigfoxProtocol extends BaseProtocol { + + public SigfoxProtocol() { + super("sigfox"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + serverList.add(new TrackerServer(new ServerBootstrap(), getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("httpEncoder", new HttpResponseEncoder()); + pipeline.addLast("httpDecoder", new HttpRequestDecoder()); + pipeline.addLast("httpAggregator", new HttpChunkAggregator(65535)); + pipeline.addLast("objectDecoder", new SigfoxProtocolDecoder(SigfoxProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/SigfoxProtocolDecoder.java b/src/org/traccar/protocol/SigfoxProtocolDecoder.java new file mode 100644 index 000000000..7cf57681f --- /dev/null +++ b/src/org/traccar/protocol/SigfoxProtocolDecoder.java @@ -0,0 +1,91 @@ +/* + * 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.buffer.ChannelBuffers; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.handler.codec.http.HttpRequest; +import org.jboss.netty.handler.codec.http.HttpResponseStatus; +import org.traccar.BaseHttpProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; + +import javax.json.Json; +import javax.json.JsonObject; +import javax.xml.bind.DatatypeConverter; +import java.io.StringReader; +import java.net.SocketAddress; +import java.net.URLDecoder; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.Date; + +public class SigfoxProtocolDecoder extends BaseHttpProtocolDecoder { + + public SigfoxProtocolDecoder(SigfoxProtocol protocol) { + super(protocol); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + HttpRequest request = (HttpRequest) msg; + JsonObject json = Json.createReader(new StringReader(URLDecoder.decode( + request.getContent().toString(StandardCharsets.UTF_8).split("=")[0], "UTF-8"))).readObject(); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, json.getString("device")); + if (deviceSession == null) { + sendResponse(channel, HttpResponseStatus.BAD_REQUEST); + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + position.setTime(new Date(json.getInt("time") * 1000L)); + + ChannelBuffer buf = ChannelBuffers.wrappedBuffer( + ByteOrder.LITTLE_ENDIAN, DatatypeConverter.parseHexBinary(json.getString("data"))); + + int type = buf.readUnsignedByte() >> 4; + if (type == 0) { + + position.setValid(true); + position.setLatitude(buf.readInt() * 0.0000001); + position.setLongitude(buf.readInt() * 0.0000001); + position.setCourse(buf.readUnsignedByte() * 2); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); + + position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 0.025); + + } else { + + getLastLocation(position, position.getDeviceTime()); + + } + + position.set(Position.KEY_RSSI, json.getJsonNumber("rssi").doubleValue()); + position.set(Position.KEY_INDEX, json.getInt("seqNumber")); + + sendResponse(channel, HttpResponseStatus.OK); + return position; + } + +} diff --git a/src/org/traccar/protocol/TmgFrameDecoder.java b/src/org/traccar/protocol/TmgFrameDecoder.java new file mode 100644 index 000000000..549c42466 --- /dev/null +++ b/src/org/traccar/protocol/TmgFrameDecoder.java @@ -0,0 +1,67 @@ +/* + * 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.buffer.ChannelBufferIndexFinder; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.frame.FrameDecoder; + +public class TmgFrameDecoder extends FrameDecoder { + + private boolean isLetter(byte c) { + return c >= 'a' && c <= 'z'; + } + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { + + int beginIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new ChannelBufferIndexFinder() { + @Override + public boolean find(ChannelBuffer buffer, int guessedIndex) { + if (buffer.getByte(guessedIndex) != (byte) '$' || buffer.writerIndex() - guessedIndex < 5) { + return false; + } + if (buffer.getByte(guessedIndex + 4) == ',' + && isLetter(buffer.getByte(guessedIndex + 1)) + && isLetter(buffer.getByte(guessedIndex + 2)) + && isLetter(buffer.getByte(guessedIndex + 3))) { + return true; + } + return false; + } + }); + + if (beginIndex >= 0) { + + buf.readerIndex(beginIndex); + + int endIndex = buf.indexOf(beginIndex, buf.writerIndex(), (byte) '\n'); + + if (endIndex >= 0) { + ChannelBuffer frame = buf.readBytes(endIndex - beginIndex); + buf.readByte(); // delimiter + return frame; + } + + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/TmgProtocol.java b/src/org/traccar/protocol/TmgProtocol.java index f30d61e9a..c11762cac 100644 --- a/src/org/traccar/protocol/TmgProtocol.java +++ b/src/org/traccar/protocol/TmgProtocol.java @@ -17,7 +17,6 @@ package org.traccar.protocol; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipeline; -import org.jboss.netty.handler.codec.frame.LineBasedFrameDecoder; import org.jboss.netty.handler.codec.string.StringDecoder; import org.jboss.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; @@ -36,7 +35,7 @@ public class TmgProtocol extends BaseProtocol { serverList.add(new TrackerServer(new ServerBootstrap(), getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); + pipeline.addLast("frameDecoder", new TmgFrameDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("objectDecoder", new TmgProtocolDecoder(TmgProtocol.this)); diff --git a/src/org/traccar/protocol/TmgProtocolDecoder.java b/src/org/traccar/protocol/TmgProtocolDecoder.java index b8458dd52..5e852aff2 100644 --- a/src/org/traccar/protocol/TmgProtocolDecoder.java +++ b/src/org/traccar/protocol/TmgProtocolDecoder.java @@ -36,7 +36,7 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder { private static final Pattern PATTERN = new PatternBuilder() .text("$") .expression("(...),") // type - .expression("[LH],") // history + .expression("[LH],").optional() // history .number("(d+),") // imei .number("(dd)(dd)(dddd),") // date (ddmmyyyy) .number("(dd)(dd)(dd),") // time (hhmmss) @@ -47,6 +47,7 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder { .expression("([EW]),") .number("(d+.?d*),") // speed .number("(d+.?d*),") // course + .groupBegin() .number("(-?d+.?d*),") // altitude .number("(d+.d+),") // hdop .number("(d+),") // satellites @@ -65,6 +66,20 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder { .number("d+.?d*,") // trip meter .expression("([^,]*),") // software version .expression("([^,]*),").optional() // rfid + .or() + .number("[^,]*,") // cid + .number("(d+),") // rssi + .number("(d+),") // satellites + .number("[^,]*,") // battery level + .expression("([01]),") // ignition + .expression("[LH]{4},") // input + .expression("[NT]{4},") // tamper status + .expression("[LH]{2},") // output + .number("(d+.d+),") // adc1 + .number("(d+.d+),") // adc1 + .number("[^,]*,") // device id + .number("(d+),") // odometer + .groupEnd() .any() .compile(); @@ -115,36 +130,52 @@ public class TmgProtocolDecoder extends BaseProtocolDecoder { position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS)); - position.setValid(parser.nextInt(0) > 0); + position.setValid(parser.nextInt() > 0); position.setLatitude(parser.nextCoordinate()); position.setLongitude(parser.nextCoordinate()); - position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble(0))); - position.setCourse(parser.nextDouble(0)); - position.setAltitude(parser.nextDouble(0)); - - position.set(Position.KEY_HDOP, parser.nextDouble(0)); - position.set(Position.KEY_SATELLITES, parser.nextInt(0)); - position.set(Position.KEY_SATELLITES_VISIBLE, parser.nextInt(0)); - position.set(Position.KEY_OPERATOR, parser.next()); - position.set(Position.KEY_RSSI, parser.nextInt(0)); - position.set(Position.KEY_IGNITION, parser.nextInt(0) == 1); - position.set(Position.KEY_BATTERY, parser.nextDouble(0)); - position.set(Position.KEY_POWER, parser.nextDouble(0)); - - int input = parser.nextBinInt(0); - int output = parser.nextBinInt(0); - - if (!BitUtil.check(input, 0)) { - position.set(Position.KEY_ALARM, Position.ALARM_SOS); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + + if (parser.hasNext(15)) { + + position.setAltitude(parser.nextDouble()); + + position.set(Position.KEY_HDOP, parser.nextDouble()); + position.set(Position.KEY_SATELLITES, parser.nextInt()); + position.set(Position.KEY_SATELLITES_VISIBLE, parser.nextInt()); + position.set(Position.KEY_OPERATOR, parser.next()); + position.set(Position.KEY_RSSI, parser.nextInt()); + position.set(Position.KEY_IGNITION, parser.nextInt() == 1); + position.set(Position.KEY_BATTERY, parser.nextDouble()); + position.set(Position.KEY_POWER, parser.nextDouble()); + + int input = parser.nextBinInt(); + int output = parser.nextBinInt(); + + if (!BitUtil.check(input, 0)) { + position.set(Position.KEY_ALARM, Position.ALARM_SOS); + } + + position.set(Position.KEY_INPUT, input); + position.set(Position.KEY_OUTPUT, output); + + position.set(Position.PREFIX_ADC + 1, parser.nextDouble()); + position.set(Position.PREFIX_ADC + 2, parser.nextDouble()); + position.set(Position.KEY_VERSION_FW, parser.next()); + position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); + } - position.set(Position.KEY_INPUT, input); - position.set(Position.KEY_OUTPUT, output); + if (parser.hasNext(6)) { + + position.set(Position.KEY_RSSI, parser.nextInt()); + position.set(Position.KEY_SATELLITES, parser.nextInt()); + position.set(Position.KEY_IGNITION, parser.nextInt() == 1); + position.set(Position.PREFIX_ADC + 1, parser.nextDouble()); + position.set(Position.PREFIX_ADC + 2, parser.nextDouble()); + position.set(Position.KEY_ODOMETER, parser.nextInt()); - position.set(Position.PREFIX_ADC + 1, parser.nextDouble(0)); - position.set(Position.PREFIX_ADC + 2, parser.nextDouble(0)); - position.set(Position.KEY_VERSION_FW, parser.next()); - position.set(Position.KEY_DRIVER_UNIQUE_ID, parser.next()); + } return position; } |