/*
 * Copyright 2012 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 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.UnitsConverter;
import org.traccar.model.Event;
import org.traccar.model.Position;

public class Gt02ProtocolDecoder extends BaseProtocolDecoder {

    public Gt02ProtocolDecoder(Gt02Protocol protocol) {
        super(protocol);
    }

    private String readImei(ChannelBuffer buf) {
        int b = buf.readUnsignedByte();
        StringBuilder imei = new StringBuilder();
        imei.append(b & 0x0F);
        for (int i = 0; i < 7; i++) {
            b = buf.readUnsignedByte();
            imei.append((b & 0xF0) >> 4);
            imei.append(b & 0x0F);
        }
        return imei.toString();
    }

    public static final int MSG_HEARTBEAT = 0x1A;
    public static final int MSG_DATA = 0x10;

    @Override
    protected Object decode(
            Channel channel, SocketAddress remoteAddress, Object msg)
            throws Exception {

        ChannelBuffer buf = (ChannelBuffer) msg;

        buf.skipBytes(2); // header
        buf.readByte(); // size

        // Zero for location messages
        buf.readByte(); // voltage
        buf.readByte(); // gsm signal

        String imei = readImei(buf);
        long index = buf.readUnsignedShort();
        int type = buf.readUnsignedByte();

        if (type == MSG_HEARTBEAT) {

            if (channel != null) {
                byte[] response = {0x54, 0x68, 0x1A, 0x0D, 0x0A};
                channel.write(ChannelBuffers.wrappedBuffer(response));
            }

        } 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);

            // Longitude
            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.setLatitude(latitude);
            position.setLongitude(longitude);
            return position;
        }

        return null;
    }

}

@@ -14,16 +14,23 @@
 ht 2012 
+- 2015 
 Anton Ta
@@ -691,62 +691,8 @@
 ss;%0A
-import java.util.Calendar;%0Aimport java.util.TimeZone;%0A
 impo
@@ -854,24 +854,98 @@
 colDecoder;%0A
+import org.traccar.helper.BitUtil;%0Aimport org.traccar.helper.DateBuilder;%0A
 import org.t
@@ -1776,28 +1776,16 @@
 ect msg)
-%0A           
  throws 
@@ -2424,43 +2424,8 @@
  %7B%0A%0A
-            // Create new position%0A
     
@@ -2576,37 +2576,8 @@
 );%0A%0A
-            // Get device id%0A
     
@@ -2724,307 +2724,128 @@
     
- //
  Date
- and time%0A            Calendar time = Calendar.getInstance(TimeZone.getTimeZone(%22UTC%22));%0A            time.clear();%0A            time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte());%0A            time.set(Calendar.MONTH, buf.readUnsignedByte() - 1);%0A            time.set(Calendar.DAY_OF_MONTH
+Builder dateBuilder = new DateBuilder()%0A                    .setDate(buf.readUnsignedByte(), buf.readUnsignedByte()
 , bu
@@ -2857,34 +2857,34 @@
 dUnsignedByte())
-;
 %0A
+ 
             time
@@ -2883,163 +2883,70 @@
     
-time.set(Calendar.HOUR_OF_DAY, buf.readUnsignedByte());%0A            time.set(Calendar.MINUTE, buf.readUnsignedByte());%0A            time.set(Calendar.SECOND
+       .setTime(buf.readUnsignedByte(), buf.readUnsignedByte()
 , bu
@@ -3001,50 +3001,33 @@
 ime(
-time
+dateBuilder
 .get
-Tim
+Dat
 e());%0A%0A
-            // Latitude%0A
     
@@ -3097,34 +3097,8 @@
 .0);
-%0A%0A            // Longitude
 %0A   
@@ -3172,29 +3172,8 @@
 );%0A%0A
-            // Speed%0A
     
@@ -3255,31 +3255,8 @@
 )));
-%0A%0A            // Course
 %0A   
@@ -3357,29 +3357,8 @@
 ed%0A%0A
-            // Flags%0A
     
@@ -3435,124 +3435,189 @@
 lid(
-(flags & 0x1) == 0x1);%0A            if ((flags & 0x2) == 0) latitude = -latitude;%0A            if ((flags & 0x4) == 0)
+BitUtil.check(flags, 0));%0A            if (!BitUtil.check(flags, 1)) %7B%0A                latitude = -latitude;%0A            %7D%0A            if (!BitUtil.check(flags, 2)) %7B%0A               
  lon
@@ -3636,16 +3636,30 @@
 ngitude;
+%0A            %7D
 %0A%0A      
@@ -3734,32 +3734,33 @@
 ude(longitude);%0A
+%0A
             retu

Refactor GT02 protocol decoder