diff options
Diffstat (limited to 'src/org')
98 files changed, 2749 insertions, 2833 deletions
diff --git a/src/org/traccar/BaseProtocolDecoder.java b/src/org/traccar/BaseProtocolDecoder.java index e9c678930..f8abdcc85 100644 --- a/src/org/traccar/BaseProtocolDecoder.java +++ b/src/org/traccar/BaseProtocolDecoder.java @@ -46,7 +46,7 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { Device device = Context.getIdentityManager().getDeviceByUniqueId(uniqueId); if (device != null) { deviceId = device.getId(); - Context.getConnectionManager().setActiveDevice(deviceId, protocol, channel, remoteAddress); + Context.getConnectionManager().addActiveDevice(deviceId, protocol, channel, remoteAddress); return true; } else { deviceId = 0; @@ -95,4 +95,11 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder { } } + @Override + protected void onMessageEvent(Channel channel, SocketAddress remoteAddress, Object msg) { + if (hasDeviceId()) { + Context.getConnectionManager().updateDevice(deviceId, Device.STATUS_ONLINE, new Date()); + } + } + } diff --git a/src/org/traccar/ExtendedObjectDecoder.java b/src/org/traccar/ExtendedObjectDecoder.java index 382ef869d..ca4561a3f 100644 --- a/src/org/traccar/ExtendedObjectDecoder.java +++ b/src/org/traccar/ExtendedObjectDecoder.java @@ -37,6 +37,7 @@ public abstract class ExtendedObjectDecoder implements ChannelUpstreamHandler { MessageEvent e = (MessageEvent) evt; Object originalMessage = e.getMessage(); Object decodedMessage = decode(e.getChannel(), e.getRemoteAddress(), originalMessage); + onMessageEvent(e.getChannel(), e.getRemoteAddress(), originalMessage); // call after decode if (originalMessage == decodedMessage) { ctx.sendUpstream(evt); } else if (decodedMessage != null) { @@ -50,6 +51,9 @@ public abstract class ExtendedObjectDecoder implements ChannelUpstreamHandler { } } + protected void onMessageEvent(Channel channel, SocketAddress remoteAddress, Object msg) { + } + protected abstract Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception; } diff --git a/src/org/traccar/MainEventHandler.java b/src/org/traccar/MainEventHandler.java index 81376724b..37f0ee387 100644 --- a/src/org/traccar/MainEventHandler.java +++ b/src/org/traccar/MainEventHandler.java @@ -25,6 +25,7 @@ import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler; import org.jboss.netty.handler.timeout.IdleStateEvent; import org.traccar.helper.Log; import org.traccar.model.Position; +import java.text.SimpleDateFormat; public class MainEventHandler extends IdleStateAwareChannelHandler { @@ -39,14 +40,15 @@ public class MainEventHandler extends IdleStateAwareChannelHandler { StringBuilder s = new StringBuilder(); s.append(formatChannel(e.getChannel())).append(" "); s.append("id: ").append(position.getDeviceId()).append(", "); - s.append("time: ").append(position.getFixTime()).append(", "); - s.append("lat: ").append(position.getLatitude()).append(", "); - s.append("lon: ").append(position.getLongitude()).append(", "); - s.append("speed: ").append(position.getSpeed()).append(", "); - s.append("course: ").append(position.getCourse()); + s.append("time: ").append( + new SimpleDateFormat(Log.DATE_FORMAT).format(position.getFixTime())).append(", "); + s.append("lat: ").append(String.format("%.5f", position.getLatitude())).append(", "); + s.append("lon: ").append(String.format("%.5f", position.getLongitude())).append(", "); + s.append("speed: ").append(String.format("%.1f", position.getSpeed())).append(", "); + s.append("course: ").append(String.format("%.1f", position.getCourse())); Log.info(s.toString()); - Context.getConnectionManager().update(position); + Context.getConnectionManager().updatePosition(position); } } diff --git a/src/org/traccar/RemoteAddressHandler.java b/src/org/traccar/RemoteAddressHandler.java index 45b441bb0..8bf2478a0 100644 --- a/src/org/traccar/RemoteAddressHandler.java +++ b/src/org/traccar/RemoteAddressHandler.java @@ -25,8 +25,7 @@ public class RemoteAddressHandler extends ExtendedObjectDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String hostAddress = ((InetSocketAddress) remoteAddress).getAddress().getHostAddress(); diff --git a/src/org/traccar/ReverseGeocoderHandler.java b/src/org/traccar/ReverseGeocoderHandler.java index 62f045c17..8cbcc72b5 100644 --- a/src/org/traccar/ReverseGeocoderHandler.java +++ b/src/org/traccar/ReverseGeocoderHandler.java @@ -62,6 +62,8 @@ public class ReverseGeocoderHandler implements ChannelUpstreamHandler { Channels.fireMessageReceived(ctx, position, e.getRemoteAddress()); } }); + } else { + Channels.fireMessageReceived(ctx, position, e.getRemoteAddress()); } } else { Channels.fireMessageReceived(ctx, message, e.getRemoteAddress()); diff --git a/src/org/traccar/WebDataHandler.java b/src/org/traccar/WebDataHandler.java index c64ca8334..332fc5222 100644 --- a/src/org/traccar/WebDataHandler.java +++ b/src/org/traccar/WebDataHandler.java @@ -15,14 +15,18 @@ */ package org.traccar; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.util.Calendar; import java.util.Formatter; import java.util.Locale; import java.util.TimeZone; import org.traccar.helper.Checksum; +import org.traccar.helper.Log; import org.traccar.model.Device; import org.traccar.model.Event; +import org.traccar.model.MiscFormatter; import org.traccar.model.Position; public class WebDataHandler extends BaseDataHandler { @@ -46,8 +50,24 @@ public class WebDataHandler extends BaseDataHandler { double lat = position.getLatitude(); double lon = position.getLongitude(); - f.format("%02d%07.4f,%c,", (int) Math.abs(lat), Math.abs(lat) % 1 * 60, lat < 0 ? 'S' : 'N'); - f.format("%03d%07.4f,%c,", (int) Math.abs(lon), Math.abs(lon) % 1 * 60, lon < 0 ? 'W' : 'E'); + + char hemisphere; + + if (lat < 0) { + hemisphere = 'S'; + } else { + hemisphere = 'N'; + } + + f.format("%02d%07.4f,%c,", (int) Math.abs(lat), Math.abs(lat) % 1 * 60, hemisphere); + + if (lon < 0) { + hemisphere = 'W'; + } else { + hemisphere = 'E'; + } + + f.format("%03d%07.4f,%c,", (int) Math.abs(lon), Math.abs(lon) % 1 * 60, hemisphere); f.format("%.2f,%.2f,", position.getSpeed(), position.getCourse()); f.format("%1$td%1$tm%1$ty,,", calendar); @@ -73,15 +93,42 @@ public class WebDataHandler extends BaseDataHandler { Device device = Context.getIdentityManager().getDeviceById(position.getDeviceId()); + String attributes = MiscFormatter.toJsonString(position.getAttributes()); + String request = url .replace("{uniqueId}", device.getUniqueId()) - .replace("{deviceId}", String.valueOf(device.getId())) + .replace("{deviceId}", String.valueOf(position.getDeviceId())) + .replace("{protocol}", String.valueOf(position.getProtocol())) + .replace("{deviceTime}", String.valueOf(position.getDeviceTime().getTime())) .replace("{fixTime}", String.valueOf(position.getFixTime().getTime())) - .replace("{latitude}", String.valueOf(position.getLatitude())) + .replace("{valid}", String.valueOf(position.getLatitude())) + .replace("{latitude}", String.valueOf(position.getValid())) .replace("{longitude}", String.valueOf(position.getLongitude())) - .replace("{gprmc}", formatSentence(position)) + .replace("{altitude}", String.valueOf(position.getAltitude())) + .replace("{speed}", String.valueOf(position.getSpeed())) + .replace("{course}", String.valueOf(position.getCourse())) .replace("{statusCode}", calculateStatus(position)); + if (position.getAddress() != null) { + try { + request = request.replace("{address}", URLEncoder.encode(position.getAddress(), "UTF-8")); + } catch (UnsupportedEncodingException error) { + Log.warning(error); + } + } + + if (request.contains("{attributes}")) { + try { + request = request.replace("{attributes}", URLEncoder.encode(attributes, "UTF-8")); + } catch (UnsupportedEncodingException error) { + Log.warning(error); + } + } + + if (request.contains("{gprmc}")) { + request = request.replace("{gprmc}", formatSentence(position)); + } + Context.getAsyncHttpClient().prepareGet(request).execute(); return position; diff --git a/src/org/traccar/database/ConnectionManager.java b/src/org/traccar/database/ConnectionManager.java index e45c83651..450f2f61f 100644 --- a/src/org/traccar/database/ConnectionManager.java +++ b/src/org/traccar/database/ConnectionManager.java @@ -18,6 +18,7 @@ package org.traccar.database; import java.net.SocketAddress; import java.sql.SQLException; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -27,6 +28,7 @@ import java.util.Set; import org.jboss.netty.channel.Channel; import org.traccar.Protocol; import org.traccar.helper.Log; +import org.traccar.model.Device; import org.traccar.model.Position; public class ConnectionManager { @@ -47,13 +49,14 @@ public class ConnectionManager { } } - public void setActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) { + public void addActiveDevice(long deviceId, Protocol protocol, Channel channel, SocketAddress remoteAddress) { activeDevices.put(deviceId, new ActiveDevice(deviceId, protocol, channel, remoteAddress)); } public void removeActiveDevice(Channel channel) { for (ActiveDevice activeDevice : activeDevices.values()) { if (activeDevice.getChannel() == channel) { + updateDevice(activeDevice.getDeviceId(), Device.STATUS_OFFLINE, new Date()); activeDevices.remove(activeDevice.getDeviceId()); break; } @@ -64,12 +67,18 @@ public class ConnectionManager { return activeDevices.get(deviceId); } - public synchronized void update(Position position) { + public synchronized void updateDevice(long deviceId, String status, Date time) { + // TODO update cache and call listener + /*Log.debug(deviceId + " " + status + " " + + new SimpleDateFormat(Log.DATE_FORMAT).format(time));*/ + } + + public synchronized void updatePosition(Position position) { long deviceId = position.getDeviceId(); positions.put(deviceId, position); if (listeners.containsKey(deviceId)) { for (DataCacheListener listener : listeners.get(deviceId)) { - listener.onUpdate(position); + listener.onUpdatePosition(position); } } } @@ -92,7 +101,7 @@ public class ConnectionManager { } public interface DataCacheListener { - void onUpdate(Position position); + void onUpdatePosition(Position position); } public void addListener(Collection<Long> devices, DataCacheListener listener) { diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java index af2dd559a..8be6aec42 100644 --- a/src/org/traccar/database/DataManager.java +++ b/src/org/traccar/database/DataManager.java @@ -241,7 +241,11 @@ public class DataManager implements IdentityManager { User user = QueryBuilder.create(dataSource, getQuery("database.loginUser")) .setString("email", email) .executeQuerySingle(new User()); - return user != null && user.isPasswordValid(password) ? user : null; + if (user != null && user.isPasswordValid(password)) { + return user; + } else { + return null; + } } public Collection<User> getUsers() throws SQLException { diff --git a/src/org/traccar/database/QueryBuilder.java b/src/org/traccar/database/QueryBuilder.java index c3cde0723..ca6335556 100644 --- a/src/org/traccar/database/QueryBuilder.java +++ b/src/org/traccar/database/QueryBuilder.java @@ -281,6 +281,92 @@ public final class QueryBuilder { } } + private <T extends Factory> void addProcessors( + List<ResultSetProcessor<T>> processors, Class<?> parameterType, final Method method, final String name) { + + if (parameterType.equals(boolean.class)) { + processors.add(new ResultSetProcessor<T>() { + @Override + public void process(T object, ResultSet resultSet) throws SQLException { + try { + method.invoke(object, resultSet.getBoolean(name)); + } catch (IllegalAccessException | InvocationTargetException error) { + Log.warning(error); + } + } + }); + } else if (parameterType.equals(int.class)) { + processors.add(new ResultSetProcessor<T>() { + @Override + public void process(T object, ResultSet resultSet) throws SQLException { + try { + method.invoke(object, resultSet.getInt(name)); + } catch (IllegalAccessException | InvocationTargetException error) { + Log.warning(error); + } + } + }); + } else if (parameterType.equals(long.class)) { + processors.add(new ResultSetProcessor<T>() { + @Override + public void process(T object, ResultSet resultSet) throws SQLException { + try { + method.invoke(object, resultSet.getLong(name)); + } catch (IllegalAccessException | InvocationTargetException error) { + Log.warning(error); + } + } + }); + } else if (parameterType.equals(double.class)) { + processors.add(new ResultSetProcessor<T>() { + @Override + public void process(T object, ResultSet resultSet) throws SQLException { + try { + method.invoke(object, resultSet.getDouble(name)); + } catch (IllegalAccessException | InvocationTargetException error) { + Log.warning(error); + } + } + }); + } else if (parameterType.equals(String.class)) { + processors.add(new ResultSetProcessor<T>() { + @Override + public void process(T object, ResultSet resultSet) throws SQLException { + try { + method.invoke(object, resultSet.getString(name)); + } catch (IllegalAccessException | InvocationTargetException error) { + Log.warning(error); + } + } + }); + } else if (parameterType.equals(Date.class)) { + processors.add(new ResultSetProcessor<T>() { + @Override + public void process(T object, ResultSet resultSet) throws SQLException { + try { + Timestamp timestamp = resultSet.getTimestamp(name); + if (timestamp != null) { + method.invoke(object, new Date(timestamp.getTime())); + } + } catch (IllegalAccessException | InvocationTargetException error) { + Log.warning(error); + } + } + }); + } else if (parameterType.equals(Map.class)) { + processors.add(new ResultSetProcessor<T>() { + @Override + public void process(T object, ResultSet resultSet) throws SQLException { + try (JsonReader reader = Json.createReader(new StringReader(resultSet.getString(name)))) { + method.invoke(object, MiscFormatter.fromJson(reader.readObject())); + } catch (IllegalAccessException | InvocationTargetException | JsonParsingException error) { + Log.warning(error); + } + } + }); + } + } + public <T extends Factory> Collection<T> executeQuery(T prototype) throws SQLException { List<T> result = new LinkedList<>(); @@ -313,89 +399,7 @@ public final class QueryBuilder { continue; } - Class<?> parameterType = method.getParameterTypes()[0]; - - if (parameterType.equals(boolean.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - method.invoke(object, resultSet.getBoolean(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - Log.warning(error); - } - } - }); - } else if (parameterType.equals(int.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - method.invoke(object, resultSet.getInt(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - Log.warning(error); - } - } - }); - } else if (parameterType.equals(long.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - method.invoke(object, resultSet.getLong(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - Log.warning(error); - } - } - }); - } else if (parameterType.equals(double.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - method.invoke(object, resultSet.getDouble(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - Log.warning(error); - } - } - }); - } else if (parameterType.equals(String.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - method.invoke(object, resultSet.getString(name)); - } catch (IllegalAccessException | InvocationTargetException error) { - Log.warning(error); - } - } - }); - } else if (parameterType.equals(Date.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try { - Timestamp timestamp = resultSet.getTimestamp(name); - if (timestamp != null) { - method.invoke(object, new Date(timestamp.getTime())); - } - } catch (IllegalAccessException | InvocationTargetException error) { - Log.warning(error); - } - } - }); - } else if (parameterType.equals(Map.class)) { - processors.add(new ResultSetProcessor<T>() { - @Override - public void process(T object, ResultSet resultSet) throws SQLException { - try (JsonReader reader = Json.createReader(new StringReader(resultSet.getString(name)))) { - method.invoke(object, MiscFormatter.fromJson(reader.readObject())); - } catch (IllegalAccessException | InvocationTargetException | JsonParsingException error) { - Log.warning(error); - } - } - }); - } + addProcessors(processors, method.getParameterTypes()[0], method, name); } } diff --git a/src/org/traccar/helper/DateBuilder.java b/src/org/traccar/helper/DateBuilder.java index 77c6821aa..c52210326 100644 --- a/src/org/traccar/helper/DateBuilder.java +++ b/src/org/traccar/helper/DateBuilder.java @@ -25,13 +25,20 @@ public class DateBuilder { public DateBuilder() { this(TimeZone.getTimeZone("UTC")); + } + public DateBuilder(Date time) { + this(time, TimeZone.getTimeZone("UTC")); } public DateBuilder(TimeZone timeZone) { + this(new Date(0), timeZone); + } + + public DateBuilder(Date time, TimeZone timeZone) { calendar = Calendar.getInstance(timeZone); calendar.clear(); - calendar.setTimeInMillis(0); + calendar.setTimeInMillis(time.getTime()); } public DateBuilder setYear(int year) { @@ -90,10 +97,19 @@ public class DateBuilder { return this; } + public DateBuilder addMillis(long millis) { + calendar.setTimeInMillis(calendar.getTimeInMillis() + millis); + return this; + } + public DateBuilder setTime(int hour, int minute, int second) { return setHour(hour).setMinute(minute).setSecond(second); } + public DateBuilder setTimeReverse(int second, int minute, int hour) { + return setHour(hour).setMinute(minute).setSecond(second); + } + public DateBuilder setTime(int hour, int minute, int second, int millis) { return setHour(hour).setMinute(minute).setSecond(second).setMillis(millis); } diff --git a/src/org/traccar/helper/Log.java b/src/org/traccar/helper/Log.java index 0e55a5445..2b747734e 100644 --- a/src/org/traccar/helper/Log.java +++ b/src/org/traccar/helper/Log.java @@ -40,6 +40,8 @@ public final class Log { private Log() { } + public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; + private static final String LOGGER_NAME = "traccar"; private static final String STACK_PACKAGE = "org.traccar"; @@ -49,7 +51,7 @@ public final class Log { public static void setupLogger(Config config) throws IOException { - Layout layout = new PatternLayout("%d{yyyy-MM-dd HH:mm:ss} %5p: %m%n"); + Layout layout = new PatternLayout("%d{" + DATE_FORMAT + "} %5p: %m%n"); Appender appender = new DailyRollingFileAppender( layout, config.getString("logger.file"), "'.'yyyyMMdd"); diff --git a/src/org/traccar/helper/ObdDecoder.java b/src/org/traccar/helper/ObdDecoder.java new file mode 100644 index 000000000..35fa4dc07 --- /dev/null +++ b/src/org/traccar/helper/ObdDecoder.java @@ -0,0 +1,78 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.helper; + +import org.traccar.model.Event; + +import java.util.AbstractMap; +import java.util.Map; + +public final class ObdDecoder { + + private ObdDecoder() { + } + + private static final int MODE_CURRENT = 0x01; + private static final int MODE_FREEZE_FRAME = 0x02; + + private static final int PID_ENGINE_LOAD = 0x04; + private static final int PID_COOLANT_TEMPERATURE = 0x05; + private static final int PID_ENGINE_RPM = 0x0C; + private static final int PID_VEHICLE_SPEED = 0x0D; + private static final int PID_THROTTLE_POSITION = 0x11; + private static final int PID_MIL_DISTANCE = 0x21; + private static final int PID_FUEL_LEVEL = 0x2F; + private static final int PID_DISTANCE_CLEARED = 0x31; + + public static Map.Entry<String, Object> decode(int mode, int pid, String value) { + switch (mode) { + case MODE_CURRENT: + case MODE_FREEZE_FRAME: + return decodeData(pid, value); + default: + return null; + } + } + + private static Map.Entry<String, Object> createEntry(String key, Object value) { + return new AbstractMap.SimpleEntry<>(key, value); + } + + private static Map.Entry<String, Object> decodeData(int pid, String value) { + int intValue = Integer.parseInt(value, 16); + switch (pid) { + case PID_ENGINE_LOAD: + return createEntry("engine-load", intValue * 100 / 255); + case PID_COOLANT_TEMPERATURE: + return createEntry("coolant-temperature", intValue - 40); + case PID_ENGINE_RPM: + return createEntry(Event.KEY_RPM, intValue / 4); + case PID_VEHICLE_SPEED: + return createEntry(Event.KEY_OBD_SPEED, intValue); + case PID_THROTTLE_POSITION: + return createEntry("throttle", intValue * 100 / 255); + case PID_MIL_DISTANCE: + return createEntry("mil-distance", intValue); + case PID_FUEL_LEVEL: + return createEntry(Event.KEY_FUEL, intValue * 100 / 255); + case PID_DISTANCE_CLEARED: + return createEntry(Event.KEY_FUEL, intValue); + default: + return null; + } + } + +} diff --git a/src/org/traccar/helper/Parser.java b/src/org/traccar/helper/Parser.java index bda7d6366..c5f5d2e37 100644 --- a/src/org/traccar/helper/Parser.java +++ b/src/org/traccar/helper/Parser.java @@ -87,9 +87,11 @@ public class Parser { } public enum CoordinateFormat { + DEG_DEG, DEG_HEM, DEG_MIN_HEM, DEG_MIN_MIN_HEM, + HEM_DEG_MIN_MIN, HEM_DEG, HEM_DEG_MIN, HEM_DEG_MIN_HEM @@ -97,9 +99,12 @@ public class Parser { public double nextCoordinate(CoordinateFormat format) { double coordinate; - String hemisphere; + String hemisphere = null; switch (format) { + case DEG_DEG: + coordinate = Double.parseDouble(next() + '.' + next()); + break; case DEG_HEM: coordinate = nextDouble(); hemisphere = next(); @@ -126,6 +131,12 @@ public class Parser { hemisphere = next(); } break; + case HEM_DEG_MIN_MIN: + hemisphere = next(); + coordinate = nextInt(); + coordinate += Double.parseDouble(next() + '.' + next()) / 60; + break; + case DEG_MIN_HEM: default: coordinate = nextInt(); coordinate += nextDouble() / 60; diff --git a/src/org/traccar/helper/PatternBuilder.java b/src/org/traccar/helper/PatternBuilder.java index 6742e7130..3a8bdd868 100644 --- a/src/org/traccar/helper/PatternBuilder.java +++ b/src/org/traccar/helper/PatternBuilder.java @@ -48,7 +48,7 @@ public class PatternBuilder { s = s.replace("dddd", "d{4}").replace("ddd", "d{3}").replace("dd", "d{2}"); s = s.replace("xxxx", "x{4}").replace("xxx", "x{3}").replace("xx", "x{2}"); - s = s.replace("d", "\\d").replace("x", "\\p{XDigit}").replaceAll("([\\.])", "\\\\$1"); + s = s.replace("d", "\\d").replace("x", "[0-9a-fA-F]").replaceAll("([\\.])", "\\\\$1"); s = s.replaceAll("\\|$", "\\\\|").replaceAll("^\\|", "\\\\|"); // special case for delimiter fragments.add(s); diff --git a/src/org/traccar/model/Command.java b/src/org/traccar/model/Command.java index 99525a02b..abca811a2 100644 --- a/src/org/traccar/model/Command.java +++ b/src/org/traccar/model/Command.java @@ -25,6 +25,7 @@ public class Command extends Extensible implements Factory { public static final String TYPE_ALARM_ARM = "alarmArm"; public static final String TYPE_ALARM_DISARM = "alarmDisarm"; public static final String TYPE_SET_TIMEZONE = "setTimezone"; + public static final String TYPE_REQUEST_PHOTO = "requestPhoto"; public static final String KEY_UNIQUE_ID = "uniqueId"; public static final String KEY_FREQUENCY = "frequency"; diff --git a/src/org/traccar/model/Device.java b/src/org/traccar/model/Device.java index 698505983..fd62cc691 100644 --- a/src/org/traccar/model/Device.java +++ b/src/org/traccar/model/Device.java @@ -54,6 +54,10 @@ public class Device implements Factory { this.uniqueId = uniqueId; } + public static final String STATUS_UNKNOWN = "unknown"; + public static final String STATUS_ONLINE = "online"; + public static final String STATUS_OFFLINE = "offline"; + private String status; public String getStatus() { diff --git a/src/org/traccar/model/Event.java b/src/org/traccar/model/Event.java index 172203a86..832e0e7c5 100644 --- a/src/org/traccar/model/Event.java +++ b/src/org/traccar/model/Event.java @@ -50,6 +50,7 @@ public abstract class Event extends Extensible { public static final String KEY_DOOR = "door"; public static final String KEY_RPM = "rpm"; public static final String KEY_HOURS = "hours"; + public static final String KEY_VIN = "vin"; public static final String KEY_OBD_SPEED = "obd-speed"; public static final String KEY_OBD_ODOMETER = "obd-odometer"; diff --git a/src/org/traccar/model/Extensible.java b/src/org/traccar/model/Extensible.java index a821d0e43..40a286987 100644 --- a/src/org/traccar/model/Extensible.java +++ b/src/org/traccar/model/Extensible.java @@ -52,4 +52,10 @@ public abstract class Extensible extends Message { } } + public void add(Map.Entry<String, Object> entry) { + if (entry.getValue() != null) { + attributes.put(entry.getKey(), entry.getValue()); + } + } + } diff --git a/src/org/traccar/protocol/ApelProtocolDecoder.java b/src/org/traccar/protocol/ApelProtocolDecoder.java index a04aa01af..e346e7d88 100644 --- a/src/org/traccar/protocol/ApelProtocolDecoder.java +++ b/src/org/traccar/protocol/ApelProtocolDecoder.java @@ -18,16 +18,14 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.nio.ByteOrder; import java.nio.charset.Charset; -import java.util.Calendar; +import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.helper.Checksum; -import org.traccar.helper.Log; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -90,8 +88,7 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; int type = buf.readUnsignedShort(); @@ -104,8 +101,7 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder { } if (type == MSG_TRACKER_ID) { - Log.warning("Unsupported authentication type"); - return null; + return null; // unsupported authentication type } if (type == MSG_TRACKER_ID_EXT) { @@ -139,7 +135,6 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder { position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); - // Message index int subtype = type; if (type == MSG_LOG_RECORDS) { position.set(Event.KEY_ARCHIVE, true); @@ -154,19 +149,10 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(); // length } - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.setTimeInMillis(buf.readUnsignedInt() * 1000); - position.setTime(time.getTime()); - - // Latitude + position.setTime(new Date(buf.readUnsignedInt() * 1000)); position.setLatitude(buf.readInt() * 180.0 / 0x7FFFFFFF); - - // Longitude position.setLongitude(buf.readInt() * 180.0 / 0x7FFFFFFF); - // Speed and Validity if (subtype == MSG_STATE_FULL_INFO_T104) { int speed = buf.readUnsignedByte(); position.setValid(speed != 255); @@ -175,39 +161,25 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder { } else { int speed = buf.readShort(); position.setValid(speed != -1); - position.setSpeed(UnitsConverter.knotsFromKph(speed / 100.0)); + position.setSpeed(UnitsConverter.knotsFromKph(speed * 0.01)); } - // Course - position.setCourse(buf.readShort() / 100.0); - - // Altitude + position.setCourse(buf.readShort() * 0.01); position.setAltitude(buf.readShort()); if (subtype == MSG_STATE_FULL_INFO_T104) { - // Satellites position.set(Event.KEY_SATELLITES, buf.readUnsignedByte()); - - // Cell signal position.set(Event.KEY_GSM, buf.readUnsignedByte()); - - // Event type position.set(Event.KEY_EVENT, buf.readUnsignedShort()); - - // Odometer position.set(Event.KEY_ODOMETER, buf.readUnsignedInt()); - - // Input/Output position.set(Event.KEY_INPUT, buf.readUnsignedByte()); position.set(Event.KEY_OUTPUT, buf.readUnsignedByte()); - // Analog sensors for (int i = 1; i <= 8; i++) { position.set(Event.PREFIX_ADC + i, buf.readUnsignedShort()); } - // Counters position.set(Event.PREFIX_COUNT + 1, buf.readUnsignedInt()); position.set(Event.PREFIX_COUNT + 2, buf.readUnsignedInt()); position.set(Event.PREFIX_COUNT + 3, buf.readUnsignedInt()); @@ -216,8 +188,7 @@ public class ApelProtocolDecoder extends BaseProtocolDecoder { positions.add(position); } - // Skip CRC - buf.readUnsignedInt(); + buf.readUnsignedInt(); // crc if (type == MSG_LOG_RECORDS) { requestArchive(channel); diff --git a/src/org/traccar/protocol/Ardi01ProtocolDecoder.java b/src/org/traccar/protocol/Ardi01ProtocolDecoder.java index 738af9bb1..80186894a 100644 --- a/src/org/traccar/protocol/Ardi01ProtocolDecoder.java +++ b/src/org/traccar/protocol/Ardi01ProtocolDecoder.java @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -32,75 +32,56 @@ public class Ardi01ProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "(\\d+)," + // IMEI - "(\\d{4})(\\d{2})(\\d{2})" + // Date (YYYYMMDD) - "(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS) - "(-?\\d+\\.\\d+)," + // Longitude - "(-?\\d+\\.\\d+)," + // Latitude - "(\\d+\\.?\\d*)," + // Speed - "(\\d+\\.?\\d*)," + // Course - "(-?\\d+\\.?\\d*)," + // Altitude - "(\\d+)," + // Satellites - "(\\d+)," + // Event - "(\\d+)," + // Battery - "(-?\\d+)"); // Temperature + private static final Pattern PATTERN = new PatternBuilder() + .number("(d+),") // imei + .number("(dddd)(dd)(dd)") // date + .number("(dd)(dd)(dd),") // time + .number("(-?d+.d+),") // longitude + .number("(-?d+.d+),") // latitude + .number("(d+.?d*),") // speed + .number("(d+.?d*),") // course + .number("(-?d+.?d*),") // altitude + .number("(d+),") // satellites + .number("(d+),") // event + .number("(d+),") // battery + .number("(-?d+)") // temperature + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; - - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - // Detect device - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Date and time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Location data - position.setLongitude(Double.parseDouble(parser.group(index++))); - position.setLatitude(Double.parseDouble(parser.group(index++))); - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(parser.group(index++)))); - position.setCourse(Double.parseDouble(parser.group(index++))); - position.setAltitude(Double.parseDouble(parser.group(index++))); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + position.setAltitude(parser.nextDouble()); - // Satellites - int satellites = Integer.parseInt(parser.group(index++)); + int satellites = parser.nextInt(); position.setValid(satellites >= 3); position.set(Event.KEY_SATELLITES, satellites); - // Event - position.set(Event.KEY_EVENT, parser.group(index++)); - - // Input - position.set(Event.KEY_BATTERY, parser.group(index++)); - - // Output - position.set(Event.PREFIX_TEMP + 1, parser.group(index++)); + position.set(Event.KEY_EVENT, parser.next()); + position.set(Event.KEY_BATTERY, parser.next()); + position.set(Event.PREFIX_TEMP + 1, parser.next()); return position; } diff --git a/src/org/traccar/protocol/AtrackProtocolDecoder.java b/src/org/traccar/protocol/AtrackProtocolDecoder.java index 792f22c07..e939cb88d 100644 --- a/src/org/traccar/protocol/AtrackProtocolDecoder.java +++ b/src/org/traccar/protocol/AtrackProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,17 +24,39 @@ import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.Context; +import org.traccar.helper.DateBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; public class AtrackProtocolDecoder extends BaseProtocolDecoder { + private static final int MIN_DATA_LENGTH = 40; + + private boolean longDate; + private boolean custom; + private String form; + public AtrackProtocolDecoder(AtrackProtocol protocol) { super(protocol); + + longDate = Context.getConfig().getBoolean(getProtocolName() + ".longDate"); + + custom = Context.getConfig().getBoolean(getProtocolName() + ".custom"); + form = Context.getConfig().getString(getProtocolName() + ".form"); + if (form != null) { + custom = true; + } } - private static final int MIN_DATA_LENGTH = 40; + public void setLongDate(boolean longDate) { + this.longDate = longDate; + } + + public void setCustom(boolean custom) { + this.custom = custom; + } private static void sendResponse(Channel channel, SocketAddress remoteAddress, long rawId, int index) { if (channel != null) { @@ -47,32 +69,115 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { } private static String readString(ChannelBuffer buf) { - String result = null; - int length = 0; - while (buf.getByte(buf.readerIndex() + length) != 0) { - length += 1; - } - if (length != 0) { - result = buf.toString(buf.readerIndex(), length, Charset.defaultCharset()); - buf.skipBytes(length); + int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0); + if (index > buf.readerIndex()) { + result = buf.readBytes(index - buf.readerIndex()).toString(Charset.defaultCharset()); } buf.readByte(); - return result; } + private void readCustomData(Position position, ChannelBuffer buf, String form) { + String[] keys = form.substring(1).split("%"); + for (String key : keys) { + switch (key) { + case "SA": + position.set(Event.KEY_SATELLITES, buf.readUnsignedByte()); + break; + case "MV": + position.set(Event.KEY_POWER, buf.readUnsignedShort()); + break; + case "BV": + position.set(Event.KEY_BATTERY, buf.readUnsignedShort()); + break; + case "GQ": + position.set(Event.KEY_GSM, buf.readUnsignedByte()); + break; + case "CE": + position.set(Event.KEY_CELL, buf.readUnsignedInt()); + break; + case "LC": + position.set(Event.KEY_LAC, buf.readUnsignedShort()); + break; + case "CN": + buf.readUnsignedInt(); // mcc + mnc + break; + case "RL": + buf.readUnsignedByte(); // rxlev + break; + case "PC": + buf.readUnsignedInt(); // pulse count + break; + case "AT": + position.setAltitude(buf.readUnsignedInt()); + break; + case "RP": + position.set(Event.KEY_RPM, buf.readUnsignedShort()); + break; + case "GS": + buf.readUnsignedByte(); // gsm status + break; + case "DT": + position.set(Event.KEY_ARCHIVE, buf.readUnsignedByte() == 1); + break; + case "VN": + position.set(Event.KEY_VIN, readString(buf)); + break; + case "MF": + buf.readUnsignedShort(); // mass air flow rate + break; + case "EL": + buf.readUnsignedByte(); // engine load + break; + case "TR": + buf.readUnsignedByte(); // throttle position + break; + case "ET": + buf.readUnsignedShort(); // engine coolant temp + break; + case "FL": + position.set(Event.KEY_FUEL, buf.readUnsignedByte()); + break; + case "ML": + buf.readUnsignedByte(); // mil status + break; + case "FC": + buf.readUnsignedInt(); // fuel used + break; + case "CI": + readString(buf); // format string + break; + case "AV1": + position.set(Event.PREFIX_ADC + 1, buf.readUnsignedShort()); + break; + case "NC": + readString(buf); // gsm neighbor cell info + break; + case "SM": + buf.readUnsignedShort(); // max speed between reports + break; + case "GL": + readString(buf); // google link + break; + case "MA": + readString(buf); // mac address + break; + default: + break; + } + } + } + @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; - // Keep alive message if (buf.getUnsignedShort(buf.readerIndex()) == 0xfe02) { if (channel != null) { - channel.write(buf, remoteAddress); + channel.write(buf, remoteAddress); // keep-alive message } return null; } @@ -82,72 +187,70 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(); // length int index = buf.readUnsignedShort(); - // Get device id long id = buf.readLong(); if (!identify(String.valueOf(id), channel, remoteAddress)) { return null; } - // Send acknowledgement sendResponse(channel, remoteAddress, id, index); List<Position> positions = new LinkedList<>(); while (buf.readableBytes() >= MIN_DATA_LENGTH) { - // Create new position Position position = new Position(); - position.setDeviceId(getDeviceId()); position.setProtocol(getProtocolName()); + position.setDeviceId(getDeviceId()); + + if (longDate) { + + DateBuilder dateBuilder = new DateBuilder() + .setDate(buf.readUnsignedShort(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + position.setTime(dateBuilder.getDate()); - // Date and time - position.setTime(new Date(buf.readUnsignedInt() * 1000)); // gps time - buf.readUnsignedInt(); // rtc time - buf.readUnsignedInt(); // send time + buf.skipBytes(7 + 7); + + + } else { + + position.setFixTime(new Date(buf.readUnsignedInt() * 1000)); + position.setDeviceTime(new Date(buf.readUnsignedInt() * 1000)); + buf.readUnsignedInt(); // send time + } - // Coordinates position.setValid(true); position.setLongitude(buf.readInt() * 0.000001); position.setLatitude(buf.readInt() * 0.000001); - - // Course position.setCourse(buf.readUnsignedShort()); - // Report type position.set(Event.KEY_TYPE, buf.readUnsignedByte()); - - // Odometer position.set(Event.KEY_ODOMETER, buf.readUnsignedInt() * 0.1); - - // Accuracy position.set(Event.KEY_HDOP, buf.readUnsignedShort() * 0.1); - - // Input position.set(Event.KEY_INPUT, buf.readUnsignedByte()); - // Speed position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort())); - // Output position.set(Event.KEY_OUTPUT, buf.readUnsignedByte()); - - // ADC position.set(Event.PREFIX_ADC + 1, buf.readUnsignedShort() * 0.001); - // Driver position.set("driver", readString(buf)); - // Temperature position.set(Event.PREFIX_TEMP + 1, buf.readShort() * 0.1); position.set(Event.PREFIX_TEMP + 2, buf.readShort() * 0.1); - // Text Message position.set("message", readString(buf)); - // With AT$FORM Command you can extend atrack protocol. - // For example adding AT$FORM %FC /Fuel used you can add the line in this position: - // position.set("fuelused", buf.readUnsignedInt() * 0.1); + if (custom) { + String form = this.form; + if (form == null) { + form = readString(buf).substring("%CI".length()); + } + readCustomData(position, buf, form); + } + positions.add(position); + } return positions; diff --git a/src/org/traccar/protocol/AutoFon45ProtocolDecoder.java b/src/org/traccar/protocol/AutoFon45ProtocolDecoder.java index 92ffaedeb..12020d161 100644 --- a/src/org/traccar/protocol/AutoFon45ProtocolDecoder.java +++ b/src/org/traccar/protocol/AutoFon45ProtocolDecoder.java @@ -1,4 +1,5 @@ /* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) * Copyright 2015 Vitaly Litvak (vitavaque@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,12 +18,12 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.util.Arrays; -import java.util.Calendar; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; import org.traccar.model.Event; import org.traccar.model.Position; import static org.traccar.protocol.AutoFon45FrameDecoder.MSG_LOCATION; @@ -30,9 +31,17 @@ import static org.traccar.protocol.AutoFon45FrameDecoder.MSG_LOGIN; public class AutoFon45ProtocolDecoder extends BaseProtocolDecoder { - private static double convertCoordinate(short degrees, int raw) { - double seconds = (raw >> 4 & 0xffffff) / 600000.0; - return (degrees + seconds) * ((raw & 0x0f) == 0 ? -1 : 1); + public AutoFon45ProtocolDecoder(AutoFon45Protocol protocol) { + super(protocol); + } + + private static double convertCoordinate(short degrees, int minutes) { + double value = degrees + BitUtil.from(minutes, 4) / 600000.0; + if (BitUtil.check(minutes, 0)) { + return value; + } else { + return -value; + } } private static byte checksum(byte[] bytes, int offset, int len) { @@ -46,19 +55,16 @@ public class AutoFon45ProtocolDecoder extends BaseProtocolDecoder { return result; } - public AutoFon45ProtocolDecoder(AutoFon45Protocol protocol) { - super(protocol); - } - @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; - int type = buf.getUnsignedByte(0); + int type = buf.getUnsignedByte(buf.readerIndex()); if (type == MSG_LOGIN) { + byte[] bytes = new byte[19]; buf.readBytes(bytes); @@ -67,24 +73,25 @@ public class AutoFon45ProtocolDecoder extends BaseProtocolDecoder { return null; } - // Send response (CRC) + // Send response (checksum) if (channel != null) { byte[] response = "resp_crc=".getBytes("US-ASCII"); response = Arrays.copyOf(response, response.length + 1); response[response.length - 1] = checksum(bytes, 0, 18); channel.write(ChannelBuffers.wrappedBuffer(response)); } + } else if (type == MSG_LOCATION) { + buf.readUnsignedByte(); - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); short status = buf.readUnsignedByte(); - position.set(Event.KEY_ALARM, (status & 0x80) != 0); - position.set(Event.KEY_BATTERY, status & 0x7F); + position.set(Event.KEY_ALARM, BitUtil.check(status, 7)); + position.set(Event.KEY_BATTERY, BitUtil.to(status, 7)); buf.skipBytes(2); // remaining time @@ -94,37 +101,27 @@ public class AutoFon45ProtocolDecoder extends BaseProtocolDecoder { buf.readByte(); // mode buf.readByte(); // gprs sending interval - buf.skipBytes(6); // MCC, MNC, LAC, CID + buf.skipBytes(6); // mcc, mnc, lac, cid - // GPS status int valid = buf.readUnsignedByte(); - position.setValid((valid & 0xc0) != 0); - position.set(Event.KEY_SATELLITES, valid & 0x3f); - - // Date and time - int timeOfDay = buf.readUnsignedByte() << 16 | buf.readUnsignedByte() << 8 | buf.readUnsignedByte(); - int date = buf.readUnsignedByte() << 16 | buf.readUnsignedByte() << 8 | buf.readUnsignedByte(); - - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.HOUR_OF_DAY, timeOfDay / 10000); - time.set(Calendar.MINUTE, (timeOfDay - time.get(Calendar.HOUR_OF_DAY) * 10000) / 100); - time.set(Calendar.SECOND, (timeOfDay - time.get(Calendar.HOUR_OF_DAY) * 10000 - time.get(Calendar.MINUTE) * 100)); - time.set(Calendar.DAY_OF_MONTH, date / 10000); - time.set(Calendar.MONTH, (date - time.get(Calendar.DAY_OF_MONTH) * 10000) / 100 - 1); - time.set(Calendar.YEAR, 2000 + (date - time.get(Calendar.DAY_OF_MONTH) * 10000 - (time.get(Calendar.MONTH) + 1) * 100)); - position.setTime(time.getTime()); - - // Location - position.setLatitude(convertCoordinate(buf.readUnsignedByte(), - buf.readUnsignedByte() << 16 | buf.readUnsignedByte() << 8 | buf.readUnsignedByte())); - position.setLongitude(convertCoordinate(buf.readUnsignedByte(), - buf.readUnsignedByte() << 16 | buf.readUnsignedByte() << 8 | buf.readUnsignedByte())); + position.setValid(BitUtil.from(valid, 6) != 0); + position.set(Event.KEY_SATELLITES, BitUtil.from(valid, 6)); + + int time = buf.readUnsignedMedium(); + int date = buf.readUnsignedMedium(); + + DateBuilder dateBuilder = new DateBuilder() + .setTime(time / 10000, time / 100 % 100, time % 100) + .setDateReverse(date / 10000, date / 100 % 100, date % 100); + position.setTime(dateBuilder.getDate()); + + position.setLatitude(convertCoordinate(buf.readUnsignedByte(), buf.readUnsignedMedium())); + position.setLongitude(convertCoordinate(buf.readUnsignedByte(), buf.readUnsignedMedium())); position.setSpeed(buf.readUnsignedByte()); - position.setCourse(buf.readUnsignedByte() << 8 | buf.readUnsignedByte()); + position.setCourse(buf.readUnsignedShort()); - buf.readUnsignedByte(); // checksum return position; + } return null; diff --git a/src/org/traccar/protocol/AutoFonProtocolDecoder.java b/src/org/traccar/protocol/AutoFonProtocolDecoder.java index 1da024e0b..9356e2cd7 100644 --- a/src/org/traccar/protocol/AutoFonProtocolDecoder.java +++ b/src/org/traccar/protocol/AutoFonProtocolDecoder.java @@ -16,14 +16,13 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; import java.util.LinkedList; import java.util.List; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -45,7 +44,6 @@ public class AutoFonProtocolDecoder extends BaseProtocolDecoder { private Position decodePosition(ChannelBuffer buf, boolean history) { - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); @@ -61,7 +59,6 @@ public class AutoFonProtocolDecoder extends BaseProtocolDecoder { position.set(Event.KEY_BATTERY, buf.readUnsignedByte()); buf.skipBytes(6); // time - // Timers if (!history) { for (int i = 0; i < 2; i++) { buf.skipBytes(5); // time @@ -77,23 +74,15 @@ public class AutoFonProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(); // lac buf.readUnsignedShort(); // cid - // GPS status int valid = buf.readUnsignedByte(); position.setValid((valid & 0xc0) != 0); position.set(Event.KEY_SATELLITES, valid & 0x3f); - // Date and time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); - time.set(Calendar.MONTH, buf.readUnsignedByte() - 1); - time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); - time.set(Calendar.HOUR_OF_DAY, buf.readUnsignedByte()); - time.set(Calendar.MINUTE, buf.readUnsignedByte()); - time.set(Calendar.SECOND, buf.readUnsignedByte()); - position.setTime(time.getTime()); - - // Location + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + position.setTime(dateBuilder.getDate()); + position.setLatitude(convertCoordinate(buf.readInt())); position.setLongitude(convertCoordinate(buf.readInt())); position.setAltitude(buf.readShort()); @@ -109,8 +98,7 @@ public class AutoFonProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -126,7 +114,6 @@ public class AutoFonProtocolDecoder extends BaseProtocolDecoder { return null; } - // Send response if (channel != null) { channel.write(ChannelBuffers.wrappedBuffer(new byte[] {buf.readByte()})); } diff --git a/src/org/traccar/protocol/Avl301ProtocolDecoder.java b/src/org/traccar/protocol/Avl301ProtocolDecoder.java index f0ad97bf8..67ea223fa 100644 --- a/src/org/traccar/protocol/Avl301ProtocolDecoder.java +++ b/src/org/traccar/protocol/Avl301ProtocolDecoder.java @@ -16,12 +16,11 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -60,8 +59,7 @@ public class Avl301ProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -81,36 +79,25 @@ public class Avl301ProtocolDecoder extends BaseProtocolDecoder { } else if (hasDeviceId() && type == MSG_GPS_LBS_STATUS) { - // Create new position Position position = new Position(); position.setDeviceId(getDeviceId()); position.setProtocol(getProtocolName()); - // Date and time(6) - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); - time.set(Calendar.MONTH, buf.readUnsignedByte() - 1); - time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); - time.set(Calendar.HOUR_OF_DAY, buf.readUnsignedByte()); - time.set(Calendar.MINUTE, buf.readUnsignedByte()); - time.set(Calendar.SECOND, buf.readUnsignedByte()); - position.setTime(time.getTime()); - - // GPS length and Satellites count - int gpsLength = buf.readUnsignedByte(); + DateBuilder dateBuilder = new DateBuilder() + .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + position.setTime(dateBuilder.getDate()); + + int gpsLength = buf.readUnsignedByte(); // gps len and sat position.set(Event.KEY_SATELLITES, gpsLength & 0xf); - //Skip Satellite numbers - buf.skipBytes(1); + buf.readUnsignedByte(); // satellites - // Location double latitude = buf.readUnsignedInt() / 600000.0; double longitude = buf.readUnsignedInt() / 600000.0; - position.setSpeed(buf.readUnsignedByte() * 1.0); // kph? + position.setSpeed(buf.readUnsignedByte()); - // Course and flags - int union = buf.readUnsignedShort(); + int union = buf.readUnsignedShort(); // course and flags position.setCourse(union & 0x03FF); position.setValid((union & 0x1000) != 0); if ((union & 0x0400) != 0) { @@ -133,10 +120,11 @@ public class Avl301ProtocolDecoder extends BaseProtocolDecoder { int flags = buf.readUnsignedByte(); position.set("acc", (flags & 0x2) != 0); - // TODO parse other flags + // parse other flags position.set(Event.KEY_POWER, buf.readUnsignedByte()); position.set(Event.KEY_GSM, buf.readUnsignedByte()); + return position; } diff --git a/src/org/traccar/protocol/BceProtocolDecoder.java b/src/org/traccar/protocol/BceProtocolDecoder.java index 31e0868fd..ef70f93d4 100644 --- a/src/org/traccar/protocol/BceProtocolDecoder.java +++ b/src/org/traccar/protocol/BceProtocolDecoder.java @@ -44,8 +44,7 @@ public class BceProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; diff --git a/src/org/traccar/protocol/BlackKiteProtocolDecoder.java b/src/org/traccar/protocol/BlackKiteProtocolDecoder.java index 6b7a8a971..3a32a1dc2 100644 --- a/src/org/traccar/protocol/BlackKiteProtocolDecoder.java +++ b/src/org/traccar/protocol/BlackKiteProtocolDecoder.java @@ -66,8 +66,7 @@ public class BlackKiteProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; diff --git a/src/org/traccar/protocol/CalAmpProtocolDecoder.java b/src/org/traccar/protocol/CalAmpProtocolDecoder.java index 6b959ea82..bd648b0f9 100644 --- a/src/org/traccar/protocol/CalAmpProtocolDecoder.java +++ b/src/org/traccar/protocol/CalAmpProtocolDecoder.java @@ -21,6 +21,7 @@ import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -68,7 +69,6 @@ public class CalAmpProtocolDecoder extends BaseProtocolDecoder { position.setDeviceId(getDeviceId()); position.setProtocol(getProtocolName()); - // Location data position.setTime(new Date(buf.readUnsignedInt() * 1000)); if (type != MSG_MINI_EVENT_REPORT) { buf.readUnsignedInt(); // fix time @@ -84,7 +84,6 @@ public class CalAmpProtocolDecoder extends BaseProtocolDecoder { position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); } - // Fix status if (type == MSG_MINI_EVENT_REPORT) { position.set(Event.KEY_SATELLITES, buf.getUnsignedByte(buf.readerIndex()) & 0xf); position.setValid((buf.readUnsignedByte() & 0x20) == 0); @@ -94,32 +93,22 @@ public class CalAmpProtocolDecoder extends BaseProtocolDecoder { } if (type != MSG_MINI_EVENT_REPORT) { - - // Carrier position.set("carrier", buf.readUnsignedShort()); - - // Cell signal position.set(Event.KEY_GSM, buf.readShort()); - } - // Modem state position.set("modem", buf.readUnsignedByte()); - // HDOP if (type != MSG_MINI_EVENT_REPORT) { position.set(Event.KEY_HDOP, buf.readUnsignedByte()); } - // Inputs position.set(Event.KEY_INPUT, buf.readUnsignedByte()); - // Unit status if (type != MSG_MINI_EVENT_REPORT) { position.set(Event.KEY_STATUS, buf.readUnsignedByte()); } - // Event code if (type == MSG_EVENT_REPORT || type == MSG_MINI_EVENT_REPORT) { if (type != MSG_MINI_EVENT_REPORT) { buf.readUnsignedByte(); // event index @@ -127,10 +116,8 @@ public class CalAmpProtocolDecoder extends BaseProtocolDecoder { position.set(Event.KEY_EVENT, buf.readUnsignedByte()); } - // Accumulators - int accCount = buf.readUnsignedByte(); - int accType = accCount >> 6; - accCount &= 0x3f; + int accType = BitUtil.from(buf.getUnsignedByte(buf.readerIndex()), 6); + int accCount = BitUtil.to(buf.readUnsignedByte(), 6); if (type != MSG_MINI_EVENT_REPORT) { buf.readUnsignedByte(); // reserved @@ -150,20 +137,16 @@ public class CalAmpProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; - // Check options header - if ((buf.getByte(buf.readerIndex()) & 0x80) != 0) { + if (BitUtil.check(buf.getByte(buf.readerIndex()), 7)) { int content = buf.readUnsignedByte(); - // Identifier - if ((content & 0x01) != 0) { + if (BitUtil.check(content, 0)) { - // Read identifier int length = buf.readUnsignedByte(); long id = 0; for (int i = 0; i < length; i++) { @@ -177,34 +160,28 @@ public class CalAmpProtocolDecoder extends BaseProtocolDecoder { identify(String.valueOf(id), channel, remoteAddress); } - // Identifier type - if ((content & 0x02) != 0) { - buf.skipBytes(buf.readUnsignedByte()); + if (BitUtil.check(content, 1)) { + buf.skipBytes(buf.readUnsignedByte()); // identifier type } - // Authentication - if ((content & 0x04) != 0) { - buf.skipBytes(buf.readUnsignedByte()); + if (BitUtil.check(content, 2)) { + buf.skipBytes(buf.readUnsignedByte()); // authentication } - // Routing - if ((content & 0x08) != 0) { - buf.skipBytes(buf.readUnsignedByte()); + if (BitUtil.check(content, 3)) { + buf.skipBytes(buf.readUnsignedByte()); // routing } - // Forwarding - if ((content & 0x10) != 0) { - buf.skipBytes(buf.readUnsignedByte()); + if (BitUtil.check(content, 4)) { + buf.skipBytes(buf.readUnsignedByte()); // forwarding } - // Responce redirection - if ((content & 0x20) != 0) { - buf.skipBytes(buf.readUnsignedByte()); + if (BitUtil.check(content, 5)) { + buf.skipBytes(buf.readUnsignedByte()); // response redirection } } - // Unidentified device if (!hasDeviceId()) { return null; } @@ -213,7 +190,6 @@ public class CalAmpProtocolDecoder extends BaseProtocolDecoder { int type = buf.readUnsignedByte(); int index = buf.readUnsignedShort(); - // Send acknowledgement if (service == SERVICE_ACKNOWLEDGED) { sendResponse(channel, remoteAddress, type, index, 0); } diff --git a/src/org/traccar/protocol/CarTrackProtocolDecoder.java b/src/org/traccar/protocol/CarTrackProtocolDecoder.java index d9bf27973..762017d33 100644 --- a/src/org/traccar/protocol/CarTrackProtocolDecoder.java +++ b/src/org/traccar/protocol/CarTrackProtocolDecoder.java @@ -1,6 +1,6 @@ /* - * Copyright 2014 Anton Tananaev (anton.tananaev@gmail.com) - * Rohit + * Copyright 2014 - 2015 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2014 Rohit * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,12 +17,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -32,100 +32,63 @@ public class CarTrackProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "\\$\\$" + // Header - "(\\d+)\\?*" + // Device ID - "&A" + - "(\\d{4})" + // Command - 2 - "&B" + - "(\\d{2})(\\d{2})(\\d{2})\\.(\\d{3})," + // HHMMSS.DDD - "([AV])," + // STATUS : A= Valid, V = Invalid - "(\\d{2})(\\d{2}\\.\\d{4})," + // Lat : XXMM.DDDDD - "([NS])," + // N/S - "(\\d{3})(\\d{2}\\.\\d{4})," + // Long : YYYMM.DDDD - "([EW])," + // E/W - "(\\d+.\\d*)?," + // Speed in Knots - "(\\d+.\\d*)?," + // Heading - "(\\d{2})(\\d{2})(\\d{2})" + // DDMMYY - ".*" + - "&C(.*)" + // IO Port Data - "&D(.*)" + // Mile Meter Data - "&E(.*)" + // Alarm Data - "(?:&Y)?(.*)"); // AD Input Data + private static final Pattern PATTERN = new PatternBuilder() + .text("$$") // header + .number("(d+)") // device id + .text("?").expression("*") + .text("&A") + .number("(dddd)") // command + .text("&B") + .number("(dd)(dd)(dd).(ddd),") // time + .expression("([AV]),") // validity + .number("(dd)(dd.dddd),") // latitude + .expression("([NS]),") + .number("(ddd)(dd.dddd),") // longitude + .expression("([EW]),") + .number("(d+.d*)?,") // speed + .number("(d+.d*)?,") // course + .number("(dd)(dd)(dd)") // date (ddmmyy) + .any() + .expression("&C([^&]*)") // io + .expression("&D([^&]*)") // odometer + .expression("&E([^&]*)") // alarm + .expression("&Y([^&]*)").optional() // adc + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; - - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - // Get device by unique identifier - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Command - position.set("command", parser.group(index++)); - - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MILLISECOND, Integer.parseInt(parser.group(index++))); - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); - - // Latitude - double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); - - // Longitude - double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; - position.setLongitude(longitude); - - // Speed - String speed = parser.group(index++); - if (speed != null) { - position.setSpeed(Double.parseDouble(speed)); - } + position.set("command", parser.next()); - // Course - String course = parser.group(index++); - if (course != null) { - position.setCourse(Double.parseDouble(course)); - } + DateBuilder dateBuilder = new DateBuilder() + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()); - // Date - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); - // State - position.set(Event.PREFIX_IO + 1, parser.group(index++)); + dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Odometer - String odometer = parser.group(index++); + position.set(Event.PREFIX_IO + 1, parser.next()); + + String odometer = parser.next(); odometer = odometer.replace(":", "A"); odometer = odometer.replace(";", "B"); odometer = odometer.replace("<", "C"); @@ -134,8 +97,9 @@ public class CarTrackProtocolDecoder extends BaseProtocolDecoder { odometer = odometer.replace("?", "F"); position.set(Event.KEY_ODOMETER, Integer.parseInt(odometer, 16)); - position.set(Event.KEY_ALARM, parser.group(index++)); - position.set("ad", parser.group(index++)); + position.set(Event.KEY_ALARM, parser.next()); + position.set(Event.PREFIX_ADC + 1, parser.next()); + return position; } diff --git a/src/org/traccar/protocol/CastelProtocolDecoder.java b/src/org/traccar/protocol/CastelProtocolDecoder.java index fe8d0a525..7d4182f7c 100644 --- a/src/org/traccar/protocol/CastelProtocolDecoder.java +++ b/src/org/traccar/protocol/CastelProtocolDecoder.java @@ -18,15 +18,14 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.nio.ByteOrder; import java.nio.charset.Charset; -import java.util.Calendar; import java.util.LinkedList; import java.util.List; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.helper.Checksum; +import org.traccar.helper.DateBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -37,29 +36,32 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final short MSG_LOGIN = 0x1001; - private static final short MSG_LOGIN_RESPONSE = (short) 0x9001; - private static final short MSG_LOGOUT = 0x1002; - private static final short MSG_HEARTBEAT = 0x1003; - private static final short MSG_HEARTBEAT_RESPONSE = (short) 0x9003; - private static final short MSG_GPS = 0x4001; - private static final short MSG_ALARM = 0x4007; - private static final short MSG_GPS_SLEEP = 0x4009; - private static final short MSG_AGPS_REQUEST = 0x5101; - private static final short MSG_CURRENT_LOCATION = (short) 0xB001; - - private static void readPosition(ChannelBuffer buf, Position position) { - - // Date and time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); - time.set(Calendar.MONTH, buf.readUnsignedByte() - 1); - time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); - time.set(Calendar.HOUR_OF_DAY, buf.readUnsignedByte()); - time.set(Calendar.MINUTE, buf.readUnsignedByte()); - time.set(Calendar.SECOND, buf.readUnsignedByte()); - position.setTime(time.getTime()); + private static final short MSG_SC_LOGIN = 0x1001; + private static final short MSG_SC_LOGIN_RESPONSE = (short) 0x9001; + private static final short MSG_SC_LOGOUT = 0x1002; + private static final short MSG_SC_HEARTBEAT = 0x1003; + private static final short MSG_SC_HEARTBEAT_RESPONSE = (short) 0x9003; + private static final short MSG_SC_GPS = 0x4001; + private static final short MSG_SC_ALARM = 0x4007; + private static final short MSG_SC_GPS_SLEEP = 0x4009; + private static final short MSG_SC_AGPS_REQUEST = 0x5101; + private static final short MSG_SC_CURRENT_LOCATION = (short) 0xB001; + + private static final short MSG_CC_LOGIN = 0x4001; + private static final short MSG_CC_LOGIN_RESPONSE = (short) 0x8001; + private static final short MSG_CC_HEARTBEAT = 0x4206; + private static final short MSG_CC_HEARTBEAT_RESPONSE = (short) 0x8206; + + private Position readPosition(ChannelBuffer buf) { + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(getDeviceId()); + + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + position.setTime(dateBuilder.getDate()); double lat = buf.readUnsignedInt() / 3600000.0; double lon = buf.readUnsignedInt() / 3600000.0; @@ -77,12 +79,36 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { position.setLongitude(lon); position.setValid((flags & 0x0C) > 0); position.set(Event.KEY_SATELLITES, flags >> 4); + + return position; + } + + private void sendResponse( + Channel channel, SocketAddress remoteAddress, + int version, ChannelBuffer id, short type, ChannelBuffer content) { + + if (channel != null) { + int length = 2 + 2 + 1 + id.readableBytes() + 2 + 2 + 2; + if (content != null) { + length += content.readableBytes(); + } + + ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, length); + response.writeByte('@'); response.writeByte('@'); + response.writeShort(length); + response.writeByte(version); + response.writeBytes(id); + response.writeShort(ChannelBuffers.swapShort(type)); + response.writeShort( + Checksum.crc16(Checksum.CRC16_X25, response.toByteBuffer(0, response.writerIndex()))); + response.writeByte(0x0D); response.writeByte(0x0A); + channel.write(response, remoteAddress); + } } @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -92,98 +118,117 @@ public class CastelProtocolDecoder extends BaseProtocolDecoder { ChannelBuffer id = buf.readBytes(20); int type = ChannelBuffers.swapShort(buf.readShort()); - if (type == MSG_HEARTBEAT) { - - if (channel != null) { - ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 31); - response.writeByte(0x40); response.writeByte(0x40); - response.writeShort(response.capacity()); - response.writeByte(version); - response.writeBytes(id); - response.writeShort(ChannelBuffers.swapShort(MSG_HEARTBEAT_RESPONSE)); - response.writeShort( - Checksum.crc16(Checksum.CRC16_X25, response.toByteBuffer(0, response.writerIndex()))); - response.writeByte(0x0D); response.writeByte(0x0A); - channel.write(response, remoteAddress); - } + if (!identify(id.toString(Charset.defaultCharset()).trim(), channel, remoteAddress)) { + return null; + } - } else if (type == MSG_LOGIN || type == MSG_LOGOUT || type == MSG_GPS - || type == MSG_ALARM || type == MSG_CURRENT_LOCATION) { + if (version == 4) { - if (!identify(id.toString(Charset.defaultCharset()).trim(), channel, remoteAddress)) { + if (type == MSG_SC_HEARTBEAT) { - return null; + sendResponse(channel, remoteAddress, version, id, MSG_SC_HEARTBEAT_RESPONSE, null); - } else if (type == MSG_LOGIN && channel != null) { + } else if (type == MSG_SC_LOGIN || type == MSG_SC_LOGOUT || type == MSG_SC_GPS + || type == MSG_SC_ALARM || type == MSG_SC_CURRENT_LOCATION) { - ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 41); - response.writeByte(0x40); response.writeByte(0x40); - response.writeShort(response.capacity()); - response.writeByte(version); - response.writeBytes(id); - response.writeShort(ChannelBuffers.swapShort(MSG_LOGIN_RESPONSE)); - response.writeInt(0xFFFFFFFF); - response.writeShort(0); - response.writeInt((int) (System.currentTimeMillis() / 1000)); - response.writeShort( - Checksum.crc16(Checksum.CRC16_X25, response.toByteBuffer(0, response.writerIndex()))); - response.writeByte(0x0D); response.writeByte(0x0A); - channel.write(response, remoteAddress); + if (type == MSG_SC_LOGIN) { + ChannelBuffer response = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 10); + response.writeInt(0xFFFFFFFF); + response.writeShort(0); + response.writeInt((int) (System.currentTimeMillis() / 1000)); + sendResponse(channel, remoteAddress, version, id, MSG_SC_LOGIN_RESPONSE, response); + } - } + if (type == MSG_SC_GPS) { + buf.readUnsignedByte(); // historical + } else if (type == MSG_SC_ALARM) { + buf.readUnsignedInt(); // alarm + } else if (type == MSG_SC_CURRENT_LOCATION) { + buf.readUnsignedShort(); + } + + buf.readUnsignedInt(); // ACC ON time + buf.readUnsignedInt(); // UTC time + long odometer = buf.readUnsignedInt(); + buf.readUnsignedInt(); // trip odometer + buf.readUnsignedInt(); // total fuel consumption + buf.readUnsignedShort(); // current fuel consumption + long status = buf.readUnsignedInt(); + buf.skipBytes(8); + + int count = buf.readUnsignedByte(); + + List<Position> positions = new LinkedList<>(); + + for (int i = 0; i < count; i++) { + Position position = readPosition(buf); + position.set(Event.KEY_ODOMETER, odometer); + position.set(Event.KEY_STATUS, status); + positions.add(position); + } + + if (!positions.isEmpty()) { + return positions; + } + + } else if (type == MSG_SC_GPS_SLEEP || type == MSG_SC_AGPS_REQUEST) { + + return readPosition(buf); - if (type == MSG_GPS) { - buf.readUnsignedByte(); // historical - } else if (type == MSG_ALARM) { - buf.readUnsignedInt(); // alarm - } else if (type == MSG_CURRENT_LOCATION) { - buf.readUnsignedShort(); } - buf.readUnsignedInt(); // ACC ON time - buf.readUnsignedInt(); // UTC time - long odometer = buf.readUnsignedInt(); - buf.readUnsignedInt(); // trip odometer - buf.readUnsignedInt(); // total fuel consumption - buf.readUnsignedShort(); // current fuel consumption - long status = buf.readUnsignedInt(); - buf.skipBytes(8); + } else { - int count = buf.readUnsignedByte(); + if (type == MSG_CC_HEARTBEAT) { - List<Position> positions = new LinkedList<>(); + sendResponse(channel, remoteAddress, version, id, MSG_CC_HEARTBEAT_RESPONSE, null); - for (int i = 0; i < count; i++) { + buf.readUnsignedByte(); // 0x01 for history + int count = buf.readUnsignedByte(); - Position position = new Position(); - position.setProtocol(getProtocolName()); - position.setDeviceId(getDeviceId()); + List<Position> positions = new LinkedList<>(); - readPosition(buf, position); + for (int i = 0; i < count; i++) { + Position position = readPosition(buf); - position.set(Event.KEY_ODOMETER, odometer); - position.set(Event.KEY_STATUS, status); + position.set(Event.KEY_STATUS, buf.readUnsignedInt()); + position.set(Event.KEY_BATTERY, buf.readUnsignedByte()); + position.set(Event.KEY_ODOMETER, buf.readUnsignedInt()); - positions.add(position); - } + buf.readUnsignedByte(); // geo-fencing id + buf.readUnsignedByte(); // geo-fencing flags + buf.readUnsignedByte(); // additional flags + + position.set(Event.KEY_LAC, buf.readUnsignedShort()); + position.set(Event.KEY_CELL, buf.readUnsignedShort()); + + positions.add(position); + } - if (!positions.isEmpty()) { return positions; - } - } else if (type == MSG_GPS_SLEEP || type == MSG_AGPS_REQUEST) { + } else if (type == MSG_CC_LOGIN) { - if (!identify(id.toString(Charset.defaultCharset()).trim(), channel, remoteAddress)) { - return null; - } + sendResponse(channel, remoteAddress, version, id, MSG_CC_LOGIN_RESPONSE, null); + + Position position = readPosition(buf); + + position.set(Event.KEY_STATUS, buf.readUnsignedInt()); + position.set(Event.KEY_BATTERY, buf.readUnsignedByte()); + position.set(Event.KEY_ODOMETER, buf.readUnsignedInt()); - Position position = new Position(); - position.setProtocol(getProtocolName()); - position.setDeviceId(getDeviceId()); + buf.readUnsignedByte(); // geo-fencing id + buf.readUnsignedByte(); // geo-fencing flags + buf.readUnsignedByte(); // additional flags - readPosition(buf, position); + // GSM_CELL_CODE + // STR_Z - firmware version + // STR_Z - hardware version + + return position; + + } - return position; } return null; diff --git a/src/org/traccar/protocol/CellocatorProtocolDecoder.java b/src/org/traccar/protocol/CellocatorProtocolDecoder.java index 5517e703b..27c94c6ba 100644 --- a/src/org/traccar/protocol/CellocatorProtocolDecoder.java +++ b/src/org/traccar/protocol/CellocatorProtocolDecoder.java @@ -17,12 +17,11 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.nio.ByteOrder; -import java.util.Calendar; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -70,8 +69,7 @@ public class CellocatorProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -84,15 +82,13 @@ public class CellocatorProtocolDecoder extends BaseProtocolDecoder { } byte packetNumber = buf.readByte(); - // Send reply sendReply(channel, deviceUniqueId, packetNumber); - // Parse location if (type == MSG_CLIENT_STATUS) { + Position position = new Position(); position.setProtocol(getProtocolName()); - // Device identifier if (!identify(String.valueOf(deviceUniqueId), channel)) { return null; } @@ -102,7 +98,6 @@ public class CellocatorProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // software version buf.readUnsignedByte(); // protocol version - // Status position.set(Event.KEY_STATUS, buf.getUnsignedByte(buf.readerIndex()) & 0x0f); int operator = (buf.readUnsignedByte() & 0xf0) << 4; @@ -128,23 +123,17 @@ public class CellocatorProtocolDecoder extends BaseProtocolDecoder { position.setValid(buf.readUnsignedByte() >= 3); // satellites - // Location data position.setLongitude(buf.readInt() / Math.PI * 180 / 100000000); position.setLatitude(buf.readInt() / Math.PI * 180 / 100000000.0); position.setAltitude(buf.readInt() * 0.01); position.setSpeed(UnitsConverter.knotsFromMps(buf.readInt() * 0.01)); position.setCourse(buf.readUnsignedShort() / Math.PI * 180.0 / 1000.0); - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.SECOND, buf.readUnsignedByte()); - time.set(Calendar.MINUTE, buf.readUnsignedByte()); - time.set(Calendar.HOUR_OF_DAY, buf.readUnsignedByte()); - time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); - time.set(Calendar.MONTH, buf.readUnsignedByte() - 1); - time.set(Calendar.YEAR, buf.readUnsignedShort()); - position.setTime(time.getTime()); + DateBuilder dateBuilder = new DateBuilder() + .setTimeReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedShort()); + position.setTime(dateBuilder.getDate()); + return position; } diff --git a/src/org/traccar/protocol/CityeasyProtocolDecoder.java b/src/org/traccar/protocol/CityeasyProtocolDecoder.java index 67da05a8d..213dd90d0 100644 --- a/src/org/traccar/protocol/CityeasyProtocolDecoder.java +++ b/src/org/traccar/protocol/CityeasyProtocolDecoder.java @@ -17,15 +17,15 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.nio.charset.Charset; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.helper.Checksum; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -35,21 +35,24 @@ public class CityeasyProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "(\\d{4})(\\d{2})(\\d{2})" + // Date - "(\\d{2})(\\d{2})(\\d{2})," + // Time - "([AV])," + // Validity - "(\\d+)," + // Satellites - "([NS]),(\\d+\\.\\d+)," + // Latitude - "([EW]),(\\d+\\.\\d+)," + // Longitude - "(\\d+\\.\\d)," + // Speed - "(\\d+\\.\\d)," + // HDOP - "(\\d+\\.\\d);" + // Altitude - "(\\d+)," + // MCC - "(\\d+)," + // MNC - "(\\d+)," + // LAC - "(\\d+)" + // Cell - ".*"); + private static final Pattern PATTERN = new PatternBuilder() + .groupBegin() + .number("(dddd)(dd)(dd)") // date + .number("(dd)(dd)(dd),") // time + .number("([AV]),") // validity + .number("(d+),") // satellites + .number("([NS]),(d+.d+),") // latitude + .number("([EW]),(d+.d+),") // longitude + .number("(d+.d),") // speed + .number("(d+.d),") // hdop + .number("(d+.d)") // altitude + .groupEnd("?").text(";") + .number("(d+),") // mcc + .number("(d+),") // mnc + .number("(d+),") // lac + .number("(d+)") // cell + .any() + .compile(); public static final int MSG_ADDRESS_REQUEST = 0x0001; public static final int MSG_STATUS = 0x0002; @@ -62,8 +65,7 @@ public class CityeasyProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -80,7 +82,7 @@ public class CityeasyProtocolDecoder extends BaseProtocolDecoder { if (type == MSG_LOCATION_REPORT || type == MSG_LOCATION_REQUEST) { String sentence = buf.toString(buf.readerIndex(), buf.readableBytes() - 8, Charset.defaultCharset()); - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, sentence); if (!parser.matches()) { return null; } @@ -89,46 +91,33 @@ public class CityeasyProtocolDecoder extends BaseProtocolDecoder { position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); - Integer index = 1; - - // Date and time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); - - position.setValid(parser.group(index++).equals("A")); - position.set(Event.KEY_SATELLITES, parser.group(index++)); - - // Latitude - String hemisphere = parser.group(index++); - double latitude = Double.parseDouble(parser.group(index++)); - if (hemisphere.compareTo("S") == 0) { - latitude = -latitude; - } - position.setLatitude(latitude); + if (parser.hasNext(15)) { - // Longitude - hemisphere = parser.group(index++); - double longitude = Double.parseDouble(parser.group(index++)); - if (hemisphere.compareTo("W") == 0) { - longitude = -longitude; - } - position.setLongitude(longitude); + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + position.setValid(parser.next().equals("A")); + position.set(Event.KEY_SATELLITES, parser.next()); + + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG)); - position.setSpeed(Double.parseDouble(parser.group(index++))); - position.set(Event.KEY_HDOP, Double.parseDouble(parser.group(index++))); - position.setAltitude(Double.parseDouble(parser.group(index++))); + position.setSpeed(parser.nextDouble()); + position.set(Event.KEY_HDOP, parser.nextDouble()); + position.setAltitude(parser.nextDouble()); + + } else { + + getLastLocation(position, null); + + } - position.set(Event.KEY_MCC, Integer.parseInt(parser.group(index++))); - position.set(Event.KEY_MNC, Integer.parseInt(parser.group(index++))); - position.set(Event.KEY_LAC, Integer.parseInt(parser.group(index++))); - position.set(Event.KEY_CELL, Integer.parseInt(parser.group(index++))); + position.set(Event.KEY_MCC, parser.nextInt()); + position.set(Event.KEY_MNC, parser.nextInt()); + position.set(Event.KEY_LAC, parser.nextInt()); + position.set(Event.KEY_CELL, parser.nextInt()); return position; } diff --git a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java b/src/org/traccar/protocol/EasyTrackProtocolDecoder.java index 21553fa44..352fc97dc 100644 --- a/src/org/traccar/protocol/EasyTrackProtocolDecoder.java +++ b/src/org/traccar/protocol/EasyTrackProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,13 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -31,105 +32,81 @@ public class EasyTrackProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "\\*..," + // Manufacturer - "(\\d+)," + // IMEI - "([^,]{2})," + // Command - "([AV])," + // Validity - "(\\p{XDigit}{2})" + // Year - "(\\p{XDigit}{2})" + // Month - "(\\p{XDigit}{2})," + // Day - "(\\p{XDigit}{2})" + // Hours - "(\\p{XDigit}{2})" + // Minutes - "(\\p{XDigit}{2})," + // Seconds - "(\\p{XDigit})" + - "(\\p{XDigit}{7})," + // Latitude - "(\\p{XDigit})" + - "(\\p{XDigit}{7})," + // Longitude - "(\\p{XDigit}{4})," + // Speed - "(\\p{XDigit}{4})," + // Course - "(\\p{XDigit}{8})," + // Status - "(\\p{XDigit}+)," + // Signal - "(\\d+)," + // Power - "(\\p{XDigit}{4})," + // Oil - "(\\p{XDigit}+),?" + // Odometer - "(\\d+)?" + // Altitude - ".*"); + private static final Pattern PATTERN = new PatternBuilder() + .text("*").expression("..,") // manufacturer + .number("(d+),") // imei + .expression("([^,]{2}),") // command + .expression("([AV]),") // validity + .number("(xx)") // year + .number("(xx)") // month + .number("(xx),") // day + .number("(xx)") // hours + .number("(xx)") // minutes + .number("(xx),") // seconds + .number("(x)") + .number("(x{7}),") // latitude + .number("(x)") + .number("(x{7}),") // longitude + .number("(x{4}),") // speed + .number("(x{4}),") // course + .number("(x{8}),") // status + .number("(x+),") // signal + .number("(d+),") // power + .number("(x{4}),") // oil + .number("(x+),?") // odometer + .number("(d+)?") // altitude + .any() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; - - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - - // Get device by IMEI - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Command - position.set("command", parser.group(index++)); - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); - - // Date - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++), 16)); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++), 16) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++), 16)); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++), 16)); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++), 16)); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++), 16)); - position.setTime(time.getTime()); + position.set("command", parser.next()); - // Location - int hemisphere = parser.group(index++).equals("8") ? -1 : 1; - position.setLatitude( - hemisphere * Integer.parseInt(parser.group(index++), 16) / 600000.0); + position.setValid(parser.next().equals("A")); - hemisphere = parser.group(index++).equals("8") ? -1 : 1; - position.setLongitude( - hemisphere * Integer.parseInt(parser.group(index++), 16) / 600000.0); + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(16), parser.nextInt(16), parser.nextInt(16)) + .setTime(parser.nextInt(16), parser.nextInt(16), parser.nextInt(16)); + position.setTime(dateBuilder.getDate()); - position.setSpeed(Integer.parseInt(parser.group(index++), 16) / 100.0); - position.setCourse(Integer.parseInt(parser.group(index++), 16) / 100.0); - - // Status - position.set(Event.KEY_STATUS, parser.group(index++)); + if (BitUtil.check(parser.nextInt(16), 7)) { + position.setLatitude(-parser.nextInt(16) / 600000.0); + } else { + position.setLatitude(parser.nextInt(16) / 600000.0); + } - // Signal - position.set("signal", parser.group(index++)); + if (BitUtil.check(parser.nextInt(16), 7)) { + position.setLongitude(-parser.nextInt(16) / 600000.0); + } else { + position.setLongitude(parser.nextInt(16) / 600000.0); + } - // Power - position.set(Event.KEY_POWER, Double.parseDouble(parser.group(index++))); + position.setSpeed(parser.nextInt(16) / 100.0); + position.setCourse(parser.nextInt(16) / 100.0); - // Oil - position.set("oil", Integer.parseInt(parser.group(index++), 16)); + position.set(Event.KEY_STATUS, parser.next()); + position.set("signal", parser.next()); + position.set(Event.KEY_POWER, parser.nextDouble()); + position.set("oil", parser.nextInt(16)); + position.set(Event.KEY_ODOMETER, parser.nextInt(16)); - // Odometer - position.set(Event.KEY_ODOMETER, Integer.parseInt(parser.group(index++), 16)); + position.setAltitude(parser.nextDouble()); - // Altitude - String altitude = parser.group(index++); - if (altitude != null) { - position.setAltitude(Double.parseDouble(altitude)); - } return position; } diff --git a/src/org/traccar/protocol/EelinkProtocolDecoder.java b/src/org/traccar/protocol/EelinkProtocolDecoder.java index 0106e6049..6d77abc1b 100644 --- a/src/org/traccar/protocol/EelinkProtocolDecoder.java +++ b/src/org/traccar/protocol/EelinkProtocolDecoder.java @@ -54,8 +54,7 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -75,24 +74,20 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { } else if (hasDeviceId() && (type == MSG_GPS || type == MSG_ALARM || type == MSG_STATE || type == MSG_SMS)) { - // Create new position Position position = new Position(); position.setDeviceId(getDeviceId()); position.setProtocol(getProtocolName()); position.set(Event.KEY_INDEX, index); - // Location position.setTime(new Date(buf.readUnsignedInt() * 1000)); position.setLatitude(buf.readInt() / 1800000.0); position.setLongitude(buf.readInt() / 1800000.0); position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); position.setCourse(buf.readUnsignedShort()); - // Cell position.set(Event.KEY_CELL, ChannelBuffers.hexDump(buf.readBytes(9))); - // Validity position.setValid((buf.readUnsignedByte() & 0x01) != 0); if (type == MSG_ALARM) { @@ -102,7 +97,9 @@ public class EelinkProtocolDecoder extends BaseProtocolDecoder { if (type == MSG_STATE) { position.set(Event.KEY_STATUS, buf.readUnsignedByte()); } + return position; + } return null; diff --git a/src/org/traccar/protocol/EnforaProtocolDecoder.java b/src/org/traccar/protocol/EnforaProtocolDecoder.java index ef34d6607..424182da9 100644 --- a/src/org/traccar/protocol/EnforaProtocolDecoder.java +++ b/src/org/traccar/protocol/EnforaProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,14 +17,14 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.nio.charset.Charset; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBufferIndexFinder; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.helper.Log; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.helper.StringFinder; import org.traccar.model.Position; @@ -34,110 +34,80 @@ public class EnforaProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "GPRMC," + - "(\\d{2})(\\d{2})(\\d{2}).(\\d+)," + // Time (HHMMSS.SS) - "([AV])," + // Validity - "(\\d{2})(\\d{2}.\\d+)," + // Latitude (DDMM.MMMMMM) - "([NS])," + - "(\\d{3})(\\d{2}.\\d+)," + // Longitude (DDDMM.MMMMMM) - "([EW])," + - "(\\d+.\\d+)?," + // Speed - "(\\d+.\\d+)?," + // Course - "(\\d{2})(\\d{2})(\\d{2})," + // Date (DDMMYY) - ".*[\r\n\u0000]*"); + private static final Pattern PATTERN = new PatternBuilder() + .text("GPRMC,") + .number("(dd)(dd)(dd).(d+),") // time + .expression("([AV]),") // validity + .number("(dd)(dd.d+),") // latitude + .expression("([NS]),") + .number("(ddd)(dd.d+),") // longitude + .expression("([EW]),") + .number("(d+.d+)?,") // speed + .number("(d+.d+)?,") // course + .number("(dd)(dd)(dd),") // date (ddmmyy) + .any() + .compile(); public static final int IMEI_LENGTH = 15; @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; - // Find IMEI (Modem ID) - String imei = null; - for (int first = -1, i = 0; i < buf.readableBytes(); i++) { - if (!Character.isDigit((char) buf.getByte(i))) { - first = i + 1; - } - - // Found digit string - if (i - first == IMEI_LENGTH - 1) { - imei = buf.toString(first, IMEI_LENGTH, Charset.defaultCharset()); - break; + // Find IMEI number + int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new ChannelBufferIndexFinder() { + @Override + public boolean find(ChannelBuffer buffer, int guessedIndex) { + if (buffer.writerIndex() - guessedIndex >= IMEI_LENGTH) { + for (int i = 0; i < IMEI_LENGTH; i++) { + if (!Character.isDigit((char) buffer.getByte(guessedIndex + i))) { + return false; + } + } + return true; + } + return false; } + }); + if (index == -1) { + return null; } - // Write log - if (imei == null) { - Log.warning("Enfora decoder failed to find IMEI"); + String imei = buf.toString(index, IMEI_LENGTH, Charset.defaultCharset()); + if (!identify(imei, channel)) { return null; } - // Find GPSMC string + // Find NMEA sentence int start = buf.indexOf(buf.readerIndex(), buf.writerIndex(), new StringFinder("GPRMC")); if (start == -1) { - // Message does not contain GPS data return null; } - String sentence = buf.toString(start, buf.readableBytes() - start, Charset.defaultCharset()); - // Parse message - Matcher parser = PATTERN.matcher(sentence); + String sentence = buf.toString(start, buf.readableBytes() - start, Charset.defaultCharset()); + Parser parser = new Parser(PATTERN, sentence); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - - // Get device by IMEI - if (!identify(imei, channel)) { - return null; - } position.setDeviceId(getDeviceId()); - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MILLISECOND, Integer.parseInt(parser.group(index++)) * 10); - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); - - // Latitude - double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); - - // Longitude - double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; - position.setLongitude(longitude); - - // Speed - position.setSpeed(Double.parseDouble(parser.group(index++))); - - // Course - String course = parser.group(index++); - if (course != null) { - position.setCourse(Double.parseDouble(course)); - } + DateBuilder dateBuilder = new DateBuilder() + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()); + + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + + dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Date - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); return position; } diff --git a/src/org/traccar/protocol/Ev603ProtocolDecoder.java b/src/org/traccar/protocol/Ev603ProtocolDecoder.java deleted file mode 100644 index 451aff7f2..000000000 --- a/src/org/traccar/protocol/Ev603ProtocolDecoder.java +++ /dev/null @@ -1,103 +0,0 @@ -/*
- * Copyright 2012 - 2013 Anton Tananaev (anton.tananaev@gmail.com)
- * Luis Parada (luis.parada@gmail.com)
- *
- * 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 java.net.SocketAddress;
-import java.util.Calendar;
-import java.util.TimeZone;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import org.jboss.netty.channel.Channel;
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.model.Position;
-
-public class Ev603ProtocolDecoder extends BaseProtocolDecoder {
-
- public Ev603ProtocolDecoder(Ev603Protocol protocol) {
- super(protocol);
- }
-
- private static final Pattern PATTERN = Pattern.compile(
- "!.," + // Type
- "(\\d{2})/(\\d{2})/(\\d{2})," + // Date dd/mm/YY
- "(\\d{2}):(\\d{2}):(\\d{2})," + // Time hh:mm:ss
- "(-?\\d+\\.\\d+)," + // Latitude
- "(-?\\d+\\.\\d+)," + // Longitude
- "(\\d+\\.?\\d*)," + // Speed
- "(\\d+\\.?\\d*)," + // Course
- ".*");
-
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg)
- throws Exception {
-
- String sentence = (String) msg;
-
- // Detect device ID
- if (sentence.startsWith("!1,")) {
-
- identify(sentence.substring(3), channel);
-
- } else {
-
- // Parse message
- Matcher parser = PATTERN.matcher(sentence);
- if (!hasDeviceId() || !parser.matches()) {
- return null;
- }
-
- // Create new position
- Position position = new Position();
- position.setDeviceId(getDeviceId());
- position.setProtocol(getProtocolName());
- Integer index = 1;
-
- // Date
- Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- time.clear();
- time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++)));
- time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1);
- time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++)));
-
- // Time
- time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++)));
- time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++)));
- time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++)));
- position.setTime(time.getTime());
-
- // Validity
- position.setValid(true);
-
- // Coordinates
- position.setLatitude(Double.parseDouble(parser.group(index++)));
- position.setLongitude(Double.parseDouble(parser.group(index++)));
-
- // Speed
- position.setSpeed(Double.parseDouble(parser.group(index++)));
-
- // Course
- position.setCourse(Double.parseDouble(parser.group(index++)));
- if (position.getCourse() > 360) {
- position.setCourse(0);
- }
- return position;
- }
-
- return null;
- }
-}
diff --git a/src/org/traccar/protocol/FlextrackProtocolDecoder.java b/src/org/traccar/protocol/FlextrackProtocolDecoder.java index e89e31aaf..a8acc42be 100644 --- a/src/org/traccar/protocol/FlextrackProtocolDecoder.java +++ b/src/org/traccar/protocol/FlextrackProtocolDecoder.java @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -32,34 +32,36 @@ public class FlextrackProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN_LOGON = Pattern.compile( - "(-?\\d+)," + // Index - "LOGON," + - "(\\d+)," + // Node ID - "(\\d+)"); // ICCID - - private static final Pattern PATTERN = Pattern.compile( - "(-?\\d+)," + // Index - "UNITSTAT," + - "(\\d{4})(\\d{2})(\\d{2})," + // Date (YYYYMMDD) - "(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS) - "\\d+," + // Node ID - "([NS])(\\d+)\\.(\\d+\\.\\d+)," + // Longitude - "([EW])(\\d+)\\.(\\d+\\.\\d+)," + // Latitude - "(\\d+)," + // Speed - "(\\d+)," + // Course - "(\\d+)," + // Satellites - "(\\d+)," + // Battery - "(-?\\d+)," + // GSM - "(\\p{XDigit}+)," + // State - "(\\d{3})" + // MCC - "(\\d{2})," + // MNC - "(-?\\d+)," + // Altitude - "(\\d+)," + // HDOP - "(\\p{XDigit}+)," + // Cell - "\\d+," + // GPS fix time - "(\\p{XDigit}+)," + // LAC - "(\\d+)"); // Odometer + private static final Pattern PATTERN_LOGON = new PatternBuilder() + .number("(-?d+),") // index + .text("LOGON,") + .number("(d+),") // node id + .number("(d+)") // iccid + .compile(); + + private static final Pattern PATTERN = new PatternBuilder() + .number("(-?d+),") // index + .text("UNITSTAT,") + .number("(dddd)(dd)(dd),") // date + .number("(dd)(dd)(dd),") // time + .number("d+,") // node id + .number("([NS])(d+).(d+.d+),") // latitude + .number("([EW])(d+).(d+.d+),") // longitude + .number("(d+),") // speed + .number("(d+),") // course + .number("(d+),") // satellites + .number("(d+),") // battery + .number("(-?d+),") // gsm + .number("(x+),") // state + .number("(ddd)") // mcc + .number("(dd),") // mnc + .number("(-?d+),") // altitude + .number("(d+),") // hdop + .number("(x+),") // cell + .number("d+,") // gps fix time + .number("(x+),") // lac + .number("(d+)") // odometer + .compile(); private void sendAcknowledgement(Channel channel, String index) { if (channel != null) { @@ -69,24 +71,21 @@ public class FlextrackProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; if (sentence.contains("LOGON")) { - Matcher parser = PATTERN_LOGON.matcher(sentence); + Parser parser = new Parser(PATTERN_LOGON, sentence); if (!parser.matches()) { return null; } - int index = 1; + sendAcknowledgement(channel, parser.next()); - sendAcknowledgement(channel, parser.group(index++)); - - String id = parser.group(index++); - String iccid = parser.group(index++); + String id = parser.next(); + String iccid = parser.next(); if (!identify(iccid, channel, null, false) && !identify(id, channel)) { return null; @@ -94,7 +93,7 @@ public class FlextrackProtocolDecoder extends BaseProtocolDecoder { } else if (sentence.contains("UNITSTAT") && hasDeviceId()) { - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, sentence); if (!parser.matches()) { return null; } @@ -103,56 +102,32 @@ public class FlextrackProtocolDecoder extends BaseProtocolDecoder { position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); - int index = 1; - - sendAcknowledgement(channel, parser.group(index++)); - - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); - - // Latitude - String hemisphere = parser.group(index++); - double lat = Integer.parseInt(parser.group(index++)); - lat += Double.parseDouble(parser.group(index++)) / 60; - if (hemisphere.equals("S")) { - lat = -lat; - } - position.setLatitude(lat); - - // Longitude - hemisphere = parser.group(index++); - double lon = Integer.parseInt(parser.group(index++)); - lon += Double.parseDouble(parser.group(index++)) / 60; - if (hemisphere.equals("W")) { - lon = -lon; - } - position.setLongitude(lon); + sendAcknowledgement(channel, parser.next()); + + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); position.setValid(true); - position.setSpeed(UnitsConverter.knotsFromKph(Integer.parseInt(parser.group(index++)))); - position.setCourse(Integer.parseInt(parser.group(index++))); - - position.set(Event.KEY_SATELLITES, Integer.parseInt(parser.group(index++))); - position.set(Event.KEY_BATTERY, Integer.parseInt(parser.group(index++))); - position.set(Event.KEY_GSM, Integer.parseInt(parser.group(index++))); - position.set(Event.KEY_STATUS, Integer.parseInt(parser.group(index++), 16)); - position.set(Event.KEY_MCC, Integer.parseInt(parser.group(index++))); - position.set(Event.KEY_MNC, Integer.parseInt(parser.group(index++))); - - position.setAltitude(Integer.parseInt(parser.group(index++))); - - position.set(Event.KEY_HDOP, Integer.parseInt(parser.group(index++)) / 10.0); - position.set(Event.KEY_CELL, parser.group(index++)); - position.set(Event.KEY_LAC, parser.group(index++)); - position.set(Event.KEY_ODOMETER, Integer.parseInt(parser.group(index++))); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN)); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextInt())); + position.setCourse(parser.nextInt()); + + position.set(Event.KEY_SATELLITES, parser.nextInt()); + position.set(Event.KEY_BATTERY, parser.nextInt()); + position.set(Event.KEY_GSM, parser.nextInt()); + position.set(Event.KEY_STATUS, parser.nextInt(16)); + position.set(Event.KEY_MCC, parser.nextInt()); + position.set(Event.KEY_MNC, parser.nextInt()); + + position.setAltitude(parser.nextInt()); + + position.set(Event.KEY_HDOP, parser.nextInt() * 0.1); + position.set(Event.KEY_CELL, parser.next()); + position.set(Event.KEY_LAC, parser.next()); + position.set(Event.KEY_ODOMETER, parser.nextInt()); return position; } diff --git a/src/org/traccar/protocol/FreedomProtocolDecoder.java b/src/org/traccar/protocol/FreedomProtocolDecoder.java index c5ef5a146..e5f0dceee 100644 --- a/src/org/traccar/protocol/FreedomProtocolDecoder.java +++ b/src/org/traccar/protocol/FreedomProtocolDecoder.java @@ -45,8 +45,7 @@ public class FreedomProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { diff --git a/src/org/traccar/protocol/GalileoProtocolDecoder.java b/src/org/traccar/protocol/GalileoProtocolDecoder.java index ea8ac08e7..ce8716291 100644 --- a/src/org/traccar/protocol/GalileoProtocolDecoder.java +++ b/src/org/traccar/protocol/GalileoProtocolDecoder.java @@ -112,8 +112,7 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -123,8 +122,8 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { List<Position> positions = new LinkedList<>(); Set<Integer> tags = new HashSet<>(); boolean hasLocation = false; + Position position = new Position(); - position.setProtocol(getProtocolName()); while (buf.readerIndex() < length) { @@ -202,6 +201,7 @@ public class GalileoProtocolDecoder extends BaseProtocolDecoder { sendReply(channel, buf.readUnsignedShort()); for (Position p : positions) { + p.setProtocol(getProtocolName()); p.setDeviceId(getDeviceId()); } diff --git a/src/org/traccar/protocol/GatorProtocolDecoder.java b/src/org/traccar/protocol/GatorProtocolDecoder.java index 2bf10b14c..aad771099 100644 --- a/src/org/traccar/protocol/GatorProtocolDecoder.java +++ b/src/org/traccar/protocol/GatorProtocolDecoder.java @@ -16,12 +16,11 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.helper.ChannelBufferTools; +import org.traccar.helper.DateBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -57,8 +56,7 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -81,42 +79,30 @@ public class GatorProtocolDecoder extends BaseProtocolDecoder { } position.setDeviceId(getDeviceId()); - // Date and time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.MONTH, ChannelBufferTools.readHexInteger(buf, 2) - 1); - time.set(Calendar.DAY_OF_MONTH, ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.HOUR_OF_DAY, ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.MINUTE, ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.SECOND, ChannelBufferTools.readHexInteger(buf, 2)); - position.setTime(time.getTime()); - - // Location + DateBuilder dateBuilder = new DateBuilder() + .setYear(ChannelBufferTools.readHexInteger(buf, 2)) + .setMonth(ChannelBufferTools.readHexInteger(buf, 2)) + .setDay(ChannelBufferTools.readHexInteger(buf, 2)) + .setHour(ChannelBufferTools.readHexInteger(buf, 2)) + .setMinute(ChannelBufferTools.readHexInteger(buf, 2)) + .setSecond(ChannelBufferTools.readHexInteger(buf, 2)); + position.setTime(dateBuilder.getDate()); + position.setLatitude(ChannelBufferTools.readCoordinate(buf)); position.setLongitude(ChannelBufferTools.readCoordinate(buf)); position.setSpeed(UnitsConverter.knotsFromKph(ChannelBufferTools.readHexInteger(buf, 4))); position.setCourse(ChannelBufferTools.readHexInteger(buf, 4)); - // Flags int flags = buf.readUnsignedByte(); position.setValid((flags & 0x80) != 0); position.set(Event.KEY_SATELLITES, flags & 0x0f); - // Status position.set(Event.KEY_STATUS, buf.readUnsignedByte()); - - // Key switch position.set("key", buf.readUnsignedByte()); - - // Oil position.set("oil", buf.readUnsignedShort() / 10.0); - - // Power position.set(Event.KEY_POWER, buf.readUnsignedByte() + buf.readUnsignedByte() / 100.0); - - // Odometer position.set(Event.KEY_ODOMETER, buf.readUnsignedInt()); + return position; } diff --git a/src/org/traccar/protocol/Gl100ProtocolDecoder.java b/src/org/traccar/protocol/Gl100ProtocolDecoder.java index db22fdb34..2995230ee 100644 --- a/src/org/traccar/protocol/Gl100ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gl100ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2013 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Position; public class Gl100ProtocolDecoder extends BaseProtocolDecoder { @@ -30,77 +30,66 @@ public class Gl100ProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "\\+RESP:GT...," + - "(\\d{15})," + // IMEI - "(?:(?:\\d+," + // Number - "\\d," + // Reserved / Geofence id - "\\d)|" + // Reserved / Geofence alert - "(?:[^,]*))," + // Calling number - "([01])," + // GPS fix - "(\\d+.\\d)," + // Speed - "(\\d+)," + // Course - "(-?\\d+.\\d)," + // Altitude - "\\d*," + // GPS accuracy - "(-?\\d+.\\d+)," + // Longitude - "(-?\\d+.\\d+)," + // Latitude - "(\\d{4})(\\d{2})(\\d{2})" + // Date (YYYYMMDD) - "(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS) - ".*"); + private static final Pattern PATTERN = new PatternBuilder() + .text("+RESP:") + .expression("GT...,") + .number("(d{15}),") // imei + .groupBegin() + .number("d+,") // number + .number("d,") // reserved / geofence id + .number("d") // reserved / geofence alert + .or() + .number("[^,]*") // calling number + .groupEnd(",") + .expression("([01]),") // gps fix + .number("(d+.d),") // speed + .number("(d+),") // course + .number("(-?d+.d),") // altitude + .number("d*,") // gps accuracy + .number("(-?d+.d+),") // longitude + .number("(-?d+.d+),") // latitude + .number("(dddd)(dd)(dd)") // date + .number("(dd)(dd)(dd),") // time + .any() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; - // Send response if (sentence.contains("AT+GTHBD=") && channel != null) { String response = "+RESP:GTHBD,GPRS ACTIVE,"; response += sentence.substring(9, sentence.lastIndexOf(',')); response += '\0'; - channel.write(response); + channel.write(response); // heartbeat response } - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, sentence); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - - // Get device by IMEI - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Validity - position.setValid(Integer.parseInt(parser.group(index++)) == 0); - - // Position info - position.setSpeed(Double.parseDouble(parser.group(index++))); - position.setCourse(Double.parseDouble(parser.group(index++))); - position.setAltitude(Double.parseDouble(parser.group(index++))); - position.setLongitude(Double.parseDouble(parser.group(index++))); - position.setLatitude(Double.parseDouble(parser.group(index++))); - - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); + position.setValid(parser.nextInt() == 0); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + position.setAltitude(parser.nextDouble()); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); + + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); return position; } diff --git a/src/org/traccar/protocol/Gl200ProtocolDecoder.java b/src/org/traccar/protocol/Gl200ProtocolDecoder.java index a0d8bd1b2..bf481667e 100644 --- a/src/org/traccar/protocol/Gl200ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gl200ProtocolDecoder.java @@ -131,7 +131,9 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder { protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - Parser parser = new Parser(PATTERN_HEARTBEAT, (String) msg); + String sentence = (String) msg; + + Parser parser = new Parser(PATTERN_HEARTBEAT, sentence); if (parser.matches()) { if (channel != null) { channel.write("+SACK:GTHBD," + parser.next() + "," + parser.next() + "$", remoteAddress); @@ -139,7 +141,7 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder { return null; } - parser = new Parser(PATTERN_INF, (String) msg); + parser = new Parser(PATTERN_INF, sentence); if (parser.matches()) { Position position = new Position(); @@ -166,7 +168,7 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder { return position; } - parser = new Parser(PATTERN, (String) msg); + parser = new Parser(PATTERN, sentence); if (!parser.matches()) { return null; } @@ -179,6 +181,11 @@ public class Gl200ProtocolDecoder extends BaseProtocolDecoder { } position.setDeviceId(getDeviceId()); + // RFID + if (sentence.startsWith("+RESP:GTIDA")) { + position.set(Event.KEY_RFID, sentence.split(",")[5]); + } + // OBD position.set(Event.KEY_RPM, parser.next()); position.set(Event.KEY_OBD_SPEED, parser.next()); diff --git a/src/org/traccar/protocol/GlobalSatProtocolDecoder.java b/src/org/traccar/protocol/GlobalSatProtocolDecoder.java index b4c64f750..2643b6375 100644 --- a/src/org/traccar/protocol/GlobalSatProtocolDecoder.java +++ b/src/org/traccar/protocol/GlobalSatProtocolDecoder.java @@ -53,7 +53,6 @@ public class GlobalSatProtocolDecoder extends BaseProtocolDecoder { channel.write("ACK\r"); } - // Message type String format; if (sentence.startsWith("GSr")) { format = format0; @@ -230,8 +229,7 @@ public class GlobalSatProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; diff --git a/src/org/traccar/protocol/GoSafeProtocolDecoder.java b/src/org/traccar/protocol/GoSafeProtocolDecoder.java index de6a6f425..2fb7522d3 100644 --- a/src/org/traccar/protocol/GoSafeProtocolDecoder.java +++ b/src/org/traccar/protocol/GoSafeProtocolDecoder.java @@ -22,7 +22,10 @@ import java.util.List; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.helper.*; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -100,6 +103,7 @@ public class GoSafeProtocolDecoder extends BaseProtocolDecoder { private Position decodePosition(Parser parser, Date time) { Position position = new Position(); + position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); position.setTime(time); diff --git a/src/org/traccar/protocol/GotopProtocolDecoder.java b/src/org/traccar/protocol/GotopProtocolDecoder.java index 16348cb7f..cfae1b0f6 100644 --- a/src/org/traccar/protocol/GotopProtocolDecoder.java +++ b/src/org/traccar/protocol/GotopProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -32,77 +32,52 @@ public class GotopProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "(\\d+)," + // IMEI - "[^,]+," + // Type - "([AV])," + // Validity - "DATE:(\\d{2})(\\d{2})(\\d{2})," + // Date (YYMMDD) - "TIME:(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS) - "LAT:(\\d+.\\d+)([NS])," + // Latitude - "LOT:(\\d+.\\d+)([EW])," + // Longitude - "Speed:(\\d+.\\d+)," + // Speed - "([^,]+)," + // Status - "(\\d+)?" + // Course - ".*"); + private static final Pattern PATTERN = new PatternBuilder() + .number("(d+),") // imei + .expression("[^,]+,") // type + .expression("([AV]),") // validity + .number("DATE:(dd)(dd)(dd),") // date (yyddmm) + .number("TIME:(dd)(dd)(dd),") // time + .number("LAT:(d+.d+)([NS]),") // latitude + .number("LOT:(d+.d+)([EW]),") // longitude + .text("Speed:").number("(d+.d+),") // speed + .expression("([^,]+),") // status + .number("(d+)?") // course + .any() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - // Parse message - String sentence = (String) msg; - Matcher parser = PATTERN.matcher(sentence); - if (sentence.isEmpty() || !parser.matches()) { + Parser parser = new Parser(PATTERN, (String) msg); + if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - // Get device by IMEI - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); + position.setValid(parser.next().equals("A")); - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Latitude - Double latitude = Double.parseDouble(parser.group(index++)); - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); - // Longitude - Double longitude = Double.parseDouble(parser.group(index++)); - if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; - position.setLongitude(longitude); + position.set(Event.KEY_STATUS, parser.next()); - // Speed - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(parser.group(index++)))); + position.setCourse(parser.nextDouble()); - // Status - position.set(Event.KEY_STATUS, parser.group(index++)); - - // Course - String course = parser.group(index++); - if (course != null) { - position.setCourse(Double.parseDouble(course)); - } return position; } diff --git a/src/org/traccar/protocol/Gps103Protocol.java b/src/org/traccar/protocol/Gps103Protocol.java index dcac04c5b..35515e2e7 100644 --- a/src/org/traccar/protocol/Gps103Protocol.java +++ b/src/org/traccar/protocol/Gps103Protocol.java @@ -35,7 +35,8 @@ public class Gps103Protocol extends BaseProtocol { Command.TYPE_POSITION_PERIODIC, Command.TYPE_POSITION_STOP, Command.TYPE_ENGINE_STOP, - Command.TYPE_ENGINE_RESUME); + Command.TYPE_ENGINE_RESUME, + Command.TYPE_REQUEST_PHOTO); } @Override diff --git a/src/org/traccar/protocol/Gps103ProtocolDecoder.java b/src/org/traccar/protocol/Gps103ProtocolDecoder.java index 9fa71ad58..aa693f42e 100644 --- a/src/org/traccar/protocol/Gps103ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gps103ProtocolDecoder.java @@ -69,8 +69,7 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; @@ -102,7 +101,6 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(); position.setProtocol(getProtocolName()); - // Get device by IMEI String imei = parser.next(); if (!identify(imei, channel, remoteAddress)) { return null; @@ -121,15 +119,15 @@ public class Gps103ProtocolDecoder extends BaseProtocolDecoder { int localHours = parser.nextInt(); int localMinutes = parser.nextInt(); - int utcHours = parser.nextInt(); - int utcMinutes = parser.nextInt(); + String utcHours = parser.next(); + String utcMinutes = parser.next(); dateBuilder.setTime(localHours, localMinutes, parser.nextInt(), parser.nextInt()); // Timezone calculation - if (utcHours != 0 && utcMinutes != 0) { - int deltaMinutes = (localHours - utcHours) * 60; - deltaMinutes += localMinutes - utcMinutes; + if (utcHours != null && utcMinutes != null) { + int deltaMinutes = (localHours - Integer.parseInt(utcHours)) * 60; + deltaMinutes += localMinutes - Integer.parseInt(utcMinutes); if (deltaMinutes <= -12 * 60) { deltaMinutes += 24 * 60; } else if (deltaMinutes > 12 * 60) { diff --git a/src/org/traccar/protocol/Gps103ProtocolEncoder.java b/src/org/traccar/protocol/Gps103ProtocolEncoder.java index 9f0df8761..23942e554 100644 --- a/src/org/traccar/protocol/Gps103ProtocolEncoder.java +++ b/src/org/traccar/protocol/Gps103ProtocolEncoder.java @@ -57,6 +57,8 @@ public class Gps103ProtocolEncoder extends StringProtocolEncoder implements Stri return formatCommand(command, "**,imei:{%s},L", Command.KEY_UNIQUE_ID); case Command.TYPE_ALARM_DISARM: return formatCommand(command, "**,imei:{%s},M", Command.KEY_UNIQUE_ID); + case Command.TYPE_REQUEST_PHOTO: + return formatCommand(command, "**,imei:{%s},160", Command.KEY_UNIQUE_ID); default: Log.warning(new UnsupportedOperationException(command.getType())); break; diff --git a/src/org/traccar/protocol/GpsGateProtocolDecoder.java b/src/org/traccar/protocol/GpsGateProtocolDecoder.java index 5e97a66f8..6c0d663d4 100644 --- a/src/org/traccar/protocol/GpsGateProtocolDecoder.java +++ b/src/org/traccar/protocol/GpsGateProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,13 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.helper.Checksum; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Position; public class GpsGateProtocolDecoder extends BaseProtocolDecoder { @@ -31,18 +31,19 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "\\$GPRMC," + - "(\\d{2})(\\d{2})(\\d{2})\\.?(?:\\d+)?," + // Time (HHMMSS.SSS) - "([AV])," + // Validity - "(\\d{2})(\\d{2}\\.\\d+)," + // Latitude (DDMM.MMMM) - "([NS])," + - "(\\d{3})(\\d{2}\\.\\d+)," + // Longitude (DDDMM.MMMM) - "([EW])," + - "(\\d+\\.\\d+)?," + // Speed - "(\\d+\\.\\d+)?," + // Course - "(\\d{2})(\\d{2})(\\d{2})" + // Date (DDMMYY) - ".+"); // Other (Checksumm) + private static final Pattern PATTERN = new PatternBuilder() + .text("$GPRMC,") + .number("(dd)(dd)(dd).?(d+)?,") // time + .expression("([AV]),") // validity + .number("(dd)(dd.d+),") // latitude + .expression("([NS]),") + .number("(ddd)(dd.d+),") // longitude + .expression("([EW]),") + .number("(d+.d+)?,") // speed + .number("(d+.d+)?,") // course + .number("(dd)(dd)(dd)") // date (ddmmyy) + .any() + .compile(); private void send(Channel channel, String message) { if (channel != null) { @@ -52,8 +53,7 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; @@ -87,58 +87,26 @@ public class GpsGateProtocolDecoder extends BaseProtocolDecoder { } else if (sentence.startsWith("$GPRMC,") && hasDeviceId()) { - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, sentence); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); - Integer index = 1; - - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); - - // Latitude - Double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); - - // Longitude - Double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; - position.setLongitude(longitude); - - // Speed - String speed = parser.group(index++); - if (speed != null) { - position.setSpeed(Double.parseDouble(speed)); - } + DateBuilder dateBuilder = new DateBuilder() + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()); - // Course - String course = parser.group(index++); - if (course != null) { - position.setCourse(Double.parseDouble(course)); - } + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); - // Date - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); + dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); return position; } diff --git a/src/org/traccar/protocol/GpsMarkerProtocolDecoder.java b/src/org/traccar/protocol/GpsMarkerProtocolDecoder.java index c9d9b0cce..9313ad099 100644 --- a/src/org/traccar/protocol/GpsMarkerProtocolDecoder.java +++ b/src/org/traccar/protocol/GpsMarkerProtocolDecoder.java @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -31,99 +31,60 @@ public class GpsMarkerProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "\\$GM" + - "\\d" + // Type - "(?:\\p{XDigit}{2})?" + // Index - "(\\d{15})" + // IMEI - "T(\\d{2})(\\d{2})(\\d{2})" + // Date - "(\\d{2})(\\d{2})(\\d{2})?" + // Time - "([NS])" + - "(\\d{2})(\\d{2}\\d{4})" + // Latitude - "([EW])" + - "(\\d{3})(\\d{2}\\d{4})" + // Longitude - "(\\d{3})" + // Speed - "(\\d{3})" + // Course - "(\\d)" + // Satellites - "(\\d{2})" + // Battery - "(\\d)" + // Input - "(\\d)" + // Output - "(\\d{3})" + // Temperature - ".*"); + private static final Pattern PATTERN = new PatternBuilder() + .text("$GM") + .number("d") // type + .number("(?:xx)?") // index + .number("(d{15})") // imei + .number("T(dd)(dd)(dd)") // date + .number("(dd)(dd)(dd)?") // time + .expression("([NS])") + .number("(dd)(dd)(dddd)") // latitude + .expression("([EW])") + .number("(ddd)(dd)(dddd)") // longitude + .number("(ddd)") // speed + .number("(ddd)") // course + .number("(d)") // satellites + .number("(dd)") // battery + .number("(d)") // input + .number("(d)") // output + .number("(ddd)") // temperature + .any() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; - - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - - // Get device by IMEI - String imei = parser.group(index++); - if (!identify(imei, channel, remoteAddress)) { + if (!identify(parser.next(), channel, remoteAddress)) { return null; } position.setDeviceId(getDeviceId()); - // Date and Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - String seconds = parser.group(index++); - if (seconds != null) { - time.set(Calendar.SECOND, Integer.parseInt(seconds)); - } - position.setTime(time.getTime()); + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Validity position.setValid(true); - - // Latitude - String hemisphere = parser.group(index++); - Double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 600000; - if (hemisphere.compareTo("S") == 0) { - latitude = -latitude; - } - position.setLatitude(latitude); - - // Longitude - hemisphere = parser.group(index++); - Double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 600000; - if (hemisphere.compareTo("W") == 0) { - longitude = -longitude; - } - position.setLongitude(longitude); - - // Speed - position.setSpeed(Double.parseDouble(parser.group(index++))); - - // Course - position.setCourse(Double.parseDouble(parser.group(index++))); - - // Additional data - position.set(Event.KEY_SATELLITES, parser.group(index++)); - position.set(Event.KEY_BATTERY, parser.group(index++)); - position.set(Event.KEY_INPUT, parser.group(index++)); - position.set(Event.KEY_OUTPUT, parser.group(index++)); - position.set(Event.PREFIX_TEMP + 1, parser.group(index++)); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_MIN)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN_MIN)); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + + position.set(Event.KEY_SATELLITES, parser.next()); + position.set(Event.KEY_BATTERY, parser.next()); + position.set(Event.KEY_INPUT, parser.next()); + position.set(Event.KEY_OUTPUT, parser.next()); + position.set(Event.PREFIX_TEMP + 1, parser.next()); return position; } diff --git a/src/org/traccar/protocol/GpsmtaProtocolDecoder.java b/src/org/traccar/protocol/GpsmtaProtocolDecoder.java index 37023181e..0ee207219 100644 --- a/src/org/traccar/protocol/GpsmtaProtocolDecoder.java +++ b/src/org/traccar/protocol/GpsmtaProtocolDecoder.java @@ -32,7 +32,7 @@ public class GpsmtaProtocolDecoder extends BaseProtocolDecoder { } private static final Pattern PATTERN = new PatternBuilder() - .number("(d+) ") // uid + .expression("([^ ]+) ") // uid .number("(d+) ") // time .number("(d+.d+) ") // latitude .number("(d+.d+) ") // longitude diff --git a/src/org/traccar/protocol/Gt02ProtocolDecoder.java b/src/org/traccar/protocol/Gt02ProtocolDecoder.java index 76756f41d..81a04be0a 100644 --- a/src/org/traccar/protocol/Gt02ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gt02ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -49,8 +49,7 @@ public class Gt02ProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -74,50 +73,40 @@ public class Gt02ProtocolDecoder extends BaseProtocolDecoder { } else if (type == MSG_DATA) { - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); position.set(Event.KEY_INDEX, index); - // Get device id if (!identify(imei, channel)) { return null; } position.setDeviceId(getDeviceId()); - // Date and time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); - time.set(Calendar.MONTH, buf.readUnsignedByte() - 1); - time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); - time.set(Calendar.HOUR_OF_DAY, buf.readUnsignedByte()); - time.set(Calendar.MINUTE, buf.readUnsignedByte()); - time.set(Calendar.SECOND, buf.readUnsignedByte()); - position.setTime(time.getTime()); - - // Latitude - double latitude = buf.readUnsignedInt() / (60.0 * 30000.0); + DateBuilder dateBuilder = new DateBuilder() + .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + position.setTime(dateBuilder.getDate()); - // Longitude + double latitude = buf.readUnsignedInt() / (60.0 * 30000.0); double longitude = buf.readUnsignedInt() / (60.0 * 30000.0); - // Speed position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedByte())); - - // Course position.setCourse(buf.readUnsignedShort()); buf.skipBytes(3); // reserved - // Flags long flags = buf.readUnsignedInt(); - position.setValid((flags & 0x1) == 0x1); - if ((flags & 0x2) == 0) latitude = -latitude; - if ((flags & 0x4) == 0) longitude = -longitude; + position.setValid(BitUtil.check(flags, 0)); + if (!BitUtil.check(flags, 1)) { + latitude = -latitude; + } + if (!BitUtil.check(flags, 2)) { + longitude = -longitude; + } position.setLatitude(latitude); position.setLongitude(longitude); + return position; } diff --git a/src/org/traccar/protocol/H02ProtocolDecoder.java b/src/org/traccar/protocol/H02ProtocolDecoder.java index 4cb197c3d..d245fbdc8 100644 --- a/src/org/traccar/protocol/H02ProtocolDecoder.java +++ b/src/org/traccar/protocol/H02ProtocolDecoder.java @@ -170,8 +170,7 @@ public class H02ProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; String marker = buf.toString(0, 1, Charset.defaultCharset()); diff --git a/src/org/traccar/protocol/HaicomProtocolDecoder.java b/src/org/traccar/protocol/HaicomProtocolDecoder.java index 42d806728..db6090650 100644 --- a/src/org/traccar/protocol/HaicomProtocolDecoder.java +++ b/src/org/traccar/protocol/HaicomProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2014 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,13 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -31,93 +32,78 @@ public class HaicomProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "\\$GPRS" + - "(\\d+)," + // IMEI - "([^,]+)," + // Version - "(\\d{2})(\\d{2})(\\d{2})," + // Date - "(\\d{2})(\\d{2})(\\d{2})," + // Time - "(\\d)" + // Flags - "(\\d{2})(\\d{5})" + // Latitude (DDMMMMM) - "(\\d{3})(\\d{5})," + // Longitude (DDDMMMMM) - "(\\d+)," + // Speed - "(\\d+)," + // Course - "(\\d+)," + // Status - "(\\d+)?," + // GPRS counting value - "(\\d+)?," + // GPS power saving counting value - "(\\d+)," + // Switch status - "(\\d+)" + // Relay status - "(?:[LH]{2})?" + // Power status - "#V(\\d+).*"); // Battery + private static final Pattern PATTERN = new PatternBuilder() + .text("$GPRS") + .number("(d+),") // imei + .expression("([^,]+),") // version + .number("(dd)(dd)(dd),") // date + .number("(dd)(dd)(dd),") // time + .number("(d)") // flags + .number("(dd)(d{5})") // latitude + .number("(ddd)(d{5}),") // longitude + .number("(d+),") // speed + .number("(d+),") // course + .number("(d+),") // status + .number("(d+)?,") // gprs counting value + .number("(d+)?,") // gps power saving counting value + .number("(d+),") // switch status + .number("(d+)") // relay status + .expression("(?:[LH]{2})?") // power status + .number("#V(d+)") // battery + .any() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; - - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - - // Get device by IMEI - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Firmware version - position.set(Event.KEY_VERSION, parser.group(index++)); - - // Date - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); - - // Validity - int flags = Integer.parseInt(parser.group(index++)); - position.setValid((flags & 0x1) != 0); - - // Latitude - Double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60000; - if ((flags & 0x4) == 0) latitude = -latitude; - position.setLatitude(latitude); - - // Longitude - Double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 60000; - if ((flags & 0x2) == 0) longitude = -longitude; - position.setLongitude(longitude); - - // Speed - position.setSpeed(Double.parseDouble(parser.group(index++)) / 10); - - // Course - position.setCourse(Double.parseDouble(parser.group(index++)) / 10); - - // Additional data - position.set(Event.KEY_STATUS, parser.group(index++)); - position.set(Event.KEY_GSM, parser.group(index++)); - position.set(Event.KEY_GPS, parser.group(index++)); - position.set(Event.KEY_INPUT, parser.group(index++)); - position.set(Event.KEY_OUTPUT, parser.group(index++)); - position.set(Event.KEY_BATTERY, Double.parseDouble(parser.group(index++)) / 10); + position.set(Event.KEY_VERSION, parser.next()); + + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + int flags = parser.nextInt(); + + position.setValid(BitUtil.check(flags, 0)); + + double latitude = parser.nextDouble() + parser.nextDouble() / 60000; + if (BitUtil.check(flags, 2)) { + position.setLatitude(latitude); + } else { + position.setLatitude(-latitude); + } + + double longitude = parser.nextDouble() + parser.nextDouble() / 60000; + if (BitUtil.check(flags, 1)) { + position.setLongitude(longitude); + } else { + position.setLongitude(-longitude); + } + + position.setSpeed(parser.nextDouble() / 10); + position.setCourse(parser.nextDouble() / 10); + + position.set(Event.KEY_STATUS, parser.next()); + position.set(Event.KEY_GSM, parser.next()); + position.set(Event.KEY_GPS, parser.next()); + position.set(Event.KEY_INPUT, parser.next()); + position.set(Event.KEY_OUTPUT, parser.next()); + position.set(Event.KEY_BATTERY, parser.nextDouble() / 10); return position; } diff --git a/src/org/traccar/protocol/HuabaoFrameDecoder.java b/src/org/traccar/protocol/HuabaoFrameDecoder.java new file mode 100644 index 000000000..003876fe5 --- /dev/null +++ b/src/org/traccar/protocol/HuabaoFrameDecoder.java @@ -0,0 +1,58 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.frame.FrameDecoder; + +public class HuabaoFrameDecoder extends FrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception { + + if (buf.readableBytes() < 2) { + return null; + } + + int index = buf.indexOf(buf.readerIndex() + 1, buf.writerIndex(), (byte) 0x7e); + if (index != -1) { + ChannelBuffer result = ChannelBuffers.buffer(index + 1 - buf.readerIndex()); + + while (buf.readerIndex() <= index) { + int b = buf.readUnsignedByte(); + if (b == 0x7d) { + int ext = buf.readUnsignedByte(); + if (ext == 0x01) { + result.writeByte(0x7d); + } else if (ext == 0x02) { + result.writeByte(0x7e); + } + } else { + result.writeByte(b); + } + } + + return result; + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/HuabaoProtocol.java b/src/org/traccar/protocol/HuabaoProtocol.java new file mode 100644 index 000000000..9f41bb8c6 --- /dev/null +++ b/src/org/traccar/protocol/HuabaoProtocol.java @@ -0,0 +1,42 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.traccar.BaseProtocol; +import org.traccar.TrackerServer; + +import java.util.List; + +public class HuabaoProtocol extends BaseProtocol { + + public HuabaoProtocol() { + super("huabao"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new HuabaoFrameDecoder()); + pipeline.addLast("objectDecoder", new HuabaoProtocolDecoder(HuabaoProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/org/traccar/protocol/HuabaoProtocolDecoder.java new file mode 100644 index 000000000..931f985a5 --- /dev/null +++ b/src/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -0,0 +1,47 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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 java.net.SocketAddress; + +public class HuabaoProtocolDecoder extends BaseProtocolDecoder { + + public HuabaoProtocolDecoder(HuabaoProtocol protocol) { + super(protocol); + } + + public static final int MSG_TERMINAL_REGISTER = 0x0100; + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ChannelBuffer buf = (ChannelBuffer) msg; + + buf.readUnsignedByte(); // start marker + //int type = buf.readUnsignedShort(); + //int flags = buf.readUnsignedShort(); + buf.skipBytes(6); // phone number + buf.readUnsignedShort(); // index + + return null; + } + +} diff --git a/src/org/traccar/protocol/IntellitracProtocolDecoder.java b/src/org/traccar/protocol/IntellitracProtocolDecoder.java index 6c5c0f58a..20a0f9160 100644 --- a/src/org/traccar/protocol/IntellitracProtocolDecoder.java +++ b/src/org/traccar/protocol/IntellitracProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -31,105 +31,88 @@ public class IntellitracProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "(?:.+,)?(\\d+)," + // Device Identifier - "(\\d{4})(\\d{2})(\\d{2})" + // Date (YYYYMMDD) - "(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS) - "(-?\\d+\\.\\d+)," + // Longitude - "(-?\\d+\\.\\d+)," + // Latitude - "(\\d+\\.?\\d*)," + // Speed - "(\\d+\\.?\\d*)," + // Course - "(-?\\d+\\.?\\d*)," + // Altitude - "(\\d+)," + // Satellites - "(\\d+)," + // Report Identifier - "(\\d+)," + // Input - "(\\d+),?" + // Output - "(\\d+\\.\\d+)?,?" + // ADC1 - "(\\d+\\.\\d+)?,?" + // ADC2 - "(?:\\d{14},\\d+," + - "(\\d+)," + // VSS - "(\\d+)," + // RPM - "(-?\\d+)," + // Coolant - "(\\d+)," + // Fuel - "(\\d+)," + // Fuel Consumption - "(-?\\d+)," + // Fuel Temperature - "(\\d+)," + // Charger Pressure - "(\\d+)," + // TPL - "(\\d+)," + // Axle Weight - "(\\d+))?" + // Odometer - ".*"); + private static final Pattern PATTERN = new PatternBuilder() + .expression(".+,").optional() + .number("(d+),") // identifier + .number("(dddd)(dd)(dd)") // date + .number("(dd)(dd)(dd),") // time + .number("(-?d+.d+),") // longitude + .number("(-?d+.d+),") // latitude + .number("(d+.?d*),") // speed + .number("(d+.?d*),") // course + .number("(-?d+.?d*),") // altitude + .number("(d+),") // satellites + .number("(d+),") // index + .number("(d+),") // input + .number("(d+),?") // output + .number("(d+.d+)?,?") // adc1 + .number("(d+.d+)?,?") // adc2 + .groupBegin() + .number("d{14},d+,") + .number("(d+),") // vss + .number("(d+),") // rpm + .number("(-?d+),") // coolant + .number("(d+),") // fuel + .number("(d+),") // fuel consumption + .number("(-?d+),") // fuel temperature + .number("(d+),") // charger pressure + .number("(d+),") // tpl + .number("(d+),") // axle weight + .number("(d+)") // odometer + .groupEnd("?") + .any() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; - - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - // Detect device - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Date and time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); - - // Location data - position.setLongitude(Double.parseDouble(parser.group(index++))); - position.setLatitude(Double.parseDouble(parser.group(index++))); - position.setSpeed(Double.parseDouble(parser.group(index++))); - position.setCourse(Double.parseDouble(parser.group(index++))); - position.setAltitude(Double.parseDouble(parser.group(index++))); - - // Satellites - int satellites = Integer.parseInt(parser.group(index++)); - position.setValid(satellites >= 3); - position.set(Event.KEY_SATELLITES, satellites); + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Report identifier - position.set(Event.KEY_INDEX, Long.parseLong(parser.group(index++))); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + position.setAltitude(parser.nextDouble()); - // Input - position.set(Event.KEY_INPUT, parser.group(index++)); + int satellites = parser.nextInt(); + position.setValid(satellites >= 3); + position.set(Event.KEY_SATELLITES, satellites); - // Output - position.set(Event.KEY_OUTPUT, parser.group(index++)); + position.set(Event.KEY_INDEX, parser.nextLong()); + position.set(Event.KEY_INPUT, parser.next()); + position.set(Event.KEY_OUTPUT, parser.next()); - // ADC - position.set(Event.PREFIX_ADC + 1, parser.group(index++)); - position.set(Event.PREFIX_ADC + 2, parser.group(index++)); + position.set(Event.PREFIX_ADC + 1, parser.next()); + position.set(Event.PREFIX_ADC + 2, parser.next()); // J1939 data - position.set(Event.KEY_OBD_SPEED, parser.group(index++)); - position.set(Event.KEY_RPM, parser.group(index++)); - position.set("coolant", parser.group(index++)); - position.set(Event.KEY_FUEL, parser.group(index++)); - position.set("consumption", parser.group(index++)); - position.set(Event.PREFIX_TEMP + 1, parser.group(index++)); - position.set(Event.KEY_CHARGE, parser.group(index++)); - position.set("tpl", parser.group(index++)); - position.set("axle", parser.group(index++)); - position.set(Event.KEY_ODOMETER, parser.group(index++)); + position.set(Event.KEY_OBD_SPEED, parser.next()); + position.set(Event.KEY_RPM, parser.next()); + position.set("coolant", parser.next()); + position.set(Event.KEY_FUEL, parser.next()); + position.set("consumption", parser.next()); + position.set(Event.PREFIX_TEMP + 1, parser.next()); + position.set(Event.KEY_CHARGE, parser.next()); + position.set("tpl", parser.next()); + position.set("axle", parser.next()); + position.set(Event.KEY_ODOMETER, parser.next()); return position; } diff --git a/src/org/traccar/protocol/Jt600ProtocolDecoder.java b/src/org/traccar/protocol/Jt600ProtocolDecoder.java index ae74a1e83..116625ee9 100644 --- a/src/org/traccar/protocol/Jt600ProtocolDecoder.java +++ b/src/org/traccar/protocol/Jt600ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2013 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +17,16 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.nio.charset.Charset; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.helper.ChannelBufferTools; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -48,58 +49,49 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { buf.readByte(); // header - // Get device by identifier String id = String.valueOf(Long.parseLong(ChannelBuffers.hexDump(buf.readBytes(5)))); if (!identify(id, channel)) { return null; } position.setDeviceId(getDeviceId()); - // Protocol and type int version = ChannelBufferTools.readHexInteger(buf, 1); buf.readUnsignedByte(); // type - buf.readBytes(2); // length - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.DAY_OF_MONTH, ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.MONTH, ChannelBufferTools.readHexInteger(buf, 2) - 1); - time.set(Calendar.YEAR, 2000 + ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.HOUR_OF_DAY, ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.MINUTE, ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.SECOND, ChannelBufferTools.readHexInteger(buf, 2)); - position.setTime(time.getTime()); - - // Coordinates + DateBuilder dateBuilder = new DateBuilder() + .setDay(ChannelBufferTools.readHexInteger(buf, 2)) + .setMonth(ChannelBufferTools.readHexInteger(buf, 2)) + .setYear(ChannelBufferTools.readHexInteger(buf, 2)) + .setHour(ChannelBufferTools.readHexInteger(buf, 2)) + .setMinute(ChannelBufferTools.readHexInteger(buf, 2)) + .setSecond(ChannelBufferTools.readHexInteger(buf, 2)); + position.setTime(dateBuilder.getDate()); + double latitude = convertCoordinate(ChannelBufferTools.readHexInteger(buf, 8)); double longitude = convertCoordinate(ChannelBufferTools.readHexInteger(buf, 9)); - // Flags byte flags = buf.readByte(); position.setValid((flags & 0x1) == 0x1); - if ((flags & 0x2) == 0) latitude = -latitude; + if ((flags & 0x2) == 0) { + latitude = -latitude; + } position.setLatitude(latitude); - if ((flags & 0x4) == 0) longitude = -longitude; + if ((flags & 0x4) == 0) { + longitude = -longitude; + } position.setLongitude(longitude); - // Speed position.setSpeed(ChannelBufferTools.readHexInteger(buf, 2)); - - // Course position.setCourse(buf.readUnsignedByte() * 2.0); if (version == 1) { position.set(Event.KEY_SATELLITES, buf.readUnsignedByte()); - - // Power position.set(Event.KEY_POWER, buf.readUnsignedByte()); buf.readByte(); // other flags and sensors - // Altitude position.setAltitude(buf.readUnsignedShort()); position.set(Event.KEY_CELL, buf.readUnsignedShort()); @@ -117,96 +109,72 @@ public class Jt600ProtocolDecoder extends BaseProtocolDecoder { position.set(Event.KEY_FUEL, fuel); } + return position; } - private static final Pattern PATTERN = Pattern.compile( - "\\(" + - "([\\d]+)," + // Id - "W01," + // Type - "(\\d{3})(\\d{2}.\\d{4})," + // Longitude (DDDMM.MMMM) - "([EW])," + - "(\\d{2})(\\d{2}.\\d{4})," + // Latitude (DDMM.MMMM) - "([NS])," + - "([AV])," + // Validity - "(\\d{2})(\\d{2})(\\d{2})," + // Date (DDMMYY) - "(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS) - "(\\d+)," + // Speed (km/h) - "(\\d+)," + // Course - "(\\d+)," + // Power - "(\\d+)," + // GPS signal - "(\\d+)," + // GSM signal - "(\\d+)," + // Alert Type - ".*\\)"); + private static final Pattern PATTERN = new PatternBuilder() + .text("(") + .number("(d+),") // id + .text("W01,") // type + .number("(ddd)(dd.dddd),") // longitude + .expression("([EW]),") + .number("(dd)(dd.dddd),") // latitude + .expression("([NS]),") + .expression("([AV]),") // validity + .number("(dd)(dd)(dd),") // date (ddmmyy) + .number("(dd)(dd)(dd),") // time + .number("(d+),") // speed + .number("(d+),") // course + .number("(d+),") // power + .number("(d+),") // gps signal + .number("(d+),") // gsm signal + .number("(d+),") // alert type + .any() + .text(")") + .compile(); private Position decodeAlertMessage(ChannelBuffer buf, Channel channel) { - String message = buf.toString(Charset.defaultCharset()); - - // Parse message - Matcher parser = PATTERN.matcher(message); + Parser parser = new Parser(PATTERN, buf.toString(Charset.defaultCharset())); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); + position.set(Event.KEY_ALARM, true); - Integer index = 1; - // Get device by identifier - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Longitude - Double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; - position.setLongitude(longitude); - - // Latitude - Double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); + position.setLongitude(parser.nextCoordinate()); + position.setLatitude(parser.nextCoordinate()); + position.setValid(parser.next().equals("A")); - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Speed - position.setSpeed(Double.parseDouble(parser.group(index++))); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); - // Course - position.setCourse(Double.parseDouble(parser.group(index++))); + position.set(Event.KEY_POWER, parser.nextDouble()); - // Power - position.set(Event.KEY_POWER, Double.parseDouble(parser.group(index++))); return position; } @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; char first = (char) buf.getByte(0); - // Check message type if (first == '$') { return decodeNormalMessage(buf, channel); } else if (first == '(') { diff --git a/src/org/traccar/protocol/KhdProtocolDecoder.java b/src/org/traccar/protocol/KhdProtocolDecoder.java index 1ae192bf5..f6083be70 100644 --- a/src/org/traccar/protocol/KhdProtocolDecoder.java +++ b/src/org/traccar/protocol/KhdProtocolDecoder.java @@ -16,14 +16,13 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.helper.ChannelBufferTools; import org.traccar.helper.Checksum; +import org.traccar.helper.DateBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -60,8 +59,7 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -72,34 +70,28 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder { if (type == MSG_ON_DEMAND || type == MSG_POSITION_UPLOAD || type == MSG_POSITION_REUPLOAD || type == MSG_ALARM || type == MSG_REPLY || type == MSG_PERIPHERAL) { - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - // Device identification if (!identify(readSerialNumber(buf), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Date and time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.MONTH, ChannelBufferTools.readHexInteger(buf, 2) - 1); - time.set(Calendar.DAY_OF_MONTH, ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.HOUR_OF_DAY, ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.MINUTE, ChannelBufferTools.readHexInteger(buf, 2)); - time.set(Calendar.SECOND, ChannelBufferTools.readHexInteger(buf, 2)); - position.setTime(time.getTime()); - - // Location + DateBuilder dateBuilder = new DateBuilder() + .setYear(ChannelBufferTools.readHexInteger(buf, 2)) + .setMonth(ChannelBufferTools.readHexInteger(buf, 2)) + .setDay(ChannelBufferTools.readHexInteger(buf, 2)) + .setHour(ChannelBufferTools.readHexInteger(buf, 2)) + .setMinute(ChannelBufferTools.readHexInteger(buf, 2)) + .setSecond(ChannelBufferTools.readHexInteger(buf, 2)); + position.setTime(dateBuilder.getDate()); + position.setLatitude(ChannelBufferTools.readCoordinate(buf)); position.setLongitude(ChannelBufferTools.readCoordinate(buf)); position.setSpeed(UnitsConverter.knotsFromKph(ChannelBufferTools.readHexInteger(buf, 4))); position.setCourse(ChannelBufferTools.readHexInteger(buf, 4)); - // Flags int flags = buf.readUnsignedByte(); position.setValid((flags & 0x80) != 0); @@ -109,14 +101,10 @@ public class KhdProtocolDecoder extends BaseProtocolDecoder { } else { - // Odometer position.set(Event.KEY_ODOMETER, buf.readUnsignedMedium()); - // Status - buf.skipBytes(4); - - // Other - buf.skipBytes(8); + buf.skipBytes(4); // status + buf.skipBytes(8); // other } diff --git a/src/org/traccar/protocol/M2mProtocolDecoder.java b/src/org/traccar/protocol/M2mProtocolDecoder.java index 6e57b5766..d60303601 100644 --- a/src/org/traccar/protocol/M2mProtocolDecoder.java +++ b/src/org/traccar/protocol/M2mProtocolDecoder.java @@ -16,13 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -36,8 +35,7 @@ public class M2mProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -53,7 +51,6 @@ public class M2mProtocolDecoder extends BaseProtocolDecoder { firstPacket = false; - // Read IMEI StringBuilder imei = new StringBuilder(); for (int i = 0; i < 8; i++) { int b = buf.readByte(); @@ -63,28 +60,23 @@ public class M2mProtocolDecoder extends BaseProtocolDecoder { imei.append(b % 10); } - // Identification identify(imei.toString(), channel); } else if (hasDeviceId()) { - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); - // Date and time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte() & 0x3f); - time.set(Calendar.MONTH, (buf.readUnsignedByte() & 0x3f) - 1); - time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); - time.set(Calendar.HOUR_OF_DAY, buf.readUnsignedByte() & 0x3f); - time.set(Calendar.MINUTE, buf.readUnsignedByte() & 0x7f); - time.set(Calendar.SECOND, buf.readUnsignedByte() & 0x7f); - position.setTime(time.getTime()); - - // Location + DateBuilder dateBuilder = new DateBuilder() + .setDay(buf.readUnsignedByte() & 0x3f) + .setMonth(buf.readUnsignedByte() & 0x3f) + .setYear(buf.readUnsignedByte()) + .setHour(buf.readUnsignedByte() & 0x3f) + .setMinute(buf.readUnsignedByte() & 0x7f) + .setSecond(buf.readUnsignedByte() & 0x7f); + position.setTime(dateBuilder.getDate()); + int degrees = buf.readUnsignedByte(); double latitude = buf.readUnsignedByte(); latitude += buf.readUnsignedByte() / 100.0; @@ -108,19 +100,19 @@ public class M2mProtocolDecoder extends BaseProtocolDecoder { latitude = -latitude; } + position.setValid(true); position.setLatitude(latitude); position.setLongitude(longitude); position.setSpeed(buf.readUnsignedByte()); - // Satellites int satellites = buf.readUnsignedByte(); if (satellites == 0) { return null; // cell information } position.set(Event.KEY_SATELLITES, satellites); - position.setValid(true); - // TODO decode everything else + // decode other data + return position; } diff --git a/src/org/traccar/protocol/MaxonProtocolDecoder.java b/src/org/traccar/protocol/MaxonProtocolDecoder.java deleted file mode 100644 index 14bf285fc..000000000 --- a/src/org/traccar/protocol/MaxonProtocolDecoder.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2012 Alex Wilson <alex@uq.edu.au> - * - * 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 java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.jboss.netty.channel.Channel; -import org.traccar.BaseProtocolDecoder; -import org.traccar.model.Position; - -/** - * Maxon Datamax GPS send protocol (NMEA + GPFID) - * As seen in the MA100-1010 router - * - * It sends its identity after the GPRMC sentence, and with the type - * GPFID. - */ -public class MaxonProtocolDecoder extends BaseProtocolDecoder { - - private Position position = null; - - public MaxonProtocolDecoder(MaxonProtocol protocol) { - super(protocol); - } - - private static final Pattern PATTERN = Pattern.compile( - "\\$GPRMC," + - "(\\d{2})(\\d{2})(\\d{2})\\.(\\d{2})," + // Time (HHMMSS.SSS) - "([AV])," + // Validity - "(\\d{2})(\\d{2}\\.\\d{5})," + // Latitude (DDMM.MMMMM) - "([NS])," + - "(\\d{3})(\\d{2}\\.\\d{5})," + // Longitude (DDDMM.MMMMM) - "([EW])," + - "(\\d+\\.\\d{3})?," + // Speed - "(\\d+\\.\\d{2})?," + // Course - "(\\d{2})(\\d{2})(\\d{2})" + // Date (DDMMYY) - ".+"); // Other (Checksumm) - - private static final Pattern PATTERN_GPFID = Pattern.compile("\\$GPFID,(\\d+)$"); - - @Override - protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - - String sentence = (String) msg; - - // Detect device ID - // Parse message - if (sentence.contains("$GPRMC")) { - - // Parse message - Matcher parser = PATTERN.matcher(sentence); - if (!parser.matches()) { - return null; - } - - // Create new position - position = new Position(); - - Integer index = 1; - - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - index += 1; // Skip milliseconds - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); - - // Latitude - Double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); - - // Longitude - Double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; - position.setLongitude(longitude); - - // Speed - String speed = parser.group(index++); - if (speed != null) { - position.setSpeed(Double.parseDouble(speed)); - } - - // Course - String course = parser.group(index++); - if (course != null) { - position.setCourse(Double.parseDouble(course)); - } - - // Date - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); - - } else if (sentence.contains("$GPFID") && position != null) { - Matcher parser = PATTERN_GPFID.matcher(sentence); - - if (parser.matches()) { - if (!identify(parser.group(1), channel)) { - return null; - } - position.setDeviceId(getDeviceId()); - return position; - } - } - - return null; - } - -} diff --git a/src/org/traccar/protocol/MegastekProtocolDecoder.java b/src/org/traccar/protocol/MegastekProtocolDecoder.java index fd6b2be83..3deff20a8 100644 --- a/src/org/traccar/protocol/MegastekProtocolDecoder.java +++ b/src/org/traccar/protocol/MegastekProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -330,8 +330,7 @@ public class MegastekProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; diff --git a/src/org/traccar/protocol/MeitrackProtocolDecoder.java b/src/org/traccar/protocol/MeitrackProtocolDecoder.java index 872055f19..86fcac375 100644 --- a/src/org/traccar/protocol/MeitrackProtocolDecoder.java +++ b/src/org/traccar/protocol/MeitrackProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,16 +17,16 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.nio.charset.Charset; -import java.util.Calendar; import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -37,138 +37,117 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "\\$\\$." + // Flag - "\\d+," + // Length - "(\\d+)," + // IMEI - "\\p{XDigit}{3}," + // Command - "(?:\\d+,)?" + - "(\\d+)," + // Event - "(-?\\d+\\.\\d+)," + // Latitude - "(-?\\d+\\.\\d+)," + // Longitude - "(\\d{2})(\\d{2})(\\d{2})" + // Date (YYMMDD) - "(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS) - "([AV])," + // Validity - "(\\d+)," + // Satellites - "(\\d+)," + // GSM Signal - "(\\d+\\.?\\d*)," + // Speed - "(\\d+)," + // Course - "(\\d+\\.?\\d*)," + // HDOP - "(-?\\d+)," + // Altitude - "(\\d+)," + // Odometer - "(\\d+)," + // Runtime - "(\\d+\\|\\d+\\|\\p{XDigit}+\\|\\p{XDigit}+)," + // Cell - "(\\p{XDigit}+)," + // State - "(\\p{XDigit}+)?\\|" + // ADC1 - "(\\p{XDigit}+)?\\|" + // ADC2 - "(\\p{XDigit}+)?\\|" + // ADC3 - "(\\p{XDigit}+)\\|" + // Battery - "(\\p{XDigit}+)," + // Power - "(?:([^,]+)?," + // Event Specific - "[^,]*," + // Reserved - "\\d*," + // Protocol - "(\\p{XDigit}{4})?)?" + // Fuel - ".*\\*\\p{XDigit}{2}(?:\r\n)?"); + private static final Pattern PATTERN = new PatternBuilder() + .text("$$").expression(".") // flag + .number("d+,") // length + .number("(d+),") // imei + .number("xxx,") // command + .number("d+,").optional() + .number("(d+),") // event + .number("(-?d+.d+),") // latitude + .number("(-?d+.d+),") // longitude + .number("(dd)(dd)(dd)") // date (ddmmyy) + .number("(dd)(dd)(dd),") // time + .number("([AV]),") // validity + .number("(d+),") // satellites + .number("(d+),") // gsm signal + .number("(d+.?d*),") // speed + .number("(d+),") // course + .number("(d+.?d*),") // hdop + .number("(-?d+),") // altitude + .number("(d+),") // odometer + .number("(d+),") // runtime + .number("(d+)|") // mcc + .number("(d+)|") // mnc + .number("(x+)|") // lac + .number("(x+),") // cell + .number("(x+),") // state + .number("(x+)?|") // adc1 + .number("(x+)?|") // adc2 + .number("(x+)?|") // adc3 + .number("(x+)|") // battery + .number("(x+),") // power + .groupBegin() + .expression("([^,]+)?,") // event specific + .expression("[^,]*,") // reserved + .number("d*,") // protocol + .number("(x{4})?") // fuel + .groupEnd("?") + .any() + .text("*") + .number("xx") + .text("\r\n").optional() + .compile(); private Position decodeRegularMessage(Channel channel, ChannelBuffer buf) { - // Parse message - String sentence = buf.toString(Charset.defaultCharset()); - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, buf.toString(Charset.defaultCharset())); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - - // Identification - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Event - int event = Integer.parseInt(parser.group(index++)); + int event = parser.nextInt(); position.set(Event.KEY_EVENT, event); - // Coordinates - position.setLatitude(Double.parseDouble(parser.group(index++))); - position.setLongitude(Double.parseDouble(parser.group(index++))); - - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); + position.setLatitude(parser.nextDouble()); + position.setLongitude(parser.nextDouble()); - // Satellites - position.set(Event.KEY_SATELLITES, parser.group(index++)); + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // GSM Signal - position.set(Event.KEY_GSM, parser.group(index++)); + position.setValid(parser.next().equals("A")); - // Speed - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(parser.group(index++)))); + position.set(Event.KEY_SATELLITES, parser.next()); + position.set(Event.KEY_GSM, parser.next()); - // Course - position.setCourse(Double.parseDouble(parser.group(index++))); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); - // HDOP - position.set(Event.KEY_HDOP, parser.group(index++)); + position.set(Event.KEY_HDOP, parser.next()); - // Altitude - position.setAltitude(Double.parseDouble(parser.group(index++))); + position.setAltitude(parser.nextDouble()); - // Other - position.set(Event.KEY_ODOMETER, parser.group(index++)); - position.set("runtime", parser.group(index++)); - position.set(Event.KEY_CELL, parser.group(index++)); - position.set(Event.KEY_STATUS, parser.group(index++)); + position.set(Event.KEY_ODOMETER, parser.next()); + position.set("runtime", parser.next()); + position.set(Event.KEY_MCC, parser.next()); + position.set(Event.KEY_MCC, parser.next()); + position.set(Event.KEY_LAC, parser.next()); + position.set(Event.KEY_CELL, parser.next()); + position.set(Event.KEY_STATUS, parser.next()); - // ADC - String adc1 = parser.group(index++); - if (adc1 != null) { - position.set(Event.PREFIX_ADC + 1, Integer.parseInt(adc1, 16)); - } - String adc2 = parser.group(index++); - if (adc2 != null) { - position.set(Event.PREFIX_ADC + 2, Integer.parseInt(adc2, 16)); - } - String adc3 = parser.group(index++); - if (adc3 != null) { - position.set(Event.PREFIX_ADC + 3, Integer.parseInt(adc3, 16)); + for (int i = 1; i <= 3; i++) { + if (parser.hasNext()) { + position.set(Event.PREFIX_ADC + i, parser.nextInt(16)); + } } - position.set(Event.KEY_BATTERY, Integer.parseInt(parser.group(index++), 16)); - position.set(Event.KEY_POWER, Integer.parseInt(parser.group(index++), 16)); - // Event specific - String data = parser.group(index++); - if (data != null && !data.isEmpty()) { + position.set(Event.KEY_BATTERY, parser.nextInt(16)); + position.set(Event.KEY_POWER, parser.nextInt(16)); + + String eventData = parser.next(); + if (eventData != null && !eventData.isEmpty()) { switch (event) { case 37: - position.set(Event.KEY_RFID, data); + position.set(Event.KEY_RFID, eventData); break; default: - position.set("event-data", data); + position.set("event-data", eventData); break; } } - // Fuel - String fuel = parser.group(index++); - if (fuel != null) { + if (parser.hasNext()) { + String fuel = parser.next(); position.set(Event.KEY_FUEL, Integer.parseInt(fuel.substring(0, 2), 16) + Integer.parseInt(fuel.substring(2), 16) * 0.01); } @@ -182,7 +161,6 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { String flag = buf.toString(2, 1, Charset.defaultCharset()); int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) ','); - // Identification String imei = buf.toString(index + 1, 15, Charset.defaultCharset()); if (!identify(imei, channel)) { return null; @@ -196,64 +174,53 @@ public class MeitrackProtocolDecoder extends BaseProtocolDecoder { position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); - // Event position.set(Event.KEY_EVENT, buf.readUnsignedByte()); - // Location position.setLatitude(buf.readInt() * 0.000001); position.setLongitude(buf.readInt() * 0.000001); - // Time (946684800 - timestamp for 2000-01-01) - position.setTime(new Date((946684800 + buf.readUnsignedInt()) * 1000)); + position.setTime(new Date((946684800 + buf.readUnsignedInt()) * 1000)); // 946684800 = 2000-01-01 - // Validity position.setValid(buf.readUnsignedByte() == 1); - // Satellites position.set(Event.KEY_SATELLITES, buf.readUnsignedByte()); - - // GSM Signal position.set(Event.KEY_GSM, buf.readUnsignedByte()); - // Speed position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort())); - - // Course position.setCourse(buf.readUnsignedShort()); - // HDOP position.set(Event.KEY_HDOP, buf.readUnsignedShort() * 0.1); - // Altitude position.setAltitude(buf.readUnsignedShort()); - // Other position.set(Event.KEY_ODOMETER, buf.readUnsignedInt()); position.set("runtime", buf.readUnsignedInt()); - position.set(Event.KEY_CELL, - buf.readUnsignedShort() + "|" + buf.readUnsignedShort() + "|" + - buf.readUnsignedShort() + "|" + buf.readUnsignedShort()); + position.set(Event.KEY_MCC, buf.readUnsignedShort()); + position.set(Event.KEY_MCC, buf.readUnsignedShort()); + position.set(Event.KEY_LAC, buf.readUnsignedShort()); + position.set(Event.KEY_CELL, buf.readUnsignedShort()); position.set(Event.KEY_STATUS, buf.readUnsignedShort()); - // ADC position.set(Event.PREFIX_ADC + 1, buf.readUnsignedShort()); position.set(Event.KEY_BATTERY, buf.readUnsignedShort() * 0.01); position.set(Event.KEY_POWER, buf.readUnsignedShort()); buf.readUnsignedInt(); // geo-fence + positions.add(position); } - // Delete recorded data if (channel != null) { StringBuilder command = new StringBuilder("@@"); command.append(flag).append(27 + positions.size() / 10).append(","); command.append(imei).append(",CCC,").append(positions.size()).append("*"); int checksum = 0; - for (int i = 0; i < command.length(); i += 1) checksum += command.charAt(i); + for (int i = 0; i < command.length(); i += 1) { + checksum += command.charAt(i); + } command.append(String.format("%02x", checksum & 0xff).toUpperCase()); command.append("\r\n"); - channel.write(command.toString()); + channel.write(command.toString()); // delete processed data } return positions; diff --git a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java index 0bb9aee60..5a5ef3964 100644 --- a/src/org/traccar/protocol/MiniFinderProtocolDecoder.java +++ b/src/org/traccar/protocol/MiniFinderProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2014 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,13 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -31,80 +32,76 @@ public class MiniFinderProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "!D," + - "(\\d+)/(\\d+)/(\\d+)," + // Date - "(\\d+):(\\d+):(\\d+)," + // Time - "(-?\\d+\\.\\d+)," + // Latitude - "(-?\\d+\\.\\d+)," + // Longitude - "(\\d+\\.?\\d*)," + // Speed - "(\\d+\\.?\\d*)," + // Course - "(\\p{XDigit}+)," + // Flags - "(-?\\d+\\.\\d+)," + // Altitude - "(\\d+)," + // Battery - "(\\d+)," + // Satellites in use - "(\\d+)," + // Satellites in view - "0"); + private static final Pattern PATTERN = new PatternBuilder() + .expression("![AD],") + .number("(d+)/(d+)/(d+),") // date + .number("(d+):(d+):(d+),") // time + .number("(-?d+.d+),") // latitude + .number("(-?d+.d+),") // longitude + .number("(d+.?d*),") // speed + .number("(d+.?d*),") // course + .groupBegin() + .number("(x+),") // flags + .number("(-?d+.d+),") // altitude + .number("(d+),") // battery + .number("(d+),") // satellites in use + .number("(d+),") // satellites in view + .text("0") + .or() + .any() + .groupEnd() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; if (sentence.startsWith("!1")) { - // Identification identify(sentence.substring(3, sentence.length()), channel); - } else if (sentence.startsWith("!D") && hasDeviceId()) { + } else if ((sentence.startsWith("!D") || sentence.startsWith("!A")) && hasDeviceId()) { - // Location - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, sentence); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); - Integer index = 1; + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); + position.setLatitude(parser.nextDouble()); + position.setLongitude(parser.nextDouble()); + position.setSpeed(parser.nextDouble()); - // Location - position.setLatitude(Double.parseDouble(parser.group(index++))); - position.setLongitude(Double.parseDouble(parser.group(index++))); - position.setSpeed(Double.parseDouble(parser.group(index++))); - position.setCourse(Double.parseDouble(parser.group(index++))); + position.setCourse(parser.nextDouble()); + if (position.getCourse() > 360) { + position.setCourse(0); + } + + if (parser.hasNext(5)) { - // Flags - String flags = parser.group(index++); - position.set(Event.KEY_FLAGS, flags); - position.setValid((Integer.parseInt(flags, 16) & 0x01) != 0); + int flags = parser.nextInt(16); + position.set(Event.KEY_FLAGS, flags); + position.setValid(BitUtil.check(flags, 0)); - // Altitude - position.setAltitude(Double.parseDouble(parser.group(index++))); + position.setAltitude(parser.nextDouble()); - // Battery - position.set(Event.KEY_BATTERY, parser.group(index++)); + position.set(Event.KEY_BATTERY, parser.next()); + position.set(Event.KEY_SATELLITES, parser.next()); - // Satellites - position.set(Event.KEY_SATELLITES, parser.group(index++)); + } return position; + } return null; diff --git a/src/org/traccar/protocol/Mta6ProtocolDecoder.java b/src/org/traccar/protocol/Mta6ProtocolDecoder.java index c23063e4d..f0349a2ef 100644 --- a/src/org/traccar/protocol/Mta6ProtocolDecoder.java +++ b/src/org/traccar/protocol/Mta6ProtocolDecoder.java @@ -17,11 +17,9 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.nio.charset.Charset; -import java.util.Calendar; import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; @@ -33,6 +31,7 @@ import org.jboss.netty.handler.codec.http.HttpVersion; import org.traccar.BaseProtocolDecoder; import org.traccar.Protocol; import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; import org.traccar.helper.Log; import org.traccar.model.Event; import org.traccar.model.Position; @@ -103,14 +102,10 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder { weekNumber = buf.readUnsignedShort(); } - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 1980); - time.set(Calendar.MONTH, 0); - time.set(Calendar.DAY_OF_MONTH, 6); - long offset = time.getTimeInMillis(); + DateBuilder dateBuilder = new DateBuilder().setDate(1980, 1, 6); + dateBuilder.addMillis(weekNumber * 7 * 24 * 60 * 60 * 1000 + weekTime); - return new Date(offset + weekNumber * 7 * 24 * 60 * 60 * 1000 + weekTime); + return dateBuilder.getDate(); } } @@ -130,7 +125,6 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder { short flags = buf.readUnsignedByte(); - // Skip events short event = buf.readUnsignedByte(); if (BitUtil.check(event, 7)) { if (BitUtil.check(event, 6)) { @@ -281,13 +275,11 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { HttpRequest request = (HttpRequest) msg; ChannelBuffer buf = request.getContent(); - // Read identifier buf.skipBytes("id=".length()); int index = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '&'); String uniqueId = buf.toString(buf.readerIndex(), index - buf.readerIndex(), Charset.defaultCharset()); @@ -297,7 +289,6 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder { buf.skipBytes(uniqueId.length()); buf.skipBytes("&bin=".length()); - // Read header short packetId = buf.readUnsignedByte(); short offset = buf.readUnsignedByte(); // dataOffset short packetCount = buf.readUnsignedByte(); @@ -305,13 +296,11 @@ public class Mta6ProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // timezone buf.skipBytes(offset - 5); - // Send response if (channel != null) { sendContinue(channel); sendResponse(channel, packetId, packetCount); } - // Parse data if (packetId == 0x31 || packetId == 0x32 || packetId == 0x36) { if (simple) { return parseFormatA1(buf); diff --git a/src/org/traccar/protocol/MxtProtocolDecoder.java b/src/org/traccar/protocol/MxtProtocolDecoder.java index ba97694d3..3ff127d5d 100644 --- a/src/org/traccar/protocol/MxtProtocolDecoder.java +++ b/src/org/traccar/protocol/MxtProtocolDecoder.java @@ -16,13 +16,11 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.Date; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -39,8 +37,7 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -64,12 +61,7 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder { position.set(Event.KEY_INDEX, buf.readUnsignedShort()); - // Date and time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000); - time.set(Calendar.MONTH, 0); - time.set(Calendar.DAY_OF_MONTH, 1); + DateBuilder dateBuilder = new DateBuilder().setDate(2000, 1, 1); long date = buf.readUnsignedInt(); @@ -78,12 +70,10 @@ public class MxtProtocolDecoder extends BaseProtocolDecoder { long minutes = BitUtil.between(date, 6, 6 + 6); long seconds = BitUtil.to(date, 6); - long millis = time.getTimeInMillis(); - millis += (((days * 24 + hours) * 60 + minutes) * 60 + seconds) * 1000; + dateBuilder.addMillis((((days * 24 + hours) * 60 + minutes) * 60 + seconds) * 1000); - position.setTime(new Date(millis)); + position.setTime(dateBuilder.getDate()); - // Location position.setValid(true); position.setLatitude(buf.readInt() / 1000000.0); position.setLongitude(buf.readInt() / 1000000.0); diff --git a/src/org/traccar/protocol/NavigilProtocolDecoder.java b/src/org/traccar/protocol/NavigilProtocolDecoder.java index 5ce5d08cb..60aaab345 100644 --- a/src/org/traccar/protocol/NavigilProtocolDecoder.java +++ b/src/org/traccar/protocol/NavigilProtocolDecoder.java @@ -261,8 +261,7 @@ public class NavigilProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; diff --git a/src/org/traccar/protocol/NavisProtocolDecoder.java b/src/org/traccar/protocol/NavisProtocolDecoder.java index 0af74d70e..5569b6a6e 100644 --- a/src/org/traccar/protocol/NavisProtocolDecoder.java +++ b/src/org/traccar/protocol/NavisProtocolDecoder.java @@ -18,14 +18,14 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.nio.ByteOrder; import java.nio.charset.Charset; -import java.util.Calendar; import java.util.LinkedList; import java.util.List; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; import org.traccar.helper.Log; import org.traccar.model.Event; import org.traccar.model.Position; @@ -83,7 +83,6 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder { position.setDeviceId(getDeviceId()); - // Format type int format; if (buf.getUnsignedByte(buf.readerIndex()) == 0) { format = buf.readUnsignedShort(); @@ -95,49 +94,29 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder { long index = buf.readUnsignedInt(); position.set(Event.KEY_INDEX, index); - // Event type position.set(Event.KEY_EVENT, buf.readUnsignedShort()); - // Event time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.HOUR_OF_DAY, buf.readUnsignedByte()); - time.set(Calendar.MINUTE, buf.readUnsignedByte()); - time.set(Calendar.SECOND, buf.readUnsignedByte()); - time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); - time.set(Calendar.MONTH, buf.readUnsignedByte()); - time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); - position.set("time", time.getTimeInMillis()); - - // Alarm status - position.set(Event.KEY_ALARM, buf.readUnsignedByte()); + buf.skipBytes(6); // event time - // Modules status + position.set(Event.KEY_ALARM, buf.readUnsignedByte()); position.set(Event.KEY_STATUS, buf.readUnsignedByte()); - - // GSM signal position.set(Event.KEY_GSM, buf.readUnsignedByte()); - // Output if (isFormat(format, F10, F20, F30)) { position.set(Event.KEY_OUTPUT, buf.readUnsignedShort()); } else if (isFormat(format, F40, F50, F51, F52)) { position.set(Event.KEY_OUTPUT, buf.readUnsignedByte()); } - // Input if (isFormat(format, F10, F20, F30, F40)) { position.set(Event.KEY_INPUT, buf.readUnsignedShort()); } else if (isFormat(format, F50, F51, F52)) { position.set(Event.KEY_INPUT, buf.readUnsignedByte()); } - position.set(Event.KEY_POWER, buf.readUnsignedShort() / 1000.0); - - // Battery power + position.set(Event.KEY_POWER, buf.readUnsignedShort() * 0.001); position.set(Event.KEY_BATTERY, buf.readUnsignedShort()); - // Temperature if (isFormat(format, F10, F20, F30)) { position.set(Event.PREFIX_TEMP + 1, buf.readShort()); } @@ -147,46 +126,37 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder { position.set(Event.PREFIX_ADC + 2, buf.readUnsignedShort()); } + // Impulse counters if (isFormat(format, F20, F50, F51, F52)) { - // Impulse counters buf.readUnsignedInt(); buf.readUnsignedInt(); } if (isFormat(format, F20, F50, F51, F52)) { - // Validity int locationStatus = buf.readUnsignedByte(); - position.setValid((locationStatus & 0x02) == 0x02); - - // Location time - time.clear(); - time.set(Calendar.HOUR_OF_DAY, buf.readUnsignedByte()); - time.set(Calendar.MINUTE, buf.readUnsignedByte()); - time.set(Calendar.SECOND, buf.readUnsignedByte()); - time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); - time.set(Calendar.MONTH, buf.readUnsignedByte()); - time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); - position.setTime(time.getTime()); - - // Location data + position.setValid(BitUtil.check(locationStatus, 1)); + + DateBuilder dateBuilder = new DateBuilder() + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + position.setTime(dateBuilder.getDate()); + position.setLatitude(buf.readFloat() / Math.PI * 180); position.setLongitude(buf.readFloat() / Math.PI * 180); position.setSpeed(buf.readFloat()); position.setCourse(buf.readUnsignedShort()); - // Odometer position.set(Event.KEY_ODOMETER, buf.readFloat()); - // Last segment - position.set("segment", buf.readFloat()); + position.set("segment", buf.readFloat()); // last segment // Segment times buf.readUnsignedShort(); buf.readUnsignedShort(); } + // Other if (isFormat(format, F51, F52)) { - // Other stuff buf.readUnsignedShort(); buf.readByte(); buf.readUnsignedShort(); @@ -198,8 +168,8 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedShort(); } + // Four temperature sensors if (isFormat(format, F40, F52)) { - // Four temperature sensors buf.readByte(); buf.readByte(); buf.readByte(); @@ -217,7 +187,6 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder { response.writeInt((int) result.getId()); sendReply(channel, response); - // No location data if (result.getPosition().getFixTime() == null) { return null; } @@ -241,7 +210,6 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder { response.writeByte(count); sendReply(channel, response); - // No location data if (positions.isEmpty()) { return null; } @@ -281,12 +249,10 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; - // Read header prefix = buf.toString(buf.readerIndex(), 4, CHARSET); buf.skipBytes(prefix.length()); // prefix @NTC by default serverId = buf.readUnsignedInt(); @@ -298,7 +264,6 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder { return null; // keep alive message } - // Read message type String type = buf.toString(buf.readerIndex(), 3, CHARSET); buf.skipBytes(type.length()); diff --git a/src/org/traccar/protocol/NoranProtocolDecoder.java b/src/org/traccar/protocol/NoranProtocolDecoder.java index 11408b1ed..35924c5b2 100644 --- a/src/org/traccar/protocol/NoranProtocolDecoder.java +++ b/src/org/traccar/protocol/NoranProtocolDecoder.java @@ -20,12 +20,12 @@ import java.nio.ByteOrder; import java.nio.charset.Charset; import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -87,14 +87,10 @@ public class NoranProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedInt(); // GIS port } - // Flags - int flags = buf.readUnsignedByte(); - position.setValid((flags & 0x01) != 0); + position.setValid(BitUtil.check(buf.readUnsignedByte(), 0)); - // Alarm type position.set(Event.KEY_ALARM, buf.readUnsignedByte()); - // Location if (newFormat) { position.setSpeed(buf.readUnsignedInt()); position.setCourse(buf.readFloat()); @@ -105,21 +101,18 @@ public class NoranProtocolDecoder extends BaseProtocolDecoder { position.setLongitude(buf.readFloat()); position.setLatitude(buf.readFloat()); - // Time if (!newFormat) { long timeValue = buf.readUnsignedInt(); - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + (int) (timeValue >> 26)); - time.set(Calendar.MONTH, (int) (timeValue >> 22 & 0x0f) - 1); - time.set(Calendar.DAY_OF_MONTH, (int) (timeValue >> 17 & 0x1f)); - time.set(Calendar.HOUR_OF_DAY, (int) (timeValue >> 12 & 0x1f)); - time.set(Calendar.MINUTE, (int) (timeValue >> 6 & 0x3f)); - time.set(Calendar.SECOND, (int) (timeValue & 0x3f)); - position.setTime(time.getTime()); + DateBuilder dateBuilder = new DateBuilder() + .setYear((int) BitUtil.from(timeValue, 26)) + .setMonth((int) BitUtil.between(timeValue, 22, 26)) + .setDay((int) BitUtil.between(timeValue, 17, 22)) + .setHour((int) BitUtil.between(timeValue, 12, 17)) + .setMinute((int) BitUtil.between(timeValue, 6, 12)) + .setSecond((int) BitUtil.to(timeValue, 6)); + position.setTime(dateBuilder.getDate()); } - // Identification ChannelBuffer rawId; if (newFormat) { rawId = buf.readBytes(12); @@ -132,14 +125,12 @@ public class NoranProtocolDecoder extends BaseProtocolDecoder { } position.setDeviceId(getDeviceId()); - // Time if (newFormat) { DateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss"); position.setTime(dateFormat.parse(buf.readBytes(17).toString(Charset.defaultCharset()))); buf.readByte(); } - // Other data if (!newFormat) { position.set(Event.PREFIX_IO + 1, buf.readUnsignedByte()); position.set(Event.KEY_FUEL, buf.readUnsignedByte()); diff --git a/src/org/traccar/protocol/OrionProtocolDecoder.java b/src/org/traccar/protocol/OrionProtocolDecoder.java index b2e1699c3..a208d83e4 100644 --- a/src/org/traccar/protocol/OrionProtocolDecoder.java +++ b/src/org/traccar/protocol/OrionProtocolDecoder.java @@ -16,16 +16,13 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; import java.util.LinkedList; import java.util.List; -import java.util.TimeZone; - import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; - import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -56,8 +53,7 @@ public class OrionProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; @@ -80,7 +76,6 @@ public class OrionProtocolDecoder extends BaseProtocolDecoder { for (int i = 0; i < (header & 0x0f); i++) { - // Create new position Position position = new Position(); position.setDeviceId(getDeviceId()); position.setProtocol(getProtocolName()); @@ -89,28 +84,21 @@ public class OrionProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // length position.set(Event.KEY_FLAGS, buf.readUnsignedShort()); - // Location position.setLatitude(convertCoordinate(buf.readInt())); position.setLongitude(convertCoordinate(buf.readInt())); position.setAltitude(buf.readShort() / 10.0); position.setCourse(buf.readUnsignedShort()); position.setSpeed(buf.readUnsignedShort() * 0.0539957); - // Date and time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); - time.set(Calendar.MONTH, buf.readUnsignedByte() - 1); - time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); - time.set(Calendar.HOUR_OF_DAY, buf.readUnsignedByte()); - time.set(Calendar.MINUTE, buf.readUnsignedByte()); - time.set(Calendar.SECOND, buf.readUnsignedByte()); - position.setTime(time.getTime()); - - // Accuracy + DateBuilder dateBuilder = new DateBuilder() + .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + position.setTime(dateBuilder.getDate()); + int satellites = buf.readUnsignedByte(); - position.set(Event.KEY_SATELLITES, satellites); position.setValid(satellites >= 3); + position.set(Event.KEY_SATELLITES, satellites); + positions.add(position); } diff --git a/src/org/traccar/protocol/OsmAndProtocolDecoder.java b/src/org/traccar/protocol/OsmAndProtocolDecoder.java index f128c6414..529a2940e 100644 --- a/src/org/traccar/protocol/OsmAndProtocolDecoder.java +++ b/src/org/traccar/protocol/OsmAndProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,8 +42,7 @@ public class OsmAndProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { HttpRequest request = (HttpRequest) msg; QueryStringDecoder decoder = new QueryStringDecoder(request.getUri()); @@ -54,11 +53,9 @@ public class OsmAndProtocolDecoder extends BaseProtocolDecoder { params = decoder.getParameters(); } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - // Identification String id; if (params.containsKey("id")) { id = params.get("id").get(0); @@ -70,7 +67,6 @@ public class OsmAndProtocolDecoder extends BaseProtocolDecoder { } position.setDeviceId(getDeviceId()); - // Decode position position.setValid(true); if (params.containsKey("timestamp")) { try { @@ -89,7 +85,6 @@ public class OsmAndProtocolDecoder extends BaseProtocolDecoder { position.setLatitude(Double.parseDouble(params.get("lat").get(0))); position.setLongitude(Double.parseDouble(params.get("lon").get(0))); - // Optional parameters if (params.containsKey("speed")) { position.setSpeed(Double.parseDouble(params.get("speed").get(0))); } @@ -124,7 +119,6 @@ public class OsmAndProtocolDecoder extends BaseProtocolDecoder { position.set("description", params.get("desc").get(0)); } - // Send response if (channel != null) { HttpResponse response = new DefaultHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK); diff --git a/src/org/traccar/protocol/PiligrimProtocolDecoder.java b/src/org/traccar/protocol/PiligrimProtocolDecoder.java index a1808a3d8..4f194605d 100644 --- a/src/org/traccar/protocol/PiligrimProtocolDecoder.java +++ b/src/org/traccar/protocol/PiligrimProtocolDecoder.java @@ -32,6 +32,7 @@ import org.jboss.netty.handler.codec.http.HttpVersion; import org.jboss.netty.handler.codec.http.QueryStringDecoder; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -58,8 +59,7 @@ public class PiligrimProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { HttpRequest request = (HttpRequest) msg; String uri = request.getUri(); @@ -80,7 +80,6 @@ public class PiligrimProtocolDecoder extends BaseProtocolDecoder { sendResponse(channel, "BINGPS: OK"); - // Identification QueryStringDecoder decoder = new QueryStringDecoder(request.getUri()); if (!identify(decoder.getParameters().get("imei").get(0), channel)) { return null; @@ -118,47 +117,39 @@ public class PiligrimProtocolDecoder extends BaseProtocolDecoder { longitude += buf.readUnsignedByte() / 6000.0; longitude += buf.readUnsignedByte() / 600000.0; - // Hemisphere int flags = buf.readUnsignedByte(); - if ((flags & 0x01) != 0) { + if (BitUtil.check(flags, 0)) { latitude = -latitude; } - if ((flags & 0x02) != 0) { + if (BitUtil.check(flags, 1)) { longitude = -longitude; } position.setLatitude(latitude); position.setLongitude(longitude); - // Satellites int satellites = buf.readUnsignedByte(); position.set(Event.KEY_SATELLITES, satellites); position.setValid(satellites >= 3); - // Speed position.setSpeed(buf.readUnsignedByte()); - // Course double course = buf.readUnsignedByte() << 1; course += (flags >> 2) & 1; course += buf.readUnsignedByte() / 100.0; position.setCourse(course); - // Sensors if (type == MSG_GPS_SENSORS) { - - // External power double power = buf.readUnsignedByte(); power += buf.readUnsignedByte() << 8; - position.set(Event.KEY_POWER, power / 100); + position.set(Event.KEY_POWER, power * 0.01); - // Battery double battery = buf.readUnsignedByte(); battery += buf.readUnsignedByte() << 8; - position.set(Event.KEY_BATTERY, battery / 100); + position.set(Event.KEY_BATTERY, battery * 0.01); buf.skipBytes(6); - } + positions.add(position); } else if (type == MSG_EVENTS) { diff --git a/src/org/traccar/protocol/ProgressProtocolDecoder.java b/src/org/traccar/protocol/ProgressProtocolDecoder.java index 9fbd601d5..93091e0a1 100644 --- a/src/org/traccar/protocol/ProgressProtocolDecoder.java +++ b/src/org/traccar/protocol/ProgressProtocolDecoder.java @@ -18,14 +18,14 @@ package org.traccar.protocol; import java.net.SocketAddress; import java.nio.ByteOrder; import java.nio.charset.Charset; -import java.util.Calendar; +import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.TimeZone; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; import org.traccar.model.Event; import org.traccar.model.Position; @@ -48,8 +48,6 @@ public class ProgressProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_ALARM = 200; public static final int MSG_ALARM_RECIEVED = 201; - private static final String HEX_CHARS = "0123456789ABCDEF"; - private void requestArchive(Channel channel) { if (lastIndex == 0) { lastIndex = newIndex; @@ -64,7 +62,8 @@ public class ProgressProtocolDecoder extends BaseProtocolDecoder { } @Override - protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; int type = buf.readUnsignedShort(); @@ -95,7 +94,6 @@ public class ProgressProtocolDecoder extends BaseProtocolDecoder { position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); - // Message index if (type == MSG_LOGMSG) { position.set(Event.KEY_ARCHIVE, true); int subtype = buf.readUnsignedShort(); @@ -112,84 +110,47 @@ public class ProgressProtocolDecoder extends BaseProtocolDecoder { newIndex = buf.readUnsignedInt(); } - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.setTimeInMillis(buf.readUnsignedInt() * 1000); - position.setTime(time.getTime()); - - // Latitude + position.setTime(new Date(buf.readUnsignedInt() * 1000)); position.setLatitude(buf.readInt() * 180.0 / 0x7FFFFFFF); - - // Longitude position.setLongitude(buf.readInt() * 180.0 / 0x7FFFFFFF); + position.setSpeed(buf.readUnsignedInt() * 0.01); + position.setCourse(buf.readUnsignedShort() * 0.01); + position.setAltitude(buf.readUnsignedShort() * 0.01); - // Speed - position.setSpeed(buf.readUnsignedInt() / 100.0); - - // Course - position.setCourse(buf.readUnsignedShort() / 100.0); - - // Altitude - position.setAltitude(buf.readUnsignedShort() / 100.0); + int satellites = buf.readUnsignedByte(); + position.setValid(satellites >= 3); + position.set(Event.KEY_SATELLITES, satellites); - // Satellites - int satellitesNumber = buf.readUnsignedByte(); - position.set(Event.KEY_SATELLITES, satellitesNumber); - - // Validity - position.setValid(satellitesNumber >= 3); - - // Cell signal position.set(Event.KEY_GSM, buf.readUnsignedByte()); - - // Odometer position.set(Event.KEY_ODOMETER, buf.readUnsignedInt()); long extraFlags = buf.readLong(); - // Analog inputs - if ((extraFlags & 0x1) == 0x1) { + if (BitUtil.check(extraFlags, 0)) { int count = buf.readUnsignedShort(); for (int i = 1; i <= count; i++) { position.set(Event.PREFIX_ADC + i, buf.readUnsignedShort()); } } - // CAN adapter - if ((extraFlags & 0x2) == 0x2) { + if (BitUtil.check(extraFlags, 1)) { int size = buf.readUnsignedShort(); position.set("can", buf.toString(buf.readerIndex(), size, Charset.defaultCharset())); buf.skipBytes(size); } - // Passenger sensor - if ((extraFlags & 0x4) == 0x4) { - int size = buf.readUnsignedShort(); - - // Convert binary data to hex - StringBuilder hex = new StringBuilder(); - for (int i = buf.readerIndex(); i < buf.readerIndex() + size; i++) { - byte b = buf.getByte(i); - hex.append(HEX_CHARS.charAt((b & 0xf0) >> 4)); - hex.append(HEX_CHARS.charAt(b & 0x0F)); - } - - position.set("passenger", hex.toString()); - - buf.skipBytes(size); + if (BitUtil.check(extraFlags, 2)) { + position.set("passenger", + ChannelBuffers.hexDump(buf.readBytes(buf.readUnsignedShort()))); } - // Send response for alarm message if (type == MSG_ALARM) { + position.set(Event.KEY_ALARM, true); byte[] response = {(byte) 0xC9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; channel.write(ChannelBuffers.wrappedBuffer(response)); - - position.set(Event.KEY_ALARM, true); } - // Skip CRC - buf.readUnsignedInt(); + buf.readUnsignedInt(); // crc positions.add(position); } diff --git a/src/org/traccar/protocol/Pt3000ProtocolDecoder.java b/src/org/traccar/protocol/Pt3000ProtocolDecoder.java index f2e0a14bc..1eaba20e4 100644 --- a/src/org/traccar/protocol/Pt3000ProtocolDecoder.java +++ b/src/org/traccar/protocol/Pt3000ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Position; public class Pt3000ProtocolDecoder extends BaseProtocolDecoder { @@ -30,84 +30,50 @@ public class Pt3000ProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "%(\\d+)," + // IMEI - "\\$GPRMC," + - "(\\d{2})(\\d{2})(\\d{2})\\.?\\d*," + // Time (HHMMSS.SSS) - "([AV])," + // Validity - "(\\d{2})(\\d{2}\\.\\d+)," + // Latitude (DDMM.MMMM) - "([NS])," + - "(\\d{3})(\\d{2}\\.\\d+)," + // Longitude (DDDMM.MMMM) - "([EW])," + - "(\\d+\\.?\\d*)?," + // Speed - "(\\d+\\.?\\d*)?," + // Course - "(\\d{2})(\\d{2})(\\d{2})" + // Date (DDMMYY) - ".+"); + private static final Pattern PATTERN = new PatternBuilder() + .number("%(d+),") // imei + .text("$GPRMC,") + .number("(dd)(dd)(dd).?d*,") // time + .expression("([AV]),") // validity + .number("(dd)(dd.d+),") // latitude + .expression("([NS]),") + .number("(ddd)(dd.d+),") // longitude + .expression("([EW]),") + .number("(d+.?d*)?,") // speed + .number("(d+.?d*)?,") // course + .number("(dd)(dd)(dd)") // date (ddmmyy) + .any() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; - - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - - // Identifier - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); - - // Latitude - Double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); + DateBuilder dateBuilder = new DateBuilder() + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); - // Longitude - Double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; - position.setLongitude(longitude); + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); - // Speed - String speed = parser.group(index++); - if (speed != null) { - position.setSpeed(Double.parseDouble(speed)); - } - - // Course - String course = parser.group(index++); - if (course != null) { - position.setCourse(Double.parseDouble(course)); - } + dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Date - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); return position; } diff --git a/src/org/traccar/protocol/Pt502ProtocolDecoder.java b/src/org/traccar/protocol/Pt502ProtocolDecoder.java index 41336dc05..4eec65758 100644 --- a/src/org/traccar/protocol/Pt502ProtocolDecoder.java +++ b/src/org/traccar/protocol/Pt502ProtocolDecoder.java @@ -1,6 +1,6 @@ /*
- * Copyright 2012 - 2014 Anton Tananaev (anton.tananaev@gmail.com)
- * Luis Parada (luis.parada@gmail.com)
+ * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com)
+ * Copyright 2012 Luis Parada (luis.parada@gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,12 +17,12 @@ package org.traccar.protocol;
import java.net.SocketAddress;
-import java.util.Calendar;
-import java.util.TimeZone;
-import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jboss.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.helper.DateBuilder;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
import org.traccar.model.Event;
import org.traccar.model.Position;
@@ -32,120 +32,74 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { super(protocol);
}
- private static final Pattern PATTERN = Pattern.compile(
- ".*" +
- "\\$[A-Z]{3}\\d?," + // Type
- "(\\d+)," + // Id
- "(\\d{2})(\\d{2})(\\d{2})\\.(\\d{3})," + // Time (HHMMSS.SSS)
- "([AV])," + // Validity
- "(\\d{2})(\\d{2}\\.\\d{4})," + // Latitude (DDMM.MMMM)
- "([NS])," +
- "(\\d{3})(\\d{2}\\.\\d{4})," + // Longitude (DDDMM.MMMM)
- "([EW])," +
- "(\\d+\\.\\d+)?," + // Speed
- "(\\d+\\.\\d+)?," + // Course
- "(\\d{2})(\\d{2})(\\d{2}),,," + // Date
- "./" +
- "([01])+," + // Input
- "([01])+/" + // Output
- "([^/]+)?/" + // ADC
- "(\\d+)" + // Odometer
- "(?:/([^/]+)?/" + // RFID
- "(\\p{XDigit}{3}))?" + // State
- ".*");
+ private static final Pattern PATTERN = new PatternBuilder()
+ .any().text("$")
+ .expression("[A-Z]{3}")
+ .number("d?,") // type
+ .number("(d+),") // id
+ .number("(dd)(dd)(dd).(ddd),") // time
+ .expression("([AV]),") // validity
+ .number("(dd)(dd.dddd),") // latitude
+ .expression("([NS]),")
+ .number("(ddd)(dd.dddd),") // longitude
+ .expression("([EW]),")
+ .number("(d+.d+)?,") // speed
+ .number("(d+.d+)?,") // course
+ .number("(dd)(dd)(dd),,,") // date
+ .expression("./")
+ .expression("([01])+,") // input
+ .expression("([01])+/") // output
+ .expression("([^/]+)?/") // adc
+ .number("(d+)") // odometer
+ .expression("/([^/]+)?/") // rfid
+ .number("(xxx)").optional(2) // state
+ .any()
+ .compile();
@Override
protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg)
- throws Exception {
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
- String sentence = (String) msg;
-
- // Parse message
- Matcher parser = PATTERN.matcher(sentence);
+ Parser parser = new Parser(PATTERN, (String) msg);
if (!parser.matches()) {
return null;
}
- // Create new position
Position position = new Position();
position.setProtocol(getProtocolName());
- Integer index = 1;
-
- // Get device by IMEI
- if (!identify(parser.group(index++), channel)) {
+ if (!identify(parser.next(), channel)) {
return null;
}
position.setDeviceId(getDeviceId());
- // Time
- Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- time.clear();
- time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++)));
- time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++)));
- time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++)));
- time.set(Calendar.MILLISECOND, Integer.parseInt(parser.group(index++)));
-
- // Validity
- position.setValid(parser.group(index++).compareTo("A") == 0);
-
- // Latitude
- Double latitude = Double.parseDouble(parser.group(index++));
- latitude += Double.parseDouble(parser.group(index++)) / 60;
- if (parser.group(index++).compareTo("S") == 0) {
- latitude = -latitude;
- }
- position.setLatitude(latitude);
-
- // Longitude
- Double longitude = Double.parseDouble(parser.group(index++));
- longitude += Double.parseDouble(parser.group(index++)) / 60;
- if (parser.group(index++).compareTo("W") == 0) {
- longitude = -longitude;
- }
- position.setLongitude(longitude);
-
- // Speed
- String speed = parser.group(index++);
- if (speed != null) {
- position.setSpeed(Double.parseDouble(speed));
- }
+ DateBuilder dateBuilder = new DateBuilder()
+ .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt());
- // Course
- String course = parser.group(index++);
- if (course != null) {
- position.setCourse(Double.parseDouble(course));
- }
+ position.setValid(parser.next().equals("A"));
+ position.setLatitude(parser.nextCoordinate());
+ position.setLongitude(parser.nextCoordinate());
+ position.setSpeed(parser.nextDouble());
+ position.setCourse(parser.nextDouble());
- // Date
- time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++)));
- time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1);
- time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++)));
- position.setTime(time.getTime());
+ dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt());
+ position.setTime(dateBuilder.getDate());
- // IO
- position.set(Event.KEY_INPUT, parser.group(index++));
- position.set(Event.KEY_OUTPUT, parser.group(index++));
+ position.set(Event.KEY_INPUT, parser.next());
+ position.set(Event.KEY_OUTPUT, parser.next());
- // ADC
- String adc = parser.group(index++);
- if (adc != null) {
- String[] values = adc.split(",");
+ if (parser.hasNext()) {
+ String[] values = parser.next().split(",");
for (int i = 0; i < values.length; i++) {
position.set(Event.PREFIX_ADC + (i + 1), Integer.parseInt(values[i], 16));
}
}
- position.set(Event.KEY_ODOMETER, parser.group(index++));
+ position.set(Event.KEY_ODOMETER, parser.next());
+ position.set(Event.KEY_RFID, parser.next());
- // Driver
- position.set(Event.KEY_RFID, parser.group(index++));
-
- // Other
- String status = parser.group(index++);
- if (status != null) {
- int value = Integer.parseInt(status, 16);
+ if (parser.hasNext()) {
+ int value = parser.nextInt(16);
position.set(Event.KEY_BATTERY, value >> 8);
position.set(Event.KEY_GSM, (value >> 4) & 0xf);
position.set(Event.KEY_SATELLITES, value & 0xf);
@@ -153,4 +107,5 @@ public class Pt502ProtocolDecoder extends BaseProtocolDecoder { return position;
}
+
}
diff --git a/src/org/traccar/protocol/RuptelaProtocolDecoder.java b/src/org/traccar/protocol/RuptelaProtocolDecoder.java index 45925d5d1..ede92d837 100644 --- a/src/org/traccar/protocol/RuptelaProtocolDecoder.java +++ b/src/org/traccar/protocol/RuptelaProtocolDecoder.java @@ -37,14 +37,12 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; buf.readUnsignedShort(); // data length - // Identify device String imei = String.format("%015d", buf.readLong()); if (!identify(imei, channel)) { return null; @@ -63,19 +61,16 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); - // Time position.setTime(new Date(buf.readUnsignedInt() * 1000)); buf.readUnsignedByte(); // timestamp extension buf.readUnsignedByte(); // priority (reserved) - // Location position.setLongitude(buf.readInt() / 10000000.0); position.setLatitude(buf.readInt() / 10000000.0); position.setAltitude(buf.readUnsignedShort() / 10.0); position.setCourse(buf.readUnsignedShort() / 100.0); - // Validity int satellites = buf.readUnsignedByte(); position.set(Event.KEY_SATELLITES, satellites); position.setValid(satellites >= 3); @@ -109,13 +104,13 @@ public class RuptelaProtocolDecoder extends BaseProtocolDecoder { for (int j = 0; j < cnt; j++) { position.set(Event.PREFIX_IO + buf.readUnsignedByte(), buf.readLong()); } + positions.add(position); } - // Acknowledgement if (channel != null) { byte[] response = {0x00, 0x02, 0x64, 0x01, 0x13, (byte) 0xbc}; - channel.write(ChannelBuffers.wrappedBuffer(response)); + channel.write(ChannelBuffers.wrappedBuffer(response)); // acknowledgement } return positions; diff --git a/src/org/traccar/protocol/Stl060ProtocolDecoder.java b/src/org/traccar/protocol/Stl060ProtocolDecoder.java index b5b972982..b583aee66 100644 --- a/src/org/traccar/protocol/Stl060ProtocolDecoder.java +++ b/src/org/traccar/protocol/Stl060ProtocolDecoder.java @@ -21,6 +21,7 @@ import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.helper.DateBuilder; import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -30,38 +31,40 @@ public class Stl060ProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - ".*\\$1," + - "(\\d+)," + // IMEI - "D001," + // Type - "[^,]*," + // Vehicle - "(\\d{2})/(\\d{2})/(\\d{2})," + // Date - "(\\d{2}):(\\d{2}):(\\d{2})," + // Time - "(\\d{2})(\\d{2})\\.?(\\d+)([NS])," + // Latitude - "(\\d{3})(\\d{2})\\.?(\\d+)([EW])," + // Longitude - "(\\d+\\.?\\d*)," + // Speed - "(\\d+\\.?\\d*)," + // Course - - "(?:(\\d+)," + // Odometer - "(\\d+)," + // Ignition - "(\\d+)," + // DI1 - "(\\d+)," + // DI2 - "(\\d+),|" + // Fuel - - "([01])," + // Charging - "([01])," + // Ignition - "0,0," + // Reserved - "(\\d+)," + // DI - "([^,]+)," + // RFID - "(\\d+)," + // Odometer - "(\\d+)," + // Temperature - "(\\d+)," + // Fuel - "([01])," + // Accelerometer - "([01])," + // DO1 - "([01]),)" + // DO2 - - "([AV])" + // Validity - ".*"); + private static final Pattern PATTERN = new PatternBuilder() + .any() + .text("$1,") + .number("(d+),") // imei + .text("D001,") // type + .expression("[^,]*,") // vehicle + .number("(dd)/(dd)/(dd),") // date + .number("(dd):(dd):(dd),") // time + .number("(dd)(dd).?(d+)([NS]),") // latitude + .number("(ddd)(dd).?(d+)([EW]),") // longitude + .number("(d+.?d*),") // speed + .number("(d+.?d*),") // course + .groupBegin() + .number("(d+),") // odometer + .number("(d+),") // Ignition + .number("(d+),") // di1 + .number("(d+),") // di2 + .number("(d+),") // fuel + .or() + .expression("([01]),") // charging + .expression("([01]),") // ignition + .expression("0,0,") // reserved + .number("(d+),") // di + .expression("([^,]+),") // rfid + .number("(d+),") // odometer + .number("(d+),") // temperature + .number("(d+),") // fuel + .expression("([01]),") // accelerometer + .expression("([01]),") // do1 + .expression("([01]),") // do2 + .groupEnd() + .expression("([AV])") // validity + .any() + .compile(); @Override protected Object decode( diff --git a/src/org/traccar/protocol/SuntechProtocolDecoder.java b/src/org/traccar/protocol/SuntechProtocolDecoder.java index 19eed1197..81a7c3041 100644 --- a/src/org/traccar/protocol/SuntechProtocolDecoder.java +++ b/src/org/traccar/protocol/SuntechProtocolDecoder.java @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -32,82 +32,66 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "S.\\d{3}(?:\\w{3})?;" + // Header - "(?:([^;]+);)?" + // Type - "(\\d{6,});" + // Device ID - "(?:\\d+;)?" + - "(\\d+);" + // Version - "(\\d{4})(\\d{2})(\\d{2});" + // Date (YYYYMMDD) - "(\\d{2}):(\\d{2}):(\\d{2});" + // Time (HH:MM:SS) - "(?:(\\p{XDigit}+);)?" + // Cell - "([-\\+]\\d{2}\\.\\d+);" + // Latitude - "([-\\+]\\d{3}\\.\\d+);" + // Longitude - "(\\d{3}\\.\\d{3});" + // Speed - "(\\d{3}\\.\\d{2});" + // Course - "(?:\\d+;)?" + - "(\\d+\\.\\d+)?" + // Battery - ".*"); // Full format + private static final Pattern PATTERN = new PatternBuilder() + .expression("S.") + .number("ddd") + .expression("(?:[A-Z]{3})?;") // header + .expression("([^;]+);").optional() // type + .number("(d{6,});") // device id + .number("d+;").optional() + .number("(d+);") // version + .number("(dddd)(dd)(dd);") // date + .number("(dd):(dd):(dd);") // time + .number("(x+);").optional() // cell + .number("([-+]dd.d+);") // latitude + .number("([-+]ddd.d+);") // longitude + .number("(ddd.ddd);") // speed + .number("(ddd.dd);") // course + .number("d+;").optional() + .number("(d+.d+)?") // battery + .any() // full format + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; - - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - int index = 1; - String type = parser.group(index++); - if (type != null && (type.equals("Alert") || type.equals("Emergency"))) { - position.set(Event.KEY_ALARM, true); + if (parser.hasNext()) { + String type = parser.next(); + if (type.equals("Alert") || type.equals("Emergency")) { + position.set(Event.KEY_ALARM, true); + } } - // Identifier - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Version - position.set(Event.KEY_VERSION, parser.group(index++)); - - // Date and Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); - - // Cell - position.set(Event.KEY_CELL, parser.group(index++)); + position.set(Event.KEY_VERSION, parser.next()); - // Coordinates - position.setLatitude(Double.parseDouble(parser.group(index++))); - position.setLongitude(Double.parseDouble(parser.group(index++))); - position.setValid(true); // wrong? + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Speed - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(parser.group(index++)))); + position.set(Event.KEY_CELL, parser.next()); - // Course - position.setCourse(Double.parseDouble(parser.group(index++))); + position.setValid(true); + position.setLatitude(parser.nextDouble()); + position.setLongitude(parser.nextDouble()); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); - // Battery - position.set(Event.KEY_BATTERY, parser.group(index++)); + position.set(Event.KEY_BATTERY, parser.next()); return position; } diff --git a/src/org/traccar/protocol/T55ProtocolDecoder.java b/src/org/traccar/protocol/T55ProtocolDecoder.java index 42db4c753..d8b3eefe8 100644 --- a/src/org/traccar/protocol/T55ProtocolDecoder.java +++ b/src/org/traccar/protocol/T55ProtocolDecoder.java @@ -82,6 +82,8 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { .any() .compile(); + private Position position = null; + private Position decodeGprmc(String sentence, Channel channel) { if (channel != null) { @@ -95,7 +97,10 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { Position position = new Position(); position.setProtocol(getProtocolName()); - position.setDeviceId(getDeviceId()); + + if (hasDeviceId()) { + position.setDeviceId(getDeviceId()); + } DateBuilder dateBuilder = new DateBuilder() .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); @@ -109,7 +114,12 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); position.setTime(dateBuilder.getDate()); - return position; + if (hasDeviceId()) { + return position; + } else { + this.position = position; // save position + return null; + } } private Position decodeGpgga(String sentence) { @@ -207,10 +217,15 @@ public class T55ProtocolDecoder extends BaseProtocolDecoder { } else if (sentence.startsWith("IMEI")) { identify(sentence.substring(5, sentence.length()), channel); } else if (sentence.startsWith("$GPFID")) { - identify(sentence.substring(6, sentence.length()), channel); + if (identify(sentence.substring(6, sentence.length()), channel) && position != null) { + Position position = this.position; + position.setDeviceId(getDeviceId()); + this.position = null; + return position; + } } else if (Character.isDigit(sentence.charAt(0)) && sentence.length() == 15) { identify(sentence, channel); - } else if (sentence.startsWith("$GPRMC") && hasDeviceId()) { + } else if (sentence.startsWith("$GPRMC")) { return decodeGprmc(sentence, channel); } else if (sentence.startsWith("$GPGGA") && hasDeviceId()) { return decodeGpgga(sentence); diff --git a/src/org/traccar/protocol/T800xProtocol.java b/src/org/traccar/protocol/T800xProtocol.java new file mode 100644 index 000000000..f98dfc943 --- /dev/null +++ b/src/org/traccar/protocol/T800xProtocol.java @@ -0,0 +1,43 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.frame.LengthFieldBasedFrameDecoder; +import org.traccar.BaseProtocol; +import org.traccar.TrackerServer; + +import java.util.List; + +public class T800xProtocol extends BaseProtocol { + + public T800xProtocol() { + super("t800x"); + } + + @Override + public void initTrackerServers(List<TrackerServer> serverList) { + serverList.add(new TrackerServer(new ServerBootstrap(), getName()) { + @Override + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 3, 2, -5, 0)); + pipeline.addLast("objectDecoder", new T800xProtocolDecoder(T800xProtocol.this)); + } + }); + } + +} diff --git a/src/org/traccar/protocol/T800xProtocolDecoder.java b/src/org/traccar/protocol/T800xProtocolDecoder.java new file mode 100644 index 000000000..ab62110ba --- /dev/null +++ b/src/org/traccar/protocol/T800xProtocolDecoder.java @@ -0,0 +1,162 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.traccar.BaseProtocolDecoder; +import org.traccar.helper.BitUtil; +import org.traccar.helper.ChannelBufferTools; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Event; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.nio.ByteOrder; + +public class T800xProtocolDecoder extends BaseProtocolDecoder { + + public T800xProtocolDecoder(T800xProtocol protocol) { + super(protocol); + } + + private static final int MSG_LOGIN = 0x01; + private static final int MSG_GPS = 0x02; + private static final int MSG_HEARTBEAT = 0x03; + private static final int MSG_ALARM = 0x04; + + private static float readSwappedFloat(ChannelBuffer buf) { + byte[] bytes = new byte[4]; + buf.readBytes(bytes); + return ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, bytes).readFloat(); + } + + private void sendResponse(Channel channel, int type, ChannelBuffer imei) { + if (channel != null) { + ChannelBuffer response = ChannelBuffers.directBuffer(15); + response.writeByte(0x23); + response.writeByte(0x23); // header + response.writeByte(type); + response.writeShort(response.capacity()); // length + response.writeShort(0x0001); // index + response.writeBytes(imei); + channel.write(response); + } + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ChannelBuffer buf = (ChannelBuffer) msg; + + buf.skipBytes(2); + int type = buf.readUnsignedByte(); + buf.readUnsignedShort(); // length + int index = buf.readUnsignedShort(); + ChannelBuffer imei = buf.readBytes(8); + + if (!identify(ChannelBuffers.hexDump(imei).substring(1), channel, remoteAddress)) { + return null; + } + + if (type == MSG_LOGIN || type == MSG_ALARM || type == MSG_HEARTBEAT) { + sendResponse(channel, type, imei); + } + + if (type == MSG_GPS || type == MSG_ALARM) { + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(getDeviceId()); + + position.set(Event.KEY_INDEX, index); + + buf.readUnsignedShort(); // acc on interval + buf.readUnsignedShort(); // acc off interval + buf.readUnsignedByte(); // angle compensation + buf.readUnsignedShort(); // distance compensation + buf.readUnsignedShort(); // speed alarm + + int locationStatus = buf.readUnsignedByte(); + + buf.readUnsignedByte(); // gsensor manager status + buf.readUnsignedByte(); // other flags + buf.readUnsignedByte(); // heartbeat + buf.readUnsignedByte(); // relay status + buf.readUnsignedShort(); // drag alarm setting + + int io = buf.readUnsignedShort(); + position.set(Event.KEY_IGNITION, BitUtil.check(io, 14)); + position.set("ac", BitUtil.check(io, 13)); + + position.set(Event.PREFIX_ADC + 1, buf.readUnsignedShort()); + position.set(Event.PREFIX_ADC + 2, buf.readUnsignedShort()); + + position.set(Event.KEY_ALARM, buf.readUnsignedByte()); + + buf.readUnsignedByte(); // reserved + + position.set(Event.KEY_ODOMETER, buf.readUnsignedInt()); + + int battery = ChannelBufferTools.readHexInteger(buf, 2); + if (battery == 0) { + battery = 100; + } + position.set(Event.KEY_BATTERY, battery); + + DateBuilder dateBuilder = new DateBuilder() + .setYear(ChannelBufferTools.readHexInteger(buf, 2)) + .setMonth(ChannelBufferTools.readHexInteger(buf, 2)) + .setDay(ChannelBufferTools.readHexInteger(buf, 2)) + .setHour(ChannelBufferTools.readHexInteger(buf, 2)) + .setMinute(ChannelBufferTools.readHexInteger(buf, 2)) + .setSecond(ChannelBufferTools.readHexInteger(buf, 2)); + + if (BitUtil.check(locationStatus, 6)) { + + position.setValid(!BitUtil.check(locationStatus, 7)); + position.setTime(dateBuilder.getDate()); + position.setAltitude(readSwappedFloat(buf)); + position.setLongitude(readSwappedFloat(buf)); + position.setLatitude(readSwappedFloat(buf)); + position.setSpeed(UnitsConverter.knotsFromKph( + ChannelBufferTools.readHexInteger(buf, 4) * 0.1)); + position.setCourse(buf.readUnsignedShort()); + + } else { + + getLastLocation(position, dateBuilder.getDate()); + + position.set(Event.KEY_MCC, buf.readUnsignedShort()); + position.set(Event.KEY_MNC, buf.readUnsignedShort()); + position.set(Event.KEY_LAC, buf.readUnsignedShort()); + position.set(Event.KEY_CELL, buf.readUnsignedShort()); + + // two more cell towers + + } + + return position; + + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/TaipProtocolDecoder.java b/src/org/traccar/protocol/TaipProtocolDecoder.java index 860dd4602..7f8c1a3c6 100644 --- a/src/org/traccar/protocol/TaipProtocolDecoder.java +++ b/src/org/traccar/protocol/TaipProtocolDecoder.java @@ -16,13 +16,13 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; import java.util.Date; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; @@ -35,44 +35,41 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { this.sendResponse = sendResponse; } - private static final Pattern PATTERN = Pattern.compile( - "(?:R[EP]V" + // Type - "(?:\\d{2}" + // Event index - "(\\d{4})" + // Week - "(\\d))?" + // Day - "(\\d{5})|" + // Seconds - "RGP" + // Type - "(\\d{2})(\\d{2})(\\d{2})" + // Date - "(\\d{2})(\\d{2})(\\d{2}))" + // Time - "([\\+\\-]\\d{2})(\\d{5})" + // Latitude - "([\\+\\-]\\d{3})(\\d{5})" + // Longitude - "(\\d{3})" + // Speed - "(\\d{3})" + // Course - "(\\d)" + // Fix mode - ".*\r?\n?"); + private static final Pattern PATTERN = new PatternBuilder() + .groupBegin() + .expression("R[EP]V") // type + .groupBegin() + .number("dd") // event index + .number("(dddd)") // week + .number("(d)") // day + .groupEnd("?") + .number("(d{5})") // seconds + .or() + .text("RGP") // type + .number("(dd)(dd)(dd)") // date + .number("(dd)(dd)(dd)") // time + .groupEnd() + .number("([-+]dd)(d{5})") // latitude + .number("([-+]ddd)(d{5})") // longitude + .number("(ddd)") // speed + .number("(ddd)") // course + .number("(d)") // fix mode + .any() + .compile(); private Date getTime(long week, long day, long seconds) { - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 1980); - time.set(Calendar.MONTH, 0); - time.set(Calendar.DAY_OF_MONTH, 6); - - long millis = time.getTimeInMillis(); - millis += ((week * 7 + day) * 24 * 60 * 60 + seconds) * 1000; - - return new Date(millis); + DateBuilder dateBuilder = new DateBuilder() + .setDate(1980, 1, 6) + .addMillis(((week * 7 + day) * 24 * 60 * 60 + seconds) * 1000); + return dateBuilder.getDate(); } private Date getTime(long seconds) { - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.set(Calendar.HOUR_OF_DAY, 0); - time.set(Calendar.MINUTE, 0); - time.set(Calendar.SECOND, 0); - time.set(Calendar.MILLISECOND, 0); - - long millis = time.getTimeInMillis() + seconds * 1000; + DateBuilder dateBuilder = new DateBuilder(new Date()) + .setTime(0, 0, 0, 0) + .addMillis(seconds * 1000); + long millis = dateBuilder.getDate().getTime(); long diff = System.currentTimeMillis() - millis; if (diff > 12 * 60 * 60 * 1000) { @@ -86,8 +83,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; @@ -97,7 +93,7 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { sentence = sentence.substring(beginIndex + 1); } - // Find device ID + // Find device identifier beginIndex = sentence.indexOf(";ID="); if (beginIndex != -1) { beginIndex += 4; @@ -106,13 +102,11 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { endIndex = sentence.length(); } - // Find device in database String id = sentence.substring(beginIndex, endIndex); if (!identify(id, channel)) { return null; } - // Send response if (sendResponse && channel != null) { channel.write(id); } @@ -120,58 +114,39 @@ public class TaipProtocolDecoder extends BaseProtocolDecoder { return null; } - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, sentence); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); position.setDeviceId(getDeviceId()); - Integer index = 1; - - // Time - String week = parser.group(index++); - String day = parser.group(index++); - String seconds = parser.group(index++); + String week = parser.next(); + String day = parser.next(); + String seconds = parser.next(); if (seconds != null) { if (week != null && day != null) { position.setTime(getTime(Integer.parseInt(week), Integer.parseInt(day), Integer.parseInt(seconds))); } else { position.setTime(getTime(Integer.parseInt(seconds))); } - index += 6; - } else { - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); } - // Latitude - String latitude = parser.group(index) + '.' + parser.group(index + 1); - index += 2; - position.setLatitude(Double.parseDouble(latitude)); - - // Latitude - String longitude = parser.group(index) + '.' + parser.group(index + 1); - index += 2; - position.setLongitude(Double.parseDouble(longitude)); + if (parser.hasNext(6)) { + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + } - // Speed and Course - position.setSpeed(UnitsConverter.knotsFromMph(Double.parseDouble(parser.group(index++)))); - position.setCourse(Double.parseDouble(parser.group(index++))); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_DEG)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_DEG)); + position.setSpeed(UnitsConverter.knotsFromMph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + position.setValid(parser.nextInt() != 0); - // Validity - position.setValid(Integer.parseInt(parser.group(index++)) != 0); return position; } diff --git a/src/org/traccar/protocol/TelikProtocolDecoder.java b/src/org/traccar/protocol/TelikProtocolDecoder.java index 255a3032b..8ad88c868 100644 --- a/src/org/traccar/protocol/TelikProtocolDecoder.java +++ b/src/org/traccar/protocol/TelikProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2014 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -31,74 +31,54 @@ public class TelikProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "\\d{4}" + - "(\\d{6})" + // Device ID - "(\\d+)," + // Type - "\\d{12}," + // Event Time - "\\d+," + - "(\\d{2})(\\d{2})(\\d{2})" + // Date - "(\\d{2})(\\d{2})(\\d{2})," + // Time - "(-?\\d+)," + // Longitude - "(-?\\d+)," + // Latitude - "(\\d)," + // Validity - "(\\d+)," + // Speed - "(\\d+)," + // Course - "(\\d+)," + // Satellites - ".*"); + private static final Pattern PATTERN = new PatternBuilder() + .number("dddd") + .number("(d{6})") // device id + .number("(d+),") // type + .number("d{12},") // event time + .number("d+,") + .number("(dd)(dd)(dd)") // date + .number("(dd)(dd)(dd),") // time + .number("(-?d+),") // longitude + .number("(-?d+),") // latitude + .number("(d),") // validity + .number("(d+),") // speed + .number("(d+),") // course + .number("(d+),") // satellites + .any() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - // Parse message - Matcher parser = PATTERN.matcher((String) msg); + Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - - // Get device by IMEI - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Message type - position.set(Event.KEY_TYPE, parser.group(index++)); - - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); - - // Location - position.setLongitude(Double.parseDouble(parser.group(index++)) / 10000); - position.setLatitude(Double.parseDouble(parser.group(index++)) / 10000); - - // Validity - position.setValid(parser.group(index++).compareTo("1") != 0); + position.set(Event.KEY_TYPE, parser.next()); - // Speed - position.setSpeed(Double.parseDouble(parser.group(index++))); + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Course - position.setCourse(Double.parseDouble(parser.group(index++))); + position.setLongitude(parser.nextDouble() / 10000); + position.setLatitude(parser.nextDouble() / 10000); + position.setValid(parser.nextInt() != 1); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); - // Satellites - position.set(Event.KEY_SATELLITES, parser.group(index++)); + position.set(Event.KEY_SATELLITES, parser.next()); return position; } diff --git a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java index d3bca57ce..830df2b96 100644 --- a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java +++ b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java @@ -200,8 +200,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ChannelBuffer buf = (ChannelBuffer) msg; diff --git a/src/org/traccar/protocol/Tk102ProtocolDecoder.java b/src/org/traccar/protocol/Tk102ProtocolDecoder.java index e9fb86cc2..386f465a5 100644 --- a/src/org/traccar/protocol/Tk102ProtocolDecoder.java +++ b/src/org/traccar/protocol/Tk102ProtocolDecoder.java @@ -41,7 +41,7 @@ public class Tk102ProtocolDecoder extends BaseProtocolDecoder { .expression("([AV])") // validity .number("(dd)(dd.dddd)([NS])") // latitude .number("(ddd)(dd.dddd)([EW])") // longitude - .number("(ddd.ddd)") // Speed + .number("(ddd.ddd)") // speed .number("(dd)(dd)(dd)") // date (ddmmyy) .number("d+") .any() diff --git a/src/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/org/traccar/protocol/Tk103ProtocolDecoder.java index 73ab08b59..95728c447 100644 --- a/src/org/traccar/protocol/Tk103ProtocolDecoder.java +++ b/src/org/traccar/protocol/Tk103ProtocolDecoder.java @@ -38,7 +38,7 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { .number("(d+)(,)?") // device id .expression(".{4},?") // command .number("d*") // imei? - .number("(dd)(dd)(dd),?") // date (yymmdd) + .number("(dd)(dd)(dd),?") // date .expression("([AV]),?") // validity .number("(dd)(dd.d+)") // latitude .expression("([NS]),?") @@ -53,6 +53,17 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { .text(")").optional() .compile(); + private static final Pattern PATTERN_BATTERY = new PatternBuilder() + .number("(d+),") // device id + .text("ZC20,") + .number("(dd)(dd)(dd),") // date (ddmmyy) + .number("(dd)(dd)(dd),") // time + .number("d+,") // battery level + .number("(d+),") // battery voltage + .number("(d+),") // power voltage + .number("d+") // installed + .compile(); + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -77,7 +88,36 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder { } } - Parser parser = new Parser(PATTERN, sentence); + Parser parser = new Parser(PATTERN_BATTERY, sentence); + if (parser.matches()) { + Position position = new Position(); + position.setProtocol(getProtocolName()); + + if (!identify(parser.next(), channel)) { + return null; + } + position.setDeviceId(getDeviceId()); + + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + + getLastLocation(position, dateBuilder.getDate()); + + int battery = parser.nextInt(); + if (battery != 65535) { + position.set(Event.KEY_BATTERY, battery); + } + + int power = parser.nextInt(); + if (power != 65535) { + position.set(Event.KEY_POWER, battery); + } + + return position; + } + + parser = new Parser(PATTERN, sentence); if (!parser.matches()) { return null; } diff --git a/src/org/traccar/protocol/Tr20ProtocolDecoder.java b/src/org/traccar/protocol/Tr20ProtocolDecoder.java index 069757820..fac0f8ee9 100644 --- a/src/org/traccar/protocol/Tr20ProtocolDecoder.java +++ b/src/org/traccar/protocol/Tr20ProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2012 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; @@ -31,98 +31,65 @@ public class Tr20ProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN_PING = Pattern.compile( - "%%[^,]+,(\\d+)"); - - private static final Pattern PATTERN_DATA = Pattern.compile( - "%%" + - "([^,]+)," + // Id - "([AL])," + // Validity - "(\\d{2})(\\d{2})(\\d{2})" + // Date (YYMMDD) - "(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS) - "([NS])" + - "(\\d{2})(\\d{2}\\.\\d+)" + // Latitude (DDMM.MMMM) - "([EW])" + - "(\\d{3})(\\d{2}\\.\\d+)," + // Longitude (DDDMM.MMMM) - "(\\d+)," + // Speed - "(\\d+)," + // Course - ".*"); + private static final Pattern PATTERN_PING = new PatternBuilder() + .text("%%") + .expression("[^,]+,") + .number("(d+)") + .compile(); + + private static final Pattern PATTERN_DATA = new PatternBuilder() + .text("%%") + .expression("([^,]+),") // id + .expression("([AL]),") // validity + .number("(dd)(dd)(dd)") // date (yymmdd) + .number("(dd)(dd)(dd),") // time + .expression("([NS])") + .number("(dd)(dd.d+)") // latitude + .expression("([EW])") + .number("(ddd)(dd.d+),") // longitude + .number("(d+),") // speed + .number("(d+),") // course + .any() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { - - String sentence = (String) msg; + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - // Keep alive message - Matcher parser = PATTERN_PING.matcher(sentence); + Parser parser = new Parser(PATTERN_PING, (String) msg); if (parser.matches()) { - - // Send response if (channel != null) { - channel.write("&&" + parser.group(1) + "\r\n"); - } - } else { - - // Data message parse - parser = PATTERN_DATA.matcher(sentence); - - // Unknown message - if (!parser.matches()) { - return null; + channel.write("&&" + parser.next() + "\r\n"); // keep-alive response } + return null; + } - // Create new position - Position position = new Position(); - position.setProtocol(getProtocolName()); - - Integer index = 1; - - // Get device by id - if (!identify(parser.group(index++), channel)) { - return null; - } - position.setDeviceId(getDeviceId()); - - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0); - - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); + parser = new Parser(PATTERN_DATA, (String) msg); + if (!parser.matches()) { + return null; + } - // Latitude - int hemisphere = 1; - if (parser.group(index++).compareTo("S") == 0) hemisphere = -1; - Double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60; - position.setLatitude(latitude * hemisphere); + Position position = new Position(); + position.setProtocol(getProtocolName()); - // Longitude - hemisphere = 1; - if (parser.group(index++).compareTo("W") == 0) hemisphere = -1; - Double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 60; - position.setLongitude(longitude * hemisphere); + if (!identify(parser.next(), channel)) { + return null; + } + position.setDeviceId(getDeviceId()); - // Speed - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(parser.group(index++)))); + position.setValid(parser.next().equals("A")); - // Course - position.setCourse(Double.parseDouble(parser.group(index++))); + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - return position; - } + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN)); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); - return null; + return position; } } diff --git a/src/org/traccar/protocol/Tr900ProtocolDecoder.java b/src/org/traccar/protocol/Tr900ProtocolDecoder.java index f8d0a7a5c..b3500d552 100644 --- a/src/org/traccar/protocol/Tr900ProtocolDecoder.java +++ b/src/org/traccar/protocol/Tr900ProtocolDecoder.java @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -31,93 +31,65 @@ public class Tr900ProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - ">(\\d+)," + // ID - "\\d+," + // Period - "(\\d)," + // Fix - "(\\d{2})(\\d{2})(\\d{2})," + // Date (YYMMDD) - "(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS) - "([EW])" + - "(\\d{3})(\\d{2}\\.\\d+)," + // Longitude (DDDMM.MMMM) - "([NS])" + - "(\\d{2})(\\d{2}\\.\\d+)," + // Latitude (DDMM.MMMM) - "[^,]*," + // Command - "(\\d+\\.?\\d*)," + // Speed - "(\\d+\\.?\\d*)," + // Course - "(\\d+)," + // GSM - "(\\d+)," + // Event - "(\\d+)-" + // ADC - "(\\d+)," + // Battery - "\\d+," + // Impulses - "(\\d+)," + // Input - "(\\d+)" + // Status - ".*(?:\r\n)?"); + private static final Pattern PATTERN = new PatternBuilder() + .number(">(d+),") // id + .number("d+,") // period + .number("(d),") // fix + .number("(dd)(dd)(dd),") // date (yymmdd) + .number("(dd)(dd)(dd),") // time + .expression("([EW])") + .number("(ddd)(dd.d+),") // longitude + .expression("([NS])") + .number("(dd)(dd.d+),") // latitude + .expression("[^,]*,") // command + .number("(d+.?d*),") // speed + .number("(d+.?d*),") // course + .number("(d+),") // gsm + .number("(d+),") // event + .number("(d+)-") // adc + .number("(d+),") // battery + .number("d+,") // impulses + .number("(d+),") // input + .number("(d+)") // status + .any() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - String sentence = (String) msg; - - // Parse message - Matcher parser = PATTERN.matcher(sentence); + Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - Integer index = 1; - // Identification - if (!identify(parser.group(index++), channel, remoteAddress)) { + if (!identify(parser.next(), channel, remoteAddress)) { return null; } position.setDeviceId(getDeviceId()); - // Validity - position.setValid(parser.group(index++).compareTo("1") == 0); - - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); - - // Longitude - String hemisphere = parser.group(index++); - Double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 60; - if (hemisphere.compareTo("W") == 0) longitude = -longitude; - position.setLongitude(longitude); + position.setValid(parser.nextInt() == 1); - // Latitude - hemisphere = parser.group(index++); - Double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60; - if (hemisphere.compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Speed - position.setSpeed(Double.parseDouble(parser.group(index++))); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN)); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.HEM_DEG_MIN)); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); - // Course - position.setCourse(Double.parseDouble(parser.group(index++))); + position.set(Event.KEY_GSM, parser.next()); + position.set(Event.KEY_EVENT, parser.nextInt()); + position.set(Event.PREFIX_ADC + 1, parser.nextInt()); + position.set(Event.KEY_BATTERY, parser.nextInt()); + position.set(Event.KEY_INPUT, parser.next()); + position.set(Event.KEY_STATUS, parser.next()); - // Other - position.set(Event.KEY_GSM, parser.group(index++)); - position.set(Event.KEY_EVENT, Integer.parseInt(parser.group(index++))); - position.set(Event.PREFIX_ADC + 1, Integer.parseInt(parser.group(index++))); - position.set(Event.KEY_BATTERY, Integer.parseInt(parser.group(index++))); - position.set(Event.KEY_INPUT, parser.group(index++)); - position.set(Event.KEY_STATUS, parser.group(index++)); return position; } diff --git a/src/org/traccar/protocol/TrackboxProtocolDecoder.java b/src/org/traccar/protocol/TrackboxProtocolDecoder.java index 39b1662ea..bf4c4f34d 100644 --- a/src/org/traccar/protocol/TrackboxProtocolDecoder.java +++ b/src/org/traccar/protocol/TrackboxProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2014 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.model.Event; import org.traccar.model.Position; @@ -31,18 +31,19 @@ public class TrackboxProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "(\\d{2})(\\d{2})(\\d{2})\\.(\\d{3})," + // Time - "(\\d{2})(\\d{2}\\.\\d{4})([NS])," + // Latitude (DDMM.MMMM) - "(\\d{3})(\\d{2}\\.\\d{4})([EW])," + // Longitude (DDDMM.MMMM) - "(\\d+\\.\\d)," + // HDOP - "(-?\\d+\\.?\\d*)," + // Altitude - "(\\d)," + // Fix Type - "(\\d+\\.\\d+)," + // Course - "(\\d+\\.\\d+)," + // Speed (kph) - "(\\d+\\.\\d+)," + // Speed (knots) - "(\\d{2})(\\d{2})(\\d{2})," + // Date - "(\\d+)"); // Satellites + private static final Pattern PATTERN = new PatternBuilder() + .number("(dd)(dd)(dd).(ddd),") // time + .number("(dd)(dd.dddd)([NS]),") // latitude + .number("(ddd)(dd.dddd)([EW]),") // longitude + .number("(d+.d),") // hdop + .number("(-?d+.?d*),") // altitude + .number("(d),") // fix type + .number("(d+.d+),") // course + .number("d+.d+,") // speed (kph) + .number("(d+.d+),") // speed (knots) + .number("(dd)(dd)(dd),") // date + .number("(d+)") // satellites + .compile(); private void sendResponse(Channel channel) { if (channel != null) { @@ -52,83 +53,51 @@ public class TrackboxProtocolDecoder extends BaseProtocolDecoder { @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { String sentence = (String) msg; if (sentence.startsWith("a=connect")) { - String id = sentence.substring(sentence.indexOf("i=") + 2); if (identify(id, channel)) { sendResponse(channel); } + return null; + } - } else { - - Matcher parser = PATTERN.matcher(sentence); - if (!parser.matches()) { - return null; - } - sendResponse(channel); - - Position position = new Position(); - position.setDeviceId(getDeviceId()); - position.setProtocol(getProtocolName()); - - Integer index = 1; - - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MILLISECOND, Integer.parseInt(parser.group(index++))); - - // Latitude - Double latitude = Double.parseDouble(parser.group(index++)); - latitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); - - // Longitude - Double longitude = Double.parseDouble(parser.group(index++)); - longitude += Double.parseDouble(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("W") == 0) longitude = -longitude; - position.setLongitude(longitude); - - // HDOP - position.set(Event.KEY_HDOP, parser.group(index++)); - - // Altitude - position.setAltitude(Double.parseDouble(parser.group(index++))); - - // Validity - int fix = Integer.parseInt(parser.group(index++)); - position.set(Event.KEY_GPS, fix); - position.setValid(fix > 0); - - // Course - position.setCourse(Double.parseDouble(parser.group(index++))); - - // Speed - index += 1; // speed in kph - position.setSpeed(Double.parseDouble(parser.group(index++))); - - // Date - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); - - // Satellites - position.set(Event.KEY_SATELLITES, parser.group(index++)); - - return position; + Parser parser = new Parser(PATTERN, sentence); + if (!parser.matches()) { + return null; } + sendResponse(channel); + + Position position = new Position(); + position.setDeviceId(getDeviceId()); + position.setProtocol(getProtocolName()); + + DateBuilder dateBuilder = new DateBuilder() + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt(), parser.nextInt()); + + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + + position.set(Event.KEY_HDOP, parser.next()); + + position.setAltitude(parser.nextDouble()); + + int fix = parser.nextInt(); + position.set(Event.KEY_GPS, fix); + position.setValid(fix > 0); + + position.setCourse(parser.nextDouble()); + position.setSpeed(parser.nextDouble()); + + dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + position.set(Event.KEY_SATELLITES, parser.next()); - return null; + return position; } } diff --git a/src/org/traccar/protocol/TramigoProtocolDecoder.java b/src/org/traccar/protocol/TramigoProtocolDecoder.java index be97f24fd..456086fe2 100644 --- a/src/org/traccar/protocol/TramigoProtocolDecoder.java +++ b/src/org/traccar/protocol/TramigoProtocolDecoder.java @@ -105,7 +105,6 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { String sentence = buf.toString(Charset.defaultCharset()); - // Coordinates Pattern pattern = Pattern.compile("(-?\\d+\\.\\d+), (-?\\d+\\.\\d+)"); Matcher matcher = pattern.matcher(sentence); if (!matcher.find()) { @@ -114,7 +113,6 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { position.setLatitude(Double.parseDouble(matcher.group(1))); position.setLongitude(Double.parseDouble(matcher.group(2))); - // Speed and Course pattern = Pattern.compile("([NSWE]{1,2}) with speed (\\d+) km/h"); matcher = pattern.matcher(sentence); if (matcher.find()) { @@ -122,7 +120,6 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { position.setCourse(0); // matcher.group(1) for course } - // Time pattern = Pattern.compile("(\\d{1,2}:\\d{2} \\w{3} \\d{1,2})"); matcher = pattern.matcher(sentence); if (!matcher.find()) { @@ -130,6 +127,7 @@ public class TramigoProtocolDecoder extends BaseProtocolDecoder { } DateFormat dateFormat = new SimpleDateFormat("HH:mm MMM d yyyy", Locale.ENGLISH); position.setTime(dateFormat.parse(matcher.group(1) + " " + Calendar.getInstance().get(Calendar.YEAR))); + return position; } diff --git a/src/org/traccar/protocol/MaxonProtocol.java b/src/org/traccar/protocol/TrvProtocol.java index 587c3f014..916b7d612 100644 --- a/src/org/traccar/protocol/MaxonProtocol.java +++ b/src/org/traccar/protocol/TrvProtocol.java @@ -17,18 +17,18 @@ 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; +import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.TrackerServer; import java.util.List; -public class MaxonProtocol extends BaseProtocol { +public class TrvProtocol extends BaseProtocol { - public MaxonProtocol() { - super("maxon"); + public TrvProtocol() { + super("trv"); } @Override @@ -36,10 +36,10 @@ public class MaxonProtocol extends BaseProtocol { serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(1024)); + pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, '#')); pipeline.addLast("stringDecoder", new StringDecoder()); pipeline.addLast("stringEncoder", new StringEncoder()); - pipeline.addLast("objectDecoder", new MaxonProtocolDecoder(MaxonProtocol.this)); + pipeline.addLast("objectDecoder", new TrvProtocolDecoder(TrvProtocol.this)); } }); } diff --git a/src/org/traccar/protocol/TrvProtocolDecoder.java b/src/org/traccar/protocol/TrvProtocolDecoder.java new file mode 100644 index 000000000..fe1162d24 --- /dev/null +++ b/src/org/traccar/protocol/TrvProtocolDecoder.java @@ -0,0 +1,125 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Event; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class TrvProtocolDecoder extends BaseProtocolDecoder { + + public TrvProtocolDecoder(TrvProtocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .text("TRV") + .number("APdd") + .number("(dd)(dd)(dd)") // date + .expression("([AV])") // validity + .number("(dd)(dd.d+)") // latitude + .expression("([NS])") + .number("(ddd)(dd.d+)") // longitude + .expression("([EW])") + .number("(ddd.d)") // speed + .number("(dd)(dd)(dd)") // time + .number("(ddd.dd)") // course + .number("(ddd)") // gsm + .number("(ddd)") // satellites + .number("(ddd)") // battery + .number("(d)") // acc + .number("dd") // arm status + .number("dd,") // working mode + .number("(d+),") // mcc + .number("(d+),") // mnc + .number("(d+),") // lac + .number("(d+)") // cell + .any() + .compile(); + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + String sentence = (String) msg; + + String type = sentence.substring(3, 7); + if (channel != null) { + channel.write("B" + type.substring(1)); // response + } + + if (type.equals("AP00")) { + identify(sentence.substring(7), channel); + return null; + } + + if (!hasDeviceId()) { + return null; + } + + if (type.equals("AP01") || type.equals("AP10")) { + + Parser parser = new Parser(PATTERN, sentence); + if (!parser.matches()) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(getDeviceId()); + + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()); + + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + + dateBuilder.setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + position.setCourse(parser.nextDouble()); + + position.set(Event.KEY_GSM, parser.nextInt()); + position.set(Event.KEY_SATELLITES, parser.nextInt()); + position.set(Event.KEY_BATTERY, parser.nextInt()); + + int acc = parser.nextInt(); + if (acc != 0) { + position.set(Event.KEY_IGNITION, acc == 1); + } + + position.set(Event.KEY_MCC, parser.nextInt()); + position.set(Event.KEY_MNC, parser.nextInt()); + position.set(Event.KEY_LAC, parser.nextInt()); + position.set(Event.KEY_CELL, parser.nextInt()); + + return position; + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/UlbotechProtocolDecoder.java b/src/org/traccar/protocol/UlbotechProtocolDecoder.java index b015e768a..68d04906d 100644 --- a/src/org/traccar/protocol/UlbotechProtocolDecoder.java +++ b/src/org/traccar/protocol/UlbotechProtocolDecoder.java @@ -24,6 +24,7 @@ import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; import org.traccar.Context; import org.traccar.helper.BitUtil; +import org.traccar.helper.ObdDecoder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -58,10 +59,9 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder { int end = buf.readerIndex() + length; while (buf.readerIndex() < end) { - int parameterLength = buf.readUnsignedByte() >> 4; - String key = String.format("pid%02X", buf.readUnsignedByte()); - String value = ChannelBuffers.hexDump(buf.readBytes(parameterLength - 2)); - position.set(key, value); + int parameterLength = buf.getUnsignedByte(buf.readerIndex()) >> 4; + position.add(ObdDecoder.decode(buf.readUnsignedByte() & 0x0F, buf.readUnsignedByte(), + ChannelBuffers.hexDump(buf.readBytes(parameterLength - 2)))); } } @@ -105,18 +105,15 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder { buf.readUnsignedByte(); // version buf.readUnsignedByte(); // type - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - // Get device id String imei = ChannelBuffers.hexDump(buf.readBytes(8)).substring(1); if (!identify(imei, channel)) { return null; } position.setDeviceId(getDeviceId()); - // Time long seconds = buf.readUnsignedInt() & 0x7fffffffL; seconds += 946684800L; // 2000-01-01 00:00 seconds -= timeZone; @@ -197,7 +194,7 @@ public class UlbotechProtocolDecoder extends BaseProtocolDecoder { break; case DATA_VIN: - position.set("vin", buf.readBytes(length).toString(Charset.defaultCharset())); + position.set(Event.KEY_VIN, buf.readBytes(length).toString(Charset.defaultCharset())); break; case DATA_RFID: diff --git a/src/org/traccar/protocol/VisiontekProtocolDecoder.java b/src/org/traccar/protocol/VisiontekProtocolDecoder.java index 8dc4bdf06..2fecda341 100644 --- a/src/org/traccar/protocol/VisiontekProtocolDecoder.java +++ b/src/org/traccar/protocol/VisiontekProtocolDecoder.java @@ -19,7 +19,10 @@ import java.net.SocketAddress; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; -import org.traccar.helper.*; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; diff --git a/src/org/traccar/protocol/Ev603Protocol.java b/src/org/traccar/protocol/WatchProtocol.java index 8e00b534f..3a10f9c59 100644 --- a/src/org/traccar/protocol/Ev603Protocol.java +++ b/src/org/traccar/protocol/WatchProtocol.java @@ -18,16 +18,17 @@ package org.traccar.protocol; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.handler.codec.string.StringDecoder; +import org.jboss.netty.handler.codec.string.StringEncoder; import org.traccar.BaseProtocol; import org.traccar.CharacterDelimiterFrameDecoder; import org.traccar.TrackerServer; import java.util.List; -public class Ev603Protocol extends BaseProtocol { +public class WatchProtocol extends BaseProtocol { - public Ev603Protocol() { - super("ev603"); + public WatchProtocol() { + super("watch"); } @Override @@ -35,9 +36,10 @@ public class Ev603Protocol extends BaseProtocol { serverList.add(new TrackerServer(new ServerBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { - pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ';')); + pipeline.addLast("frameDecoder", new CharacterDelimiterFrameDecoder(1024, ']')); pipeline.addLast("stringDecoder", new StringDecoder()); - pipeline.addLast("objectDecoder", new Ev603ProtocolDecoder(Ev603Protocol.this)); + pipeline.addLast("stringEncoder", new StringEncoder()); + pipeline.addLast("objectDecoder", new WatchProtocolDecoder(WatchProtocol.this)); } }); } diff --git a/src/org/traccar/protocol/WatchProtocolDecoder.java b/src/org/traccar/protocol/WatchProtocolDecoder.java new file mode 100644 index 000000000..a24d0a56b --- /dev/null +++ b/src/org/traccar/protocol/WatchProtocolDecoder.java @@ -0,0 +1,146 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Event; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.util.regex.Pattern; + +public class WatchProtocolDecoder extends BaseProtocolDecoder { + + public WatchProtocolDecoder(WatchProtocol protocol) { + super(protocol); + } + + private static final Pattern PATTERN = new PatternBuilder() + .text("[") + .expression("(..)").text("*") // manufacturer + .number("(d+)").text("*") // equipment id + .number("xxxx").text("*") // length + .expression("([^,]+)") // type + .expression("(.*)") // content + .compile(); + + private static final Pattern PATTERN_POSITION = new PatternBuilder() + .text(",") + .number("(dd)(dd)(dd),") // date (ddmmyy) + .number("(dd)(dd)(dd),") // time + .expression("([AV]),") // validity + .number("(d+.d+),") // latitude + .expression("([NS]),") + .number("(d+.d+),") // longitude + .expression("([EW]),") + .number("(d+.d+),") // speed + .number("(d+.d+),") // course + .number("(d+.?d*),") // altitude + .number("(d+),") // satellites + .number("(d+),") // gsm + .number("(d+),") // battery + .number("(d+),") // steps + .number("d+,") // tumbles + .any() + .compile(); + + private void sendResponse(Channel channel, String manufacturer, String id, String content) { + if (channel != null) { + channel.write( + String.format("[%s*%s*%04x*%s]", manufacturer, id, content.length(), content)); + } + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + Parser parser = new Parser(PATTERN, (String) msg); + if (!parser.matches()) { + return null; + } + + String manufacturer = parser.next(); + String id = parser.next(); + if (!identify(id, channel)) { + return null; + } + + String type = parser.next(); + String content = parser.next(); + + if (type.equals("LK")) { + + sendResponse(channel, manufacturer, id, "LK"); + + if (!content.isEmpty()) { + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(getDeviceId()); + + getLastLocation(position, null); + + position.set(Event.KEY_BATTERY, content.split(",")[3]); + + return position; + } + + } else if (type.equals("UD") || type.equals("UD2") || type.equals("AL")) { + + if (type.equals("AL")) { + sendResponse(channel, manufacturer, id, "AL"); + } + + parser = new Parser(PATTERN_POSITION, content); + if (!parser.matches()) { + return null; + } + + Position position = new Position(); + position.setProtocol(getProtocolName()); + position.setDeviceId(getDeviceId()); + + DateBuilder dateBuilder = new DateBuilder() + .setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM)); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + position.setAltitude(parser.nextDouble()); + + position.set(Event.KEY_SATELLITES, parser.nextInt()); + position.set(Event.KEY_GSM, parser.nextInt()); + position.set(Event.KEY_BATTERY, parser.nextInt()); + + position.set("steps", parser.nextInt()); + + return position; + + } + + return null; + } + +} diff --git a/src/org/traccar/protocol/WondexProtocolDecoder.java b/src/org/traccar/protocol/WondexProtocolDecoder.java index c2a5a2201..1a639b05c 100644 --- a/src/org/traccar/protocol/WondexProtocolDecoder.java +++ b/src/org/traccar/protocol/WondexProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2014 Anton Tananaev (anton.tananaev@gmail.com) + * Copyright 2013 - 2015 Anton Tananaev (anton.tananaev@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ package org.traccar.protocol; import java.net.SocketAddress; -import java.util.Calendar; -import java.util.TimeZone; -import java.util.regex.Matcher; import java.util.regex.Pattern; import org.jboss.netty.channel.Channel; import org.traccar.BaseProtocolDecoder; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Event; import org.traccar.model.Position; @@ -32,88 +32,67 @@ public class WondexProtocolDecoder extends BaseProtocolDecoder { super(protocol); } - private static final Pattern PATTERN = Pattern.compile( - "[^\\d]*" + // Header - "(\\d+)," + // Device Identifier - "(\\d{4})(\\d{2})(\\d{2})" + // Date (YYYYMMDD) - "(\\d{2})(\\d{2})(\\d{2})," + // Time (HHMMSS) - "(-?\\d+\\.\\d+)," + // Longitude - "(-?\\d+\\.\\d+)," + // Latitude - "(\\d+)," + // Speed - "(\\d+)," + // Course - "(-?\\d+\\.?\\d*)," + // Altitude - "(\\d+)," + // Satellites - "(\\d+),?" + // Event - "(?:(\\d+\\.\\d+)V,)?" + // Battery - "(\\d+\\.\\d+)?,?" + // Odometer - "(\\d+)?,?" + // Input - "(\\d+\\.\\d+)?,?" + // ADC1 - "(\\d+\\.\\d+)?,?" + // ADC2 - "(\\d+)?.*"); // Output + private static final Pattern PATTERN = new PatternBuilder() + .number("[^d]*") // deader + .number("(d+),") // device identifier + .number("(dddd)(dd)(dd)") // date + .number("(dd)(dd)(dd),") // time + .number("(-?d+.d+),") // longitude + .number("(-?d+.d+),") // latitude + .number("(d+),") // speed + .number("(d+),") // course + .number("(-?d+.?d*),") // altitude + .number("(d+),") // satellites + .number("(d+),?") // event + .number("(d+.d+)V,").optional() // battery + .number("(d+.d+)?,?") // odometer + .number("(d+)?,?") // input + .number("(d+.d+)?,?") // adc1 + .number("(d+.d+)?,?") // adc2 + .number("(d+)?") // output + .any() + .compile(); @Override protected Object decode( - Channel channel, SocketAddress remoteAddress, Object msg) - throws Exception { + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { - // Parse message - Matcher parser = PATTERN.matcher((String) msg); + Parser parser = new Parser(PATTERN, (String) msg); if (!parser.matches()) { return null; } - // Create new position Position position = new Position(); position.setProtocol(getProtocolName()); - int index = 1; - // Device identifier - if (!identify(parser.group(index++), channel)) { + if (!identify(parser.next(), channel)) { return null; } position.setDeviceId(getDeviceId()); - // Time - Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - time.clear(); - time.set(Calendar.YEAR, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MONTH, Integer.parseInt(parser.group(index++)) - 1); - time.set(Calendar.DAY_OF_MONTH, Integer.parseInt(parser.group(index++))); - time.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.parseInt(parser.group(index++))); - time.set(Calendar.SECOND, Integer.parseInt(parser.group(index++))); - position.setTime(time.getTime()); + DateBuilder dateBuilder = new DateBuilder() + .setDate(parser.nextInt(), parser.nextInt(), parser.nextInt()) + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); - // Position data - position.setLongitude(Double.parseDouble(parser.group(index++))); - position.setLatitude(Double.parseDouble(parser.group(index++))); - position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(parser.group(index++)))); - position.setCourse(Double.parseDouble(parser.group(index++))); - position.setAltitude(Double.parseDouble(parser.group(index++))); + position.setLongitude(parser.nextDouble()); + position.setLatitude(parser.nextDouble()); + position.setSpeed(UnitsConverter.knotsFromKph(parser.nextDouble())); + position.setCourse(parser.nextDouble()); + position.setAltitude(parser.nextDouble()); - // Satellites - int satellites = Integer.parseInt(parser.group(index++)); + int satellites = parser.nextInt(); position.setValid(satellites >= 3); position.set(Event.KEY_SATELLITES, satellites); - // Event - position.set(Event.KEY_EVENT, parser.group(index++)); + position.set(Event.KEY_EVENT, parser.next()); + position.set(Event.KEY_BATTERY, parser.next()); + position.set(Event.KEY_ODOMETER, parser.next()); + position.set(Event.KEY_INPUT, parser.next()); + position.set(Event.PREFIX_ADC + 1, parser.next()); + position.set(Event.PREFIX_ADC + 2, parser.next()); + position.set(Event.KEY_OUTPUT, parser.next()); - // Battery - position.set(Event.KEY_BATTERY, parser.group(index++)); - - // Odometer - position.set(Event.KEY_ODOMETER, parser.group(index++)); - - // Input - position.set(Event.KEY_INPUT, parser.group(index++)); - - // ADC - position.set(Event.PREFIX_ADC + 1, parser.group(index++)); - position.set(Event.PREFIX_ADC + 2, parser.group(index++)); - - // Output - position.set(Event.KEY_OUTPUT, parser.group(index++)); return position; } diff --git a/src/org/traccar/web/AsyncServlet.java b/src/org/traccar/web/AsyncServlet.java index 73803ab91..63b08ff3e 100644 --- a/src/org/traccar/web/AsyncServlet.java +++ b/src/org/traccar/web/AsyncServlet.java @@ -50,7 +50,7 @@ public class AsyncServlet extends BaseServlet { public static class AsyncSession { - private static final boolean DEBUG_ASYNC = false; + private static final boolean DEBUG_ASYNC = true; private static final long SESSION_TIMEOUT = 30; private static final long REQUEST_TIMEOUT = 20; @@ -88,7 +88,7 @@ public class AsyncServlet extends BaseServlet { private final ConnectionManager.DataCacheListener dataListener = new ConnectionManager.DataCacheListener() { @Override - public void onUpdate(Position position) { + public void onUpdatePosition(Position position) { synchronized (AsyncSession.this) { logEvent("onUpdate deviceId: " + position.getDeviceId()); if (!destroyed) { @@ -114,7 +114,9 @@ public class AsyncServlet extends BaseServlet { } Context.getConnectionManager().removeListener(devices, dataListener); synchronized (ASYNC_SESSIONS) { - ASYNC_SESSIONS.remove(userId); + if (ASYNC_SESSIONS.get(userId) == AsyncSession.this) { + ASYNC_SESSIONS.remove(userId); + } } } }; |