diff options
Diffstat (limited to 'src/main/java/org/traccar/protocol/NavisProtocolDecoder.java')
-rw-r--r-- | src/main/java/org/traccar/protocol/NavisProtocolDecoder.java | 683 |
1 files changed, 683 insertions, 0 deletions
diff --git a/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java new file mode 100644 index 000000000..7ba474ae0 --- /dev/null +++ b/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java @@ -0,0 +1,683 @@ +/* + * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.DeviceSession; +import org.traccar.NetworkMessage; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.Checksum; +import org.traccar.helper.Checksum.Algorithm; +import org.traccar.helper.DateBuilder; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; + +import java.net.SocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; +import java.util.Date; + +public class NavisProtocolDecoder extends BaseProtocolDecoder { + + private static final int[] FLEX_FIELDS_SIZES = { + 4, 2, 4, 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 2, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 4, 4, 2, 2, + 4, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 2, 1, 4, 2, 2, 2, 2, 2, 1, 1, 1, 2, 4, 2, 1, + /* FLEX 2.0 */ + 8, 2, 1, 16, 4, 2, 4, 37, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 12, 24, 48, 1, 1, 1, 1, 4, 4, + 1, 4, 2, 6, 2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1 + }; + + private String prefix; + private long deviceUniqueId, serverId; + private int flexDataSize; + private int flexBitFieldSize; + private final byte[] flexBitField = new byte[16]; + + public NavisProtocolDecoder(Protocol protocol) { + super(protocol); + } + + public static final int F10 = 0x01; + public static final int F20 = 0x02; + public static final int F30 = 0x03; + public static final int F40 = 0x04; + public static final int F50 = 0x05; + public static final int F51 = 0x15; + public static final int F52 = 0x25; + public static final int F60 = 0x06; + + public int getFlexDataSize() { + return flexDataSize; + } + + private static boolean isFormat(int type, int... types) { + for (int i : types) { + if (type == i) { + return true; + } + } + return false; + } + + private Position parseNtcbPosition(DeviceSession deviceSession, ByteBuf buf) { + Position position = new Position(getProtocolName()); + + position.setDeviceId(deviceSession.getDeviceId()); + + int format; + if (buf.getUnsignedByte(buf.readerIndex()) == 0) { + format = buf.readUnsignedShortLE(); + } else { + format = buf.readUnsignedByte(); + } + position.set("format", format); + + position.set(Position.KEY_INDEX, buf.readUnsignedIntLE()); + position.set(Position.KEY_EVENT, buf.readUnsignedShortLE()); + + buf.skipBytes(6); // event time + + short armedStatus = buf.readUnsignedByte(); + if (isFormat(format, F10, F20, F30, F40, F50, F51, F52)) { + position.set(Position.KEY_ARMED, BitUtil.to(armedStatus, 7)); + if (BitUtil.check(armedStatus, 7)) { + position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); + } + } else if (isFormat(format, F60)) { + position.set(Position.KEY_ARMED, BitUtil.check(armedStatus, 0)); + if (BitUtil.check(armedStatus, 1)) { + position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); + } + } + + position.set(Position.KEY_STATUS, buf.readUnsignedByte()); + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + + if (isFormat(format, F10, F20, F30)) { + int output = buf.readUnsignedShortLE(); + position.set(Position.KEY_OUTPUT, output); + for (int i = 0; i < 16; i++) { + position.set(Position.PREFIX_OUT + (i + 1), BitUtil.check(output, i)); + } + } else if (isFormat(format, F50, F51, F52)) { + short extField = buf.readUnsignedByte(); + position.set(Position.KEY_OUTPUT, BitUtil.to(extField, 2)); + position.set(Position.PREFIX_OUT + 1, BitUtil.check(extField, 0)); + position.set(Position.PREFIX_OUT + 2, BitUtil.check(extField, 1)); + position.set(Position.KEY_SATELLITES, BitUtil.from(extField, 2)); + } else if (isFormat(format, F40, F60)) { + short output = buf.readUnsignedByte(); + position.set(Position.KEY_OUTPUT, BitUtil.to(output, 4)); + for (int i = 0; i < 4; i++) { + position.set(Position.PREFIX_OUT + (i + 1), BitUtil.check(output, i)); + } + } + + if (isFormat(format, F10, F20, F30, F40)) { + int input = buf.readUnsignedShortLE(); + position.set(Position.KEY_INPUT, input); + if (!isFormat(format, F40)) { + for (int i = 0; i < 16; i++) { + position.set(Position.PREFIX_IN + (i + 1), BitUtil.check(input, i)); + } + } else { + position.set(Position.PREFIX_IN + 1, BitUtil.check(input, 0)); + position.set(Position.PREFIX_IN + 2, BitUtil.check(input, 1)); + position.set(Position.PREFIX_IN + 3, BitUtil.check(input, 2)); + position.set(Position.PREFIX_IN + 4, BitUtil.check(input, 3)); + position.set(Position.PREFIX_IN + 5, BitUtil.between(input, 4, 7)); + position.set(Position.PREFIX_IN + 6, BitUtil.between(input, 7, 10)); + position.set(Position.PREFIX_IN + 7, BitUtil.between(input, 10, 12)); + position.set(Position.PREFIX_IN + 8, BitUtil.between(input, 12, 14)); + } + } else if (isFormat(format, F50, F51, F52, F60)) { + short input = buf.readUnsignedByte(); + position.set(Position.KEY_INPUT, input); + for (int i = 0; i < 8; i++) { + position.set(Position.PREFIX_IN + (i + 1), BitUtil.check(input, i)); + } + } + + position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.001); + position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001); + + if (isFormat(format, F10, F20, F30)) { + position.set(Position.PREFIX_TEMP + 1, buf.readShortLE()); + } + + if (isFormat(format, F10, F20, F50, F51, F52, F60)) { + position.set(Position.PREFIX_ADC + 1, buf.readUnsignedShortLE()); + position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShortLE()); + } + if (isFormat(format, F60)) { + position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShortLE()); + } + + // Impulse counters + if (isFormat(format, F20, F50, F51, F52, F60)) { + buf.readUnsignedIntLE(); + buf.readUnsignedIntLE(); + } + + if (isFormat(format, F60)) { + // Fuel + buf.readUnsignedShortLE(); + buf.readUnsignedShortLE(); + buf.readByte(); + buf.readShortLE(); + buf.readByte(); + buf.readUnsignedShortLE(); + buf.readByte(); + buf.readUnsignedShortLE(); + buf.readByte(); + buf.readUnsignedShortLE(); + buf.readByte(); + buf.readUnsignedShortLE(); + buf.readByte(); + buf.readUnsignedShortLE(); + buf.readByte(); + buf.readUnsignedShortLE(); + buf.readByte(); + buf.readUnsignedShortLE(); + + position.set(Position.PREFIX_TEMP + 1, buf.readByte()); + position.set(Position.PREFIX_TEMP + 2, buf.readByte()); + position.set(Position.PREFIX_TEMP + 3, buf.readByte()); + position.set(Position.PREFIX_TEMP + 4, buf.readByte()); + position.set(Position.KEY_AXLE_WEIGHT, buf.readIntLE()); + position.set(Position.KEY_RPM, buf.readUnsignedShortLE()); + } + + if (isFormat(format, F20, F50, F51, F52, F60)) { + int navSensorState = buf.readUnsignedByte(); + position.setValid(BitUtil.check(navSensorState, 1)); + if (isFormat(format, F60)) { + position.set(Position.KEY_SATELLITES, BitUtil.from(navSensorState, 2)); + } + + DateBuilder dateBuilder = new DateBuilder() + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setDateReverse(buf.readUnsignedByte(), buf.readUnsignedByte() + 1, buf.readUnsignedByte()); + position.setTime(dateBuilder.getDate()); + + if (isFormat(format, F60)) { + position.setLatitude(buf.readIntLE() / 600000.0); + position.setLongitude(buf.readIntLE() / 600000.0); + position.setAltitude(buf.readIntLE() * 0.1); + } else { + position.setLatitude(buf.readFloatLE() / Math.PI * 180); + position.setLongitude(buf.readFloatLE() / Math.PI * 180); + } + + position.setSpeed(UnitsConverter.knotsFromKph(buf.readFloatLE())); + position.setCourse(buf.readUnsignedShortLE()); + + position.set(Position.KEY_ODOMETER, buf.readFloatLE() * 1000); + position.set(Position.KEY_DISTANCE, buf.readFloatLE() * 1000); + + // Segment times + buf.readUnsignedShortLE(); + buf.readUnsignedShortLE(); + } + + // Other + if (isFormat(format, F51, F52)) { + buf.readUnsignedShortLE(); + buf.readByte(); + buf.readUnsignedShortLE(); + buf.readUnsignedShortLE(); + buf.readByte(); + buf.readUnsignedShortLE(); + buf.readUnsignedShortLE(); + buf.readByte(); + buf.readUnsignedShortLE(); + } + + // Four temperature sensors + if (isFormat(format, F40, F52)) { + position.set(Position.PREFIX_TEMP + 1, buf.readByte()); + position.set(Position.PREFIX_TEMP + 2, buf.readByte()); + position.set(Position.PREFIX_TEMP + 3, buf.readByte()); + position.set(Position.PREFIX_TEMP + 4, buf.readByte()); + } + + return position; + } + + private Object processNtcbSingle(DeviceSession deviceSession, Channel channel, ByteBuf buf) { + Position position = parseNtcbPosition(deviceSession, buf); + + ByteBuf response = Unpooled.buffer(7); + response.writeCharSequence("*<T", StandardCharsets.US_ASCII); + response.writeIntLE((int) position.getLong(Position.KEY_INDEX)); + sendNtcbReply(channel, response); + + return position.getFixTime() != null ? position : null; + } + + private Object processNtcbArray(DeviceSession deviceSession, Channel channel, ByteBuf buf) { + List<Position> positions = new LinkedList<>(); + int count = buf.readUnsignedByte(); + + for (int i = 0; i < count; i++) { + Position position = parseNtcbPosition(deviceSession, buf); + if (position.getFixTime() != null) { + positions.add(position); + } + } + + ByteBuf response = Unpooled.buffer(7); + response.writeCharSequence("*<A", StandardCharsets.US_ASCII); + response.writeByte(count); + sendNtcbReply(channel, response); + + if (positions.isEmpty()) { + return null; + } + + return positions; + } + + private boolean checkFlexBitfield(int index) { + int byteIndex = Math.floorDiv(index, 8); + int bitIndex = Math.floorMod(index, 8); + return BitUtil.check(flexBitField[byteIndex], 7 - bitIndex); + } + + private Position parseFlexPosition(DeviceSession deviceSession, ByteBuf buf) { + + Position position = new Position(getProtocolName()); + + position.setDeviceId(deviceSession.getDeviceId()); + + int status = 0; + short input = 0; + short output = 0; + + for (int i = 0; i < flexBitFieldSize; i++) { + if (!checkFlexBitfield(i)) { + continue; + } + + switch (i) { + case 0: + position.set(Position.KEY_INDEX, buf.readUnsignedIntLE()); + break; + case 1: + position.set(Position.KEY_EVENT, buf.readUnsignedShortLE()); + break; + case 3: + short armedStatus = buf.readUnsignedByte(); + position.set(Position.KEY_ARMED, BitUtil.check(armedStatus, 0)); + if (BitUtil.check(armedStatus, 1)) { + position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); + } + break; + case 4: + status = buf.readUnsignedByte(); + position.set(Position.KEY_STATUS, status); + break; + case 5: + int status2 = buf.readUnsignedByte(); + position.set(Position.KEY_STATUS, (short) (BitUtil.to(status, 8) | (status2 << 8))); + break; + case 6: + position.set(Position.KEY_RSSI, buf.readUnsignedByte()); + break; + case 7: + int navSensorState = buf.readUnsignedByte(); + position.setValid(BitUtil.check(navSensorState, 1)); + position.set(Position.KEY_SATELLITES, BitUtil.from(navSensorState, 2)); + break; + case 8: + position.setTime(new DateBuilder(new Date(buf.readUnsignedIntLE() * 1000)).getDate()); + break; + case 9: + position.setLatitude(buf.readIntLE() / 600000.0); + break; + case 10: + position.setLongitude(buf.readIntLE() / 600000.0); + break; + case 11: + position.setAltitude(buf.readIntLE() * 0.1); + break; + case 12: + position.setSpeed(UnitsConverter.knotsFromKph(buf.readFloatLE())); + break; + case 13: + position.setCourse(buf.readUnsignedShortLE()); + break; + case 14: + position.set(Position.KEY_ODOMETER, buf.readFloatLE() * 1000); + break; + case 15: + position.set(Position.KEY_DISTANCE, buf.readFloatLE() * 1000); + break; + case 18: + position.set(Position.KEY_POWER, buf.readUnsignedShortLE() * 0.001); + break; + case 19: + position.set(Position.KEY_BATTERY, buf.readUnsignedShortLE() * 0.001); + break; + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + position.set(Position.PREFIX_ADC + (i - 19), buf.readUnsignedShortLE()); + break; + case 28: + input = buf.readUnsignedByte(); + position.set(Position.KEY_INPUT, input); + for (int k = 0; k < 8; k++) { + position.set(Position.PREFIX_IN + (k + 1), BitUtil.check(input, k)); + } + break; + case 29: + short input2 = buf.readUnsignedByte(); + position.set(Position.KEY_INPUT, (short) (BitUtil.to(input, 8) | (input2 << 8))); + for (int k = 0; k < 8; k++) { + position.set(Position.PREFIX_IN + (k + 9), BitUtil.check(input2, k)); + } + break; + case 30: + output = buf.readUnsignedByte(); + position.set(Position.KEY_OUTPUT, output); + for (int k = 0; k < 8; k++) { + position.set(Position.PREFIX_OUT + (k + 1), BitUtil.check(output, k)); + } + break; + case 31: + short output2 = buf.readUnsignedByte(); + position.set(Position.KEY_OUTPUT, (short) (BitUtil.to(output, 8) | (output2 << 8))); + for (int k = 0; k < 8; k++) { + position.set(Position.PREFIX_OUT + (k + 9), BitUtil.check(output2, k)); + } + break; + case 36: + position.set(Position.KEY_HOURS, buf.readUnsignedIntLE() * 1000); + break; + case 44: + case 45: + case 46: + case 47: + case 48: + case 49: + case 50: + case 51: + position.set(Position.PREFIX_TEMP + (i - 43), buf.readByte()); + break; + case 68: + position.set("can-speed", buf.readUnsignedByte()); + break; + // FLEX 2.0 + case 69: + int satVisible = 0; + for (int k = 0; k < 8; k++) { + satVisible += buf.readUnsignedByte(); + } + position.set(Position.KEY_SATELLITES_VISIBLE, satVisible); + break; + case 70: + position.set(Position.KEY_HDOP, buf.readUnsignedByte() * 0.1); + position.set(Position.KEY_PDOP, buf.readUnsignedByte() * 0.1); + break; + default: + if (i < FLEX_FIELDS_SIZES.length) { + buf.skipBytes(FLEX_FIELDS_SIZES[i]); + } + break; + } + } + + return position; + } + + private Position parseFlex20Position(DeviceSession deviceSession, ByteBuf buf) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + int length = buf.readUnsignedShort(); + if (length <= buf.readableBytes() && buf.readUnsignedByte() == 0x0A) { + + buf.readUnsignedByte(); // length of static part + + position.set(Position.KEY_INDEX, buf.readUnsignedIntLE()); + + position.set(Position.KEY_EVENT, buf.readUnsignedShortLE()); + buf.readUnsignedInt(); // event time + + int navSensorState = buf.readUnsignedByte(); + position.setValid(BitUtil.check(navSensorState, 1)); + position.set(Position.KEY_SATELLITES, BitUtil.from(navSensorState, 2)); + + position.setTime(new DateBuilder(new Date(buf.readUnsignedIntLE() * 1000)).getDate()); + position.setLatitude(buf.readIntLE() / 600000.0); + position.setLongitude(buf.readIntLE() / 600000.0); + position.setAltitude(buf.readIntLE() * 0.1); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readFloatLE())); + position.setCourse(buf.readUnsignedShortLE()); + position.set(Position.KEY_ODOMETER, buf.readFloatLE() * 1000); + + buf.skipBytes(length - buf.readerIndex() - 1); // skip unused part + } + + return position; + } + + private interface FlexPositionParser { + Position parsePosition(DeviceSession deviceSession, ByteBuf buf); + } + + private Object processFlexSingle( + FlexPositionParser parser, String flexHeader, DeviceSession deviceSession, Channel channel, ByteBuf buf) { + + if (!flexHeader.equals("~C")) { + buf.readUnsignedInt(); // event index + } + + Position position = parser.parsePosition(deviceSession, buf); + + ByteBuf response = Unpooled.buffer(); + response.writeCharSequence(flexHeader, StandardCharsets.US_ASCII); + response.writeIntLE((int) position.getLong(Position.KEY_INDEX)); + sendFlexReply(channel, response); + + return position.getFixTime() != null ? position : null; + } + + private Object processFlexArray( + FlexPositionParser parser, String flexHeader, DeviceSession deviceSession, Channel channel, ByteBuf buf) { + + List<Position> positions = new LinkedList<>(); + int count = buf.readUnsignedByte(); + + for (int i = 0; i < count; i++) { + Position position = parser.parsePosition(deviceSession, buf); + if (position.getFixTime() != null) { + positions.add(position); + } + } + + ByteBuf response = Unpooled.buffer(); + response.writeCharSequence(flexHeader, StandardCharsets.US_ASCII); + response.writeByte(count); + sendFlexReply(channel, response); + + return !positions.isEmpty() ? positions : null; + } + + private Object processFlexNegotiation(Channel channel, ByteBuf buf) { + if ((byte) buf.readUnsignedByte() != (byte) 0xB0) { + return null; + } + + short flexProtocolVersion = buf.readUnsignedByte(); + short flexStructVersion = buf.readUnsignedByte(); + if ((flexProtocolVersion == 0x0A || flexProtocolVersion == 0x14) + && (flexStructVersion == 0x0A || flexStructVersion == 0x14)) { + + flexBitFieldSize = buf.readUnsignedByte(); + if (flexBitFieldSize > 122) { + return null; + } + + buf.readBytes(flexBitField, 0, (int) Math.ceil((double) flexBitFieldSize / 8)); + + flexDataSize = 0; + for (int i = 0; i < flexBitFieldSize; i++) { + if (checkFlexBitfield(i)) { + flexDataSize += FLEX_FIELDS_SIZES[i]; + } + } + } else { + flexProtocolVersion = 0x14; + flexStructVersion = 0x14; + } + + ByteBuf response = Unpooled.buffer(9); + response.writeCharSequence("*<FLEX", StandardCharsets.US_ASCII); + response.writeByte(0xB0); + response.writeByte(flexProtocolVersion); + response.writeByte(flexStructVersion); + sendNtcbReply(channel, response); + + return null; + } + + private Object processHandshake(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { + buf.readByte(); // colon + if (getDeviceSession(channel, remoteAddress, buf.toString(StandardCharsets.US_ASCII)) != null) { + sendNtcbReply(channel, Unpooled.copiedBuffer("*<S", StandardCharsets.US_ASCII)); + } + return null; + } + + private void sendNtcbReply(Channel channel, ByteBuf data) { + if (channel != null) { + ByteBuf header = Unpooled.buffer(16); + header.writeCharSequence(prefix, StandardCharsets.US_ASCII); + header.writeIntLE((int) deviceUniqueId); + header.writeIntLE((int) serverId); + header.writeShortLE(data.readableBytes()); + header.writeByte(Checksum.xor(data.nioBuffer())); + header.writeByte(Checksum.xor(header.nioBuffer())); + + channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(header, data), channel.remoteAddress())); + } + } + + private void sendFlexReply(Channel channel, ByteBuf data) { + if (channel != null) { + ByteBuf cs = Unpooled.buffer(1); + cs.writeByte(Checksum.crc8(new Algorithm(8, 0x31, 0xFF, false, false, 0x00), data.nioBuffer())); + + channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(data, cs), channel.remoteAddress())); + } + } + + private Object decodeNtcb(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { + + prefix = buf.toString(buf.readerIndex(), 4, StandardCharsets.US_ASCII); + buf.skipBytes(prefix.length()); // prefix @NTC by default + serverId = buf.readUnsignedIntLE(); + deviceUniqueId = buf.readUnsignedIntLE(); + int length = buf.readUnsignedShortLE(); + buf.skipBytes(2); // header and data XOR checksum + + if (length == 0) { + return null; // keep alive message + } + + String type = buf.toString(buf.readerIndex(), 3, StandardCharsets.US_ASCII); + buf.skipBytes(type.length()); + + if (type.equals("*>S")) { + return processHandshake(channel, remoteAddress, buf); + } else { + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession != null) { + switch (type) { + case "*>A": + return processNtcbArray(deviceSession, channel, buf); + case "*>T": + return processNtcbSingle(deviceSession, channel, buf); + case "*>F": + buf.skipBytes(3); + return processFlexNegotiation(channel, buf); + default: + break; + } + } + } + + return null; + } + + private Object decodeFlex(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { + + if (buf.getByte(buf.readerIndex()) == 0x7F) { + return null; // keep alive + } + + String type = buf.toString(buf.readerIndex(), 2, StandardCharsets.US_ASCII); + buf.skipBytes(type.length()); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession != null) { + switch (type) { + // FLEX 1.0 + case "~A": + return processFlexArray(this::parseFlexPosition, type, deviceSession, channel, buf); + case "~T": + case "~C": + return processFlexSingle(this::parseFlexPosition, type, deviceSession, channel, buf); + // FLEX 2.0 (extra packages) + case "~E": + return processFlexArray(this::parseFlex20Position, type, deviceSession, channel, buf); + case "~X": + return processFlexSingle(this::parseFlex20Position, type, deviceSession, channel, buf); + default: + break; + } + } + + return null; + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + if (flexDataSize > 0) { + return decodeFlex(channel, remoteAddress, buf); + } else { + return decodeNtcb(channel, remoteAddress, buf); + } + } + +} |