diff options
Diffstat (limited to 'src/org/traccar')
-rw-r--r-- | src/org/traccar/protocol/SkypatrolProtocolDecoder.java | 247 |
1 files changed, 168 insertions, 79 deletions
diff --git a/src/org/traccar/protocol/SkypatrolProtocolDecoder.java b/src/org/traccar/protocol/SkypatrolProtocolDecoder.java index d5adf2aaf..3f54fd0bf 100644 --- a/src/org/traccar/protocol/SkypatrolProtocolDecoder.java +++ b/src/org/traccar/protocol/SkypatrolProtocolDecoder.java @@ -15,10 +15,10 @@ */ package org.traccar.protocol; +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.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.traccar.GenericProtocolDecoder; @@ -32,119 +32,208 @@ import org.traccar.model.Position; public class SkypatrolProtocolDecoder extends GenericProtocolDecoder { /** - * Device ID - */ - private Long deviceId; - - /** * Initialize */ public SkypatrolProtocolDecoder(DataManager dataManager) { super(dataManager); } - /** - * Regular expressions pattern - */ - static private 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{2})?," + // Speed - "(\\d+\\.\\d{2})?," + // Course - "(\\d{2})(\\d{2})(\\d{2})" + // Date (DDMMYY) - ".+"); // Other (Checksumm) - + private static boolean checkBit(long mask, int bit) { + long checkMask = 1 << bit; + return (mask & checkMask) == checkMask; + } + + private static double convertCoordinate(long coordinate) { + int sign = 1; + if (coordinate > 0x7fffffffl) { + sign = -1; + coordinate = 0xffffffffl - coordinate; + } + + double degrees = coordinate / 1000000; + degrees += (coordinate % 1000000) / 600000.0; + + return sign * degrees; + } + /** * Decode message */ + @Override protected Object decode( ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { - String sentence = (String) msg; + ChannelBuffer buf = (ChannelBuffer) msg; - // Detect device ID - if (sentence.contains("$PGID")) { - String imei = sentence.substring(6, 6 + 15); - try { - deviceId = getDataManager().getDeviceByImei(imei).getId(); - } catch(Exception error) { - Log.warning("Unknown device - " + imei); - } + // Read header + int apiNumber = buf.readUnsignedShort(); + int commandType = buf.readUnsignedByte(); + int messageType = buf.getUnsignedByte(buf.readerIndex()) >> 4; + boolean needAck = (buf.readUnsignedByte() & 0xf) == 1; + long mask = 0; + if (buf.readUnsignedByte() == 4) { + mask = buf.readUnsignedInt(); } - // Parse message - else if (sentence.contains("$GPRMC") && deviceId != null) { - - // Send response - if (channel != null) { - channel.write("OK1\r\n"); + // Binary position report + if (apiNumber == 5 && + commandType == 2 && + messageType == 1 && + checkBit(mask, 0)) { + + // Create new position + Position position = new Position(); + StringBuilder extendedInfo = new StringBuilder("<protocol>skypatrol</protocol>"); + + // Status code + if (checkBit(mask, 1)) { + extendedInfo.append("<status>"); + extendedInfo.append(buf.readUnsignedInt()); + extendedInfo.append("</status>"); } - - // Parse message - Matcher parser = pattern.matcher(sentence); - if (!parser.matches()) { + + // Device id + String id = null; + if (checkBit(mask, 23)) { + id = buf.toString(buf.readerIndex(), 8, Charset.defaultCharset()).trim(); + buf.skipBytes(8); + } else if (checkBit(mask, 2)) { + id = buf.toString(buf.readerIndex(), 22, Charset.defaultCharset()).trim(); + buf.skipBytes(22); + } else { + Log.warning("No device id field"); return null; } + try { + position.setDeviceId(getDataManager().getDeviceByImei(id).getId()); + } catch(Exception error) { + Log.warning("Unknown device - " + id); + return null; + } + + // IO data + if (checkBit(mask, 3)) { + buf.readUnsignedShort(); + } + + // ADC 1 + if (checkBit(mask, 4)) { + buf.readUnsignedShort(); + } - // Create new position - Position position = new Position(); - position.setDeviceId(deviceId); - - Integer index = 1; + // ADC 2 + if (checkBit(mask, 5)) { + buf.readUnsignedShort(); + } - // Time + // Function category + if (checkBit(mask, 7)) { + buf.readUnsignedByte(); + } + Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); time.clear(); - time.set(Calendar.HOUR, Integer.valueOf(parser.group(index++))); - time.set(Calendar.MINUTE, Integer.valueOf(parser.group(index++))); - time.set(Calendar.SECOND, Integer.valueOf(parser.group(index++))); - index += 1; // Skip milliseconds - // Validity - position.setValid(parser.group(index++).compareTo("A") == 0 ? true : false); + // Date + if (checkBit(mask, 8)) { + time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); + time.set(Calendar.MONTH, buf.readUnsignedByte() - 1); + time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); + } + + // GPS status + if (checkBit(mask, 9)) { + position.setValid(buf.readUnsignedByte() == 1); + } // Latitude - Double latitude = Double.valueOf(parser.group(index++)); - latitude += Double.valueOf(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("S") == 0) latitude = -latitude; - position.setLatitude(latitude); + if (checkBit(mask, 10)) { + position.setLatitude(convertCoordinate(buf.readUnsignedInt())); + } // Longitude - Double lonlitude = Double.valueOf(parser.group(index++)); - lonlitude += Double.valueOf(parser.group(index++)) / 60; - if (parser.group(index++).compareTo("W") == 0) lonlitude = -lonlitude; - position.setLongitude(lonlitude); - + if (checkBit(mask, 11)) { + position.setLongitude(convertCoordinate(buf.readUnsignedInt())); + } + // Speed - String speed = parser.group(index++); - if (speed != null) { - position.setSpeed(Double.valueOf(speed)); - } else { - position.setSpeed(0.0); + if (checkBit(mask, 12)) { + position.setSpeed(buf.readUnsignedShort() / 10.0); } // Course - String course = parser.group(index++); - if (course != null) { - position.setCourse(Double.valueOf(course)); - } else { - position.setCourse(0.0); + if (checkBit(mask, 13)) { + position.setCourse(buf.readUnsignedShort() / 10.0); } - // Date - time.set(Calendar.DAY_OF_MONTH, Integer.valueOf(parser.group(index++))); - time.set(Calendar.MONTH, Integer.valueOf(parser.group(index++)) - 1); - time.set(Calendar.YEAR, 2000 + Integer.valueOf(parser.group(index++))); + // Time + if (checkBit(mask, 14)) { + time.set(Calendar.HOUR, buf.readUnsignedByte()); + time.set(Calendar.MINUTE, buf.readUnsignedByte()); + time.set(Calendar.SECOND, buf.readUnsignedByte()); + } + position.setTime(time.getTime()); - + // Altitude - position.setAltitude(0.0); + if (checkBit(mask, 15)) { + buf.skipBytes(3); + } + + // Satellites + if (checkBit(mask, 16)) { + extendedInfo.append("<satellites>"); + extendedInfo.append(buf.readUnsignedByte()); + extendedInfo.append("</satellites>"); + } + + // Battery percentage + if (checkBit(mask, 17)) { + buf.readUnsignedShort(); + } + + // Trip milage + if (checkBit(mask, 20)) { + extendedInfo.append("<trip>"); + extendedInfo.append(buf.readUnsignedInt()); + extendedInfo.append("</trip>"); + } + + // Milage + if (checkBit(mask, 21)) { + extendedInfo.append("<milage>"); + extendedInfo.append(buf.readUnsignedInt()); + extendedInfo.append("</milage>"); + } + + // Time of message generation + if (checkBit(mask, 22)) { + buf.skipBytes(6); + } + + // Battery level + if (checkBit(mask, 24)) { + position.setPower(buf.readUnsignedShort() / 1000.0); + } + + // GPS overspeed + if (checkBit(mask, 25)) { + buf.skipBytes(18); + } + + // Cell information + if (checkBit(mask, 26)) { + buf.skipBytes(54); + } + + // Sequence number + if (checkBit(mask, 28)) { + position.setId((long) buf.readUnsignedShort()); + } + + // Extended info + position.setExtendedInfo(extendedInfo.toString()); return position; } |