aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar/protocol
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2022-09-18 10:47:58 -0700
committerAnton Tananaev <anton@traccar.org>2022-09-18 10:47:58 -0700
commit8532bffcec8e239c6699845e09e63da927f51d9a (patch)
tree1335861fc8a829cb5b6afb947fe4951580f2fa32 /src/main/java/org/traccar/protocol
parent9b5f0b7e273733b61db6912cb73cc08e5d866ae6 (diff)
downloadtrackermap-server-8532bffcec8e239c6699845e09e63da927f51d9a.tar.gz
trackermap-server-8532bffcec8e239c6699845e09e63da927f51d9a.tar.bz2
trackermap-server-8532bffcec8e239c6699845e09e63da927f51d9a.zip
Refactor NDTPv6 protocol
Diffstat (limited to 'src/main/java/org/traccar/protocol')
-rw-r--r--src/main/java/org/traccar/protocol/NDTPv6Protocol.java26
-rw-r--r--src/main/java/org/traccar/protocol/NDTPv6ProtocolDecoder.java309
-rw-r--r--src/main/java/org/traccar/protocol/NDTPv6ProtocolEncoder.java38
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java (renamed from src/main/java/org/traccar/protocol/NDTPv6FrameDecoder.java)64
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6Protocol.java39
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java203
-rw-r--r--src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java42
7 files changed, 316 insertions, 405 deletions
diff --git a/src/main/java/org/traccar/protocol/NDTPv6Protocol.java b/src/main/java/org/traccar/protocol/NDTPv6Protocol.java
deleted file mode 100644
index 78dc11b50..000000000
--- a/src/main/java/org/traccar/protocol/NDTPv6Protocol.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 2020 - NDTP v6 Protocol
- */
-package org.traccar.protocol;
-
-import org.traccar.BaseProtocol;
-import org.traccar.PipelineBuilder;
-import org.traccar.TrackerServer;
-import org.traccar.config.Config;
-
-import javax.inject.Inject;
-
-public class NDTPv6Protocol extends BaseProtocol {
-
- @Inject
- public NDTPv6Protocol(Config config) {
- addServer(
- new TrackerServer(config, getName(), false) {
- @Override
- protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
- pipeline.addLast(new NDTPv6ProtocolDecoder(NDTPv6Protocol.this));
- }
- }
- );
- }
-}
diff --git a/src/main/java/org/traccar/protocol/NDTPv6ProtocolDecoder.java b/src/main/java/org/traccar/protocol/NDTPv6ProtocolDecoder.java
deleted file mode 100644
index 788afd65b..000000000
--- a/src/main/java/org/traccar/protocol/NDTPv6ProtocolDecoder.java
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * 2020 - NDTP v6 Protocol Decoder
- */
-package org.traccar.protocol;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import io.netty.channel.Channel;
-
-import java.net.SocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Date;
-
-import org.traccar.BaseProtocolDecoder;
-import org.traccar.NetworkMessage;
-import org.traccar.Protocol;
-import org.traccar.helper.BitUtil;
-import org.traccar.helper.Checksum;
-import org.traccar.model.Position;
-import org.traccar.session.DeviceSession;
-
-public class NDTPv6ProtocolDecoder extends BaseProtocolDecoder {
-
- public NDTPv6ProtocolDecoder(Protocol protocol) {
- super(protocol);
- }
-
- private static final byte[] SIGNATURE = {0x7E, 0x7E};
-
- private static final int NPL_FLAG_CRC = 2;
- private static final int NPH_RESULT_OK = 0x00000000;
- private static final int NPL_TYPE_NPH = 2;
- private static final int NPL_ADDRESS_SERVER = 0;
-
- /* common packets for all services */
- private static final int NPH_RESULT = 0;
-
- /* NPH service types */
- private static final int NPH_SRV_GENERIC_CONTROLS = 0;
- private static final int NPH_SRV_NAVDATA = 1;
-
- /* NPH_SRV_GENERIC_CONTROLS packets */
- private static final int NPH_SGC_RESULT = NPH_RESULT;
- private static final int NPH_SGC_CONN_REQUEST = 100;
-
- /* NPH_SRV_NAVDATA packets */
- private static final int NPH_SND_RESULT = NPH_RESULT;
-
- private static void sendResultResponse(
- Channel channel,
- short serviceId,
- int requestId,
- int nphSendResult,
- int nphResult
- ) {
- // Формирование пакета данных
- byte[] serviceIdBytes = ByteBuffer
- .allocate(2)
- .order(ByteOrder.LITTLE_ENDIAN)
- .putShort(serviceId)
- .array();
- byte[] nphSendResultBytes = ByteBuffer
- .allocate(4)
- .order(ByteOrder.LITTLE_ENDIAN)
- .putInt(nphSendResult)
- .array();
- byte[] requestIdBytes = ByteBuffer
- .allocate(4)
- .order(ByteOrder.LITTLE_ENDIAN)
- .putInt(requestId)
- .array();
- byte[] nphResultBytes = ByteBuffer
- .allocate(4)
- .order(ByteOrder.LITTLE_ENDIAN)
- .putInt(nphResult)
- .array();
-
- byte[] allByteArray = new byte[serviceIdBytes.length +
- requestIdBytes.length +
- nphSendResultBytes.length +
- nphResultBytes.length];
-
- System.arraycopy(serviceIdBytes, 0, allByteArray, 0, serviceIdBytes.length);
- System.arraycopy(
- nphSendResultBytes,
- 0,
- allByteArray,
- serviceIdBytes.length,
- nphSendResultBytes.length
- );
- System.arraycopy(
- requestIdBytes,
- 0,
- allByteArray,
- serviceIdBytes.length + nphSendResultBytes.length,
- requestIdBytes.length
- );
- System.arraycopy(
- nphResultBytes,
- 0,
- allByteArray,
- serviceIdBytes.length + requestIdBytes.length + nphSendResultBytes.length,
- nphResultBytes.length
- );
-
- // ПАКЕТ ОТВЕТА КЛИЕНТУ
- ByteBuf response = Unpooled.buffer();
- // NPL
- response.writeBytes(SIGNATURE);
- response.writeShortLE(allByteArray.length); // Размер данных
- response.writeShortLE(NPL_FLAG_CRC); // Флаги
-
- response.writeShort(
- Checksum.crc16(Checksum.CRC16_MODBUS, ByteBuffer.wrap(allByteArray))
- ); // CRC
- response.writeByte(NPL_TYPE_NPH); // Тип
- response.writeIntLE(NPL_ADDRESS_SERVER); // peer_address
- response.writeShortLE(0); // request_id
-
- response.writeBytes(allByteArray);
-
- channel.writeAndFlush(
- new NetworkMessage(response, channel.remoteAddress())
- );
- }
-
- private static final short MAIN_NAV_DATA = 0;
- private static final short ADDITIONAL_NAV_DATA = 2;
-
- private void decodeData(ByteBuf buf, Position position, int dataType) {
- if (dataType == NPH_SRV_NAVDATA) {
- short cellType;
- short cellNumber;
-
- cellType = buf.readUnsignedByte(); // Тип ячейки
- cellNumber = buf.readUnsignedByte(); // Номер ячейки
- if (cellType == MAIN_NAV_DATA && (cellNumber == 0 || cellNumber == 1)) {
- position.setTime(new Date(buf.readUnsignedIntLE() * 1000)); // Значение реального времени unix
- position.setLongitude(buf.readIntLE() / 10000000.0); // Долгота в градусах, умноженная на 10 000 000
- position.setLatitude(buf.readIntLE() / 10000000.0); // Широта в градусах, умноженная на 10 000 000
-
- short flags = buf.readUnsignedByte(); // Достоверность навигационных данных:
- // bit7 - достоверность нав. данных (1 - достоверны, 0 – нет);
- // bit6 - полушарие долготы (1 – E, 0 – W);
- // bit5 - полушарие широты (1 – N, 0 – S);
- // bit4 - флаг работы от встроенного аккумулятора;
- // bit3 – флаг первоначального включения;
- // bit2 – состояние SOS (1 – SOS, 0 – нет SOS);
- // bit1 – флаг тревожной информации (один из параметров
- // находится в диапазоне тревоги)
- position.setValid(BitUtil.check(flags, 7));
- if (BitUtil.check(flags, 1)) {
- position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
- }
- position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 20); // Напряжение батареи, 1бит = 20мВ
- position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShortLE()); // Средняя скорость за период в км/ч
- position.setSpeed(buf.readUnsignedShortLE() / 1.85); // Максимальная скорость за период в км/ч
-
- int course = buf.readUnsignedShortLE(); // Направление движения
- position.setCourse(course);
-
- position.set(Position.KEY_DISTANCE, buf.readUnsignedShortLE()); // Пройденный путь, м
- position.setAltitude(buf.readShortLE()); // Высота над уровнем моря в метрах (-18000 - +18000)
- position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); // Количество видимых спутников
- position.set(Position.KEY_PDOP, buf.readUnsignedByte()); // PDOP
- }
- cellType = buf.readUnsignedByte(); // Тип ячейки
- cellNumber = buf.readUnsignedByte(); // Номер ячейки
- if (cellType == ADDITIONAL_NAV_DATA && cellNumber == 0) {
- int analogIn1 = buf.readUnsignedShortLE(); // Значение 0 аналогового входа в 16 битном формате
- //(analogIn1 - отражает напряжение на батерии для радиоборта)
- int analogIn2 = buf.readUnsignedShortLE(); // Значение 1 аналогового входа в 16 битном формате
- int analogIn3 = buf.readUnsignedShortLE(); // Значение 2 аналогового входа в 16 битном формате
- int analogIn4 = buf.readUnsignedShortLE(); // Значение 3 аналогового входа в 16 битном формате
-
- buf.readUnsignedByte(); // Значение цифровых входов
- buf.readUnsignedByte(); // Состояние дискретных выходов
- buf.readUnsignedShortLE(); // Количество импульсов на дискретном входе 0 с предыдущей нав. отметки
- buf.readUnsignedShortLE(); // Количество импульсов на дискретном входе 1 с предыдущей нав. отметки
- buf.readUnsignedShortLE(); // Количество импульсов на дискретном входе 2 с предыдущей нав. отметки
- buf.readUnsignedShortLE(); // Количество импульсов на дискретном входе 3 с предыдущей нав. отметки
- buf.readUnsignedIntLE(); // Длина трека с момента первого включения
-
- position.set(Position.KEY_ANTENNA, buf.readUnsignedByte()); // Сила GSM сигнала
- position.set(Position.KEY_GPS, buf.readUnsignedByte()); // Состояние GPRS подключения
- position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte()); // Акселерометр - энергия
- position.set(Position.KEY_POWER, buf.readUnsignedByte() * 200); // Напряжение борт. сети (1бит - 200мв)
-
- position.set(Position.PREFIX_ADC + 1, analogIn1 * 1);
- position.set(Position.PREFIX_ADC + 2, analogIn2 * 1);
- position.set(Position.PREFIX_ADC + 3, analogIn3 * 1);
- position.set(Position.PREFIX_ADC + 4, analogIn4 * 1);
-
- // Расчет уровня батареи
- // float Voltage = 5 / 4096 * analogIn1; // Вольтаж
-
- float batteryLevel = (analogIn1 - 3600) / 6;
-
- if (batteryLevel > 100) {
- batteryLevel = 100;
- }
- if (batteryLevel < 0) {
- batteryLevel = 0;
- }
-
- position.set(Position.KEY_BATTERY_LEVEL, batteryLevel);
- }
- }
- }
-
- @Override
- protected Object decode(
- Channel channel,
- SocketAddress remoteAddress,
- Object msg
- )
- throws Exception {
- ByteBuf buf = (ByteBuf) msg;
-
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
-
- // Заголовок NPL
- buf.skipBytes(2); // Сигнатура (7e 7e)
- buf.readUnsignedShortLE(); // Размер данных (nph + размер массива данных)
- buf.readUnsignedShortLE(); // Флаги соединения (2 - проверять crc)
- buf.readUnsignedShortLE(); // CRC (Modbus)
- buf.readUnsignedByte(); // Тип пакета (nph)
- buf.readUnsignedIntLE(); // Адрес участника соединения
- buf.readUnsignedShortLE(); // Идентификатор NPL
-
- // Заголовок NPH
- int serviceId = buf.readUnsignedShortLE(); // Идентификатор услуги (NPH_SRV_NAVDATA) service_id
- int serviceType = buf.readUnsignedShortLE(); // Тип пакета
- buf.readUnsignedShortLE(); // Флаг (1 - требуется подтверждение) NPH_FLAG_REQUEST
- long requestId = buf.readUnsignedIntLE(); // Идентификатор nph
-
- if (
- deviceSession == null &&
- serviceId == NPH_SRV_GENERIC_CONTROLS &&
- serviceType == NPH_SGC_CONN_REQUEST
- ) { // Регистрация устройства
- buf.readUnsignedShortLE(); // Версия протокола NDTP (старший номер)
- buf.readUnsignedShortLE(); // Версия протокола NDTP (младший номер)
- buf.readUnsignedShortLE(); // Опции соединения (connection_flags)
- // Определяет настройки соединения,
- // которые будут использоваться после установки соединения.
- // На данный момент их две:
- // - бит0: шифровать пакеты (0 - нет, 1 — да)
- // - бит1: рассчитывать CRC пакетов (0 - нет, 1 — да)
- // - бит2: подключение симулятора (0 — подключается обычный клиент,
- // 1 подключается симулятор)
- // - бит3: тип алгоритма шифрования.
- // - 0 - blowfish
- // - 1 – ГОСТ
- // - бит8: наличие поля IMEI (0 - нет, 1 — да)
- // - бит9: наличие поля IMSI (0 - нет, 1 — да)
- // - остальные биты не используются.
- int deviceId = buf.readUnsignedShortLE();
- Position position = new Position(getProtocolName());
- deviceSession =
- getDeviceSession(channel, remoteAddress, String.valueOf(deviceId));
- position.setDeviceId(deviceSession.getDeviceId());
-
- if (channel != null) {
- sendResultResponse(
- channel,
- (short) serviceId,
- (int) requestId,
- NPH_SND_RESULT,
- NPH_RESULT_OK
- );
- }
-
- position.set(Position.KEY_RESULT, String.valueOf(NPH_SGC_RESULT));
- position.setTime(new Date());
- getLastLocation(position, new Date());
- position.setValid(false);
-
- return position;
- }
-
- if (serviceId == NPH_SRV_NAVDATA) {
- deviceSession = getDeviceSession(channel, remoteAddress);
- if (deviceSession == null) {
- return null;
- }
-
- Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
-
- if (channel != null) {
- sendResultResponse(
- channel,
- (short) serviceId,
- (int) requestId,
- NPH_SND_RESULT,
- NPH_RESULT_OK
- );
- }
-
- decodeData(buf, position, NPH_SRV_NAVDATA);
-
- return position;
- }
-
- return null;
- }
-}
diff --git a/src/main/java/org/traccar/protocol/NDTPv6ProtocolEncoder.java b/src/main/java/org/traccar/protocol/NDTPv6ProtocolEncoder.java
deleted file mode 100644
index af0dd58f9..000000000
--- a/src/main/java/org/traccar/protocol/NDTPv6ProtocolEncoder.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 2020 - NDTP v6 Protocol Encoder
- */
-package org.traccar.protocol;
-
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.Unpooled;
-import java.nio.charset.StandardCharsets;
-import org.traccar.BaseProtocolEncoder;
-import org.traccar.Protocol;
-import org.traccar.model.Command;
-
-public class NDTPv6ProtocolEncoder extends BaseProtocolEncoder {
-
- public NDTPv6ProtocolEncoder(Protocol protocol) {
- super(protocol);
- }
-
- private ByteBuf encodeCommand(String commandString) {
- ByteBuf buffer = Unpooled.buffer();
- buffer.writeBytes(commandString.getBytes(StandardCharsets.US_ASCII));
- return buffer;
- }
-
- @Override
- protected Object encodeCommand(Command command) {
- switch (command.getType()) {
- case Command.TYPE_IDENTIFICATION:
- return encodeCommand("BB+IDNT");
- case Command.TYPE_REBOOT_DEVICE:
- return encodeCommand("BB+RESET");
- case Command.TYPE_POSITION_SINGLE:
- return encodeCommand("BB+RRCD");
- default:
- return null;
- }
- }
-}
diff --git a/src/main/java/org/traccar/protocol/NDTPv6FrameDecoder.java b/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java
index c869b11a4..5b610013a 100644
--- a/src/main/java/org/traccar/protocol/NDTPv6FrameDecoder.java
+++ b/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java
@@ -1,32 +1,32 @@
-/*
- * Copyright 2016 - 2018 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.channel.Channel;
-import io.netty.channel.ChannelHandlerContext;
-import org.traccar.BaseFrameDecoder;
-
-public class NDTPv6FrameDecoder extends BaseFrameDecoder {
-
- @Override
- protected Object decode(
- ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
-
- return buf;
- }
-
-}
+/*
+ * Copyright 2022 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.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import org.traccar.BaseFrameDecoder;
+
+public class NdtpV6FrameDecoder extends BaseFrameDecoder {
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception {
+
+ return buf;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6Protocol.java b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java
new file mode 100644
index 000000000..ce0dbbef2
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 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.handler.codec.string.StringEncoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+import org.traccar.config.Config;
+
+import javax.inject.Inject;
+
+public class NdtpV6Protocol extends BaseProtocol {
+
+ @Inject
+ public NdtpV6Protocol(Config config) {
+ addServer(new TrackerServer(config, getName(), false) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
+ pipeline.addLast(new StringEncoder());
+ pipeline.addLast(new NdtpV6ProtocolDecoder(NdtpV6Protocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java b/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java
new file mode 100644
index 000000000..35cb0bae8
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2022 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.NetworkMessage;
+import org.traccar.Protocol;
+import org.traccar.helper.BitUtil;
+import org.traccar.helper.Checksum;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+import org.traccar.session.DeviceSession;
+
+import java.net.SocketAddress;
+import java.util.Date;
+
+public class NdtpV6ProtocolDecoder extends BaseProtocolDecoder {
+
+ public NdtpV6ProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ private static final byte[] SIGNATURE = {0x7E, 0x7E};
+
+ private static final int NPL_FLAG_CRC = 2;
+ private static final int NPH_RESULT_OK = 0x00000000;
+ private static final int NPL_TYPE_NPH = 2;
+ private static final int NPL_ADDRESS_SERVER = 0;
+
+ private static final int NPH_RESULT = 0;
+
+ private static final int NPH_SRV_GENERIC_CONTROLS = 0;
+ private static final int NPH_SRV_NAVDATA = 1;
+
+ private static final int NPH_SGC_RESULT = NPH_RESULT;
+ private static final int NPH_SGC_CONN_REQUEST = 100;
+
+ private static final int NPH_SND_RESULT = NPH_RESULT;
+
+ private void sendResponse(
+ Channel channel, int serviceId, long requestId) {
+
+ ByteBuf content = Unpooled.buffer();
+ content.writeShortLE(serviceId);
+ content.writeIntLE(NPH_SND_RESULT);
+ content.writeIntLE((int) requestId);
+ content.writeIntLE(NPH_RESULT_OK);
+
+ ByteBuf response = Unpooled.buffer();
+ response.writeBytes(SIGNATURE);
+ response.writeShortLE(content.readableBytes());
+ response.writeShortLE(NPL_FLAG_CRC); // flags
+ response.writeShort(Checksum.crc16(Checksum.CRC16_MODBUS, content.nioBuffer()));
+ response.writeByte(NPL_TYPE_NPH); // type
+ response.writeIntLE(NPL_ADDRESS_SERVER); // peer address
+ response.writeShortLE(0); // request id
+ response.writeBytes(content);
+ content.release();
+
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+
+ private static final short MAIN_NAV_DATA = 0;
+ private static final short ADDITIONAL_NAV_DATA = 2;
+
+ private void decodeData(ByteBuf buf, Position position) {
+
+ short itemType;
+ short itemIndex;
+
+ itemType = buf.readUnsignedByte();
+ itemIndex = buf.readUnsignedByte();
+ if (itemType == MAIN_NAV_DATA && (itemIndex == 0 || itemIndex == 1)) {
+
+ position.setTime(new Date(buf.readUnsignedIntLE() * 1000));
+ position.setLongitude(buf.readIntLE() / 10000000.0);
+ position.setLatitude(buf.readIntLE() / 10000000.0);
+
+ short flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(flags, 7));
+ if (BitUtil.check(flags, 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+
+ position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 20);
+ position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShortLE());
+ position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE()));
+ position.setCourse(buf.readUnsignedShortLE());
+
+ position.set(Position.KEY_ODOMETER, buf.readUnsignedShortLE());
+ position.setAltitude(buf.readShortLE());
+ position.set(Position.KEY_SATELLITES, buf.readUnsignedByte());
+ position.set(Position.KEY_PDOP, buf.readUnsignedByte());
+ }
+
+ itemType = buf.readUnsignedByte();
+ itemIndex = buf.readUnsignedByte();
+ if (itemType == ADDITIONAL_NAV_DATA && itemIndex == 0) {
+
+ position.set(Position.KEY_BATTERY_LEVEL, Math.max((buf.readUnsignedShortLE() - 3600) / 6, 100));
+ position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShortLE());
+ position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShortLE());
+ position.set(Position.PREFIX_ADC + 4, buf.readUnsignedShortLE());
+
+ buf.readUnsignedByte(); // inputs
+ buf.readUnsignedByte(); // outputs
+ buf.readUnsignedShortLE(); // in1 count
+ buf.readUnsignedShortLE(); // in2 count
+ buf.readUnsignedShortLE(); // in3 count
+ buf.readUnsignedShortLE(); // in4 count
+ buf.readUnsignedIntLE(); // track length
+
+ position.set(Position.KEY_ANTENNA, buf.readUnsignedByte());
+ position.set(Position.KEY_GPS, buf.readUnsignedByte());
+ position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte());
+ position.set(Position.KEY_POWER, buf.readUnsignedByte() * 200);
+ }
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+
+ buf.skipBytes(2); // signature
+ buf.readUnsignedShortLE(); // length
+ buf.readUnsignedShortLE(); // connection flags
+ buf.readUnsignedShortLE(); // checksum
+ buf.readUnsignedByte(); // type
+ buf.readUnsignedIntLE(); // address
+ buf.readUnsignedShortLE(); // identification
+
+ int serviceId = buf.readUnsignedShortLE();
+ int serviceType = buf.readUnsignedShortLE();
+ buf.readUnsignedShortLE(); // request flags
+ long requestId = buf.readUnsignedIntLE();
+
+ if (deviceSession == null && serviceId == NPH_SRV_GENERIC_CONTROLS && serviceType == NPH_SGC_CONN_REQUEST) {
+
+ buf.readUnsignedShortLE(); // version major
+ buf.readUnsignedShortLE(); // version minor
+ buf.readUnsignedShortLE(); // connection flags
+
+ int deviceId = buf.readUnsignedShortLE();
+ Position position = new Position(getProtocolName());
+ deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId));
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (channel != null) {
+ sendResponse(channel, serviceId, requestId);
+ }
+
+ position.set(Position.KEY_RESULT, String.valueOf(NPH_SGC_RESULT));
+ position.setTime(new Date());
+ getLastLocation(position, new Date());
+ position.setValid(false);
+
+ return position;
+
+ }
+
+ if (serviceId == NPH_SRV_NAVDATA) {
+
+ deviceSession = getDeviceSession(channel, remoteAddress);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ if (channel != null) {
+ sendResponse(channel, serviceId, requestId);
+ }
+
+ decodeData(buf, position);
+
+ return position;
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java b/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java
new file mode 100644
index 000000000..7aac8658a
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 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 org.traccar.BaseProtocolEncoder;
+import org.traccar.Protocol;
+import org.traccar.model.Command;
+
+public class NdtpV6ProtocolEncoder extends BaseProtocolEncoder {
+
+ public NdtpV6ProtocolEncoder(Protocol protocol) {
+ super(protocol);
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+ switch (command.getType()) {
+ case Command.TYPE_IDENTIFICATION:
+ return "BB+IDNT";
+ case Command.TYPE_REBOOT_DEVICE:
+ return "BB+RESET";
+ case Command.TYPE_POSITION_SINGLE:
+ return "BB+RRCD";
+ default:
+ return null;
+ }
+ }
+
+}