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

    HttpRequest request = (HttpRequest) msg;
    QueryStringDecoder decoder = new QueryStringDecoder(request.getUri());

    DeviceSession deviceSession =
        getDeviceSession(channel, remoteAddress, decoder.getParameters().get("UserName").get(0));
    if (deviceSession == null) {
      return null;
    }

    Parser parser = new Parser(PATTERN, decoder.getParameters().get("LOC").get(0));
    if (!parser.matches()) {
      return null;
    }

    Position position = new Position();
    position.setProtocol(getProtocolName());
    position.setDeviceId(deviceSession.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(true);
    position.setLatitude(parser.nextDouble());
    position.setLongitude(parser.nextDouble());
    position.setAltitude(parser.nextDouble());
    position.setSpeed(parser.nextDouble());
    position.setCourse(parser.nextDouble());

    if (channel != null) {
      HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
      channel.write(response).addListener(ChannelFutureListener.CLOSE);
    }

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

    ChannelBuffer buf = (ChannelBuffer) msg;

    buf.skipBytes(2); // header
    ChannelBuffer id = buf.readBytes(12);
    buf.readUnsignedByte(); // separator
    int type = buf.readUnsignedByte();
    buf.readUnsignedShort(); // length

    if (type == MSG_LOGIN) {

      int command = buf.readUnsignedByte(); // 0x00 - heartbeat

      if (command == 0x01) {
        String imei = buf.toString(buf.readerIndex(), 15, StandardCharsets.US_ASCII);
        DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
        if (deviceSession != null && channel != null) {
          ChannelBuffer response = ChannelBuffers.dynamicBuffer();
          response.writeByte(0x48);
          response.writeByte(0x52); // header
          response.writeBytes(id);
          response.writeByte(0x2c); // separator
          response.writeByte(type);
          response.writeShort(0x0002); // length
          response.writeShort(0x8000);
          response.writeShort(0x0000); // checksum
          channel.write(response);
        }
      }

    } else if (type == MSG_GPS) {

      DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
      if (deviceSession == null) {
        return null;
      }

      Position position = new Position();
      position.setProtocol(getProtocolName());
      position.setDeviceId(deviceSession.getDeviceId());

      position.setTime(new Date(buf.readUnsignedInt() * 1000));

      int flags = buf.readUnsignedByte();

      position.setValid(true);
      position.setLatitude(convertCoordinate(buf.readUnsignedInt(), !BitUtil.check(flags, 0)));
      position.setLongitude(convertCoordinate(buf.readUnsignedInt(), !BitUtil.check(flags, 1)));

      position.setSpeed(buf.readUnsignedByte());
      position.setCourse(buf.readUnsignedByte());

      position.set(Position.KEY_LAC, buf.readUnsignedShort());
      position.set(Position.KEY_CID, buf.readUnsignedShort());

      return position;
    }

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

    ChannelBuffer buf = (ChannelBuffer) msg;

    buf.readUnsignedByte(); // header
    int length = (buf.readUnsignedShort() & 0x7fff) + 3;

    List<Position> positions = new LinkedList<>();
    Set<Integer> tags = new HashSet<>();
    boolean hasLocation = false;
    Position position = new Position();
    position.setProtocol(getProtocolName());

    while (buf.readerIndex() < length) {

      // Check if new message started
      int tag = buf.readUnsignedByte();
      if (tags.contains(tag)) {
        if (hasLocation && position.getFixTime() != null) {
          positions.add(position);
        }
        tags.clear();
        hasLocation = false;
        position = new Position();
      }
      tags.add(tag);

      switch (tag) {
        case TAG_IMEI:
          getDeviceSession(
              channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII));
          break;

        case TAG_DATE:
          position.setTime(new Date(buf.readUnsignedInt() * 1000));
          break;

        case TAG_COORDINATES:
          hasLocation = true;
          position.setValid((buf.readUnsignedByte() & 0xf0) == 0x00);
          position.setLatitude(buf.readInt() / 1000000.0);
          position.setLongitude(buf.readInt() / 1000000.0);
          break;

        case TAG_SPEED_COURSE:
          position.setSpeed(buf.readUnsignedShort() * 0.0539957);
          position.setCourse(buf.readUnsignedShort() * 0.1);
          break;

        case TAG_ALTITUDE:
          position.setAltitude(buf.readShort());
          break;

        case TAG_STATUS:
          int status = buf.readUnsignedShort();
          position.set(Position.KEY_IGNITION, BitUtil.check(status, 9));
          position.set(Position.KEY_ALARM, BitUtil.check(status, 15));
          position.set(Position.KEY_POWER, BitUtil.check(status, 2));
          break;

        case TAG_DIGITAL_INPUTS:
          int input = buf.readUnsignedShort();
          for (int i = 0; i < 16; i++) {
            position.set(Position.PREFIX_IO + (i + 1), BitUtil.check(input, i));
          }
          break;

        case TAG_DIGITAL_OUTPUTS:
          int output = buf.readUnsignedShort();
          for (int i = 0; i < 16; i++) {
            position.set(Position.PREFIX_IO + (i + 17), BitUtil.check(output, i));
          }
          break;

        case TAG_INPUT_VOLTAGE1:
          position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShort() / 1000.0);
          break;

        case TAG_INPUT_VOLTAGE2:
          position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShort() / 1000.0);
          break;

        case TAG_INPUT_VOLTAGE3:
          position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShort() / 1000.0);
          break;

        case TAG_INPUT_VOLTAGE4:
          position.set(Position.PREFIX_ADC + 4, buf.readUnsignedShort() / 1000.0);
          break;

        case TAG_XT1:
        case TAG_XT2:
        case TAG_XT3:
          buf.skipBytes(16);
          break;

        default:
          break;
      }
    }

    if (hasLocation && position.getFixTime() != null) {
      positions.add(position);
    }

    DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
    if (deviceSession == null) {
      return null;
    }

    sendReply(channel, buf.readUnsignedShort());

    for (Position p : positions) {
      p.setDeviceId(deviceSession.getDeviceId());
    }

    if (positions.isEmpty()) {
      return null;
    }

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

    ChannelBuffer buf = (ChannelBuffer) msg;

    buf.readUnsignedShort(); // device id
    buf.readUnsignedByte(); // length

    int type = buf.readUnsignedByte();

    DeviceSession deviceSession;
    if (type == MSG_IMEI) {
      deviceSession =
          getDeviceSession(
              channel, remoteAddress, buf.readBytes(15).toString(StandardCharsets.US_ASCII));
    } else {
      deviceSession = getDeviceSession(channel, remoteAddress);
    }

    if (deviceSession == null) {
      return null;
    }

    if (BitUtil.to(type, 2) == 0) {

      Position position = new Position();
      position.setProtocol(getProtocolName());
      position.setDeviceId(deviceSession.getDeviceId());

      buf.readUnsignedByte(); // firmware version
      buf.readUnsignedShort(); // index

      position.set(Position.KEY_STATUS, buf.readUnsignedShort());

      position.setValid(true);
      position.setLatitude(buf.readFloat());
      position.setLongitude(buf.readFloat());
      position.setCourse(buf.readUnsignedShort() * 0.1);
      position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShort() * 0.1));

      buf.readUnsignedByte(); // acceleration

      position.setAltitude(buf.readUnsignedShort());

      position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1);
      position.set(Position.KEY_SATELLITES, buf.readUnsignedByte() & 0x0f);

      position.setTime(new Date(buf.readUnsignedInt() * 1000));

      position.set(Position.KEY_POWER, buf.readUnsignedShort());
      position.set(Position.KEY_BATTERY, buf.readUnsignedShort());

      if (BitUtil.check(type, 2)) {
        buf.skipBytes(4);
      }

      if (BitUtil.check(type, 3)) {
        buf.skipBytes(12);
      }

      if (BitUtil.check(type, 4)) {
        buf.skipBytes(8);
      }

      if (BitUtil.check(type, 5)) {
        buf.skipBytes(9);
      }

      if (BitUtil.check(type, 6)) {
        buf.skipBytes(buf.getUnsignedByte(buf.readerIndex()));
      }

      if (BitUtil.check(type, 7)) {
        position.set(Position.KEY_ODOMETER, buf.readUnsignedInt());
      }

      return position;
    }

    return null;
  }