aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/traccar/protocol/GranitProtocolDecoder.java')
-rw-r--r--src/main/java/org/traccar/protocol/GranitProtocolDecoder.java239
1 files changed, 239 insertions, 0 deletions
diff --git a/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java b/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java
new file mode 100644
index 000000000..8900e5b39
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/GranitProtocolDecoder.java
@@ -0,0 +1,239 @@
+/*
+ * 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.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
+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.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class GranitProtocolDecoder extends BaseProtocolDecoder {
+
+ private static final int HEADER_LENGTH = 6;
+
+ private double adc1Ratio;
+ private double adc2Ratio;
+ private double adc3Ratio;
+ private double adc4Ratio;
+
+ public GranitProtocolDecoder(Protocol protocol) {
+ super(protocol);
+ adc1Ratio = Context.getConfig().getDouble("granit.adc1Ratio", 1);
+ adc2Ratio = Context.getConfig().getDouble("granit.adc2Ratio", 1);
+ adc3Ratio = Context.getConfig().getDouble("granit.adc3Ratio", 1);
+ adc4Ratio = Context.getConfig().getDouble("granit.adc4Ratio", 1);
+ }
+
+ public static void appendChecksum(ByteBuf buffer, int length) {
+ buffer.writeByte('*');
+ int checksum = Checksum.xor(buffer.nioBuffer(0, length)) & 0xFF;
+ String checksumString = String.format("%02X", checksum);
+ buffer.writeBytes(checksumString.getBytes(StandardCharsets.US_ASCII));
+ buffer.writeByte('\r'); buffer.writeByte('\n');
+ }
+
+ private static void sendResponseCurrent(Channel channel, int deviceId, long time) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeBytes("BB+UGRC~".getBytes(StandardCharsets.US_ASCII));
+ response.writeShortLE(6); // length
+ response.writeInt((int) time);
+ response.writeShortLE(deviceId);
+ appendChecksum(response, 16);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+
+ private static void sendResponseArchive(Channel channel, int deviceId, int packNum) {
+ ByteBuf response = Unpooled.buffer();
+ response.writeBytes("BB+ARCF~".getBytes(StandardCharsets.US_ASCII));
+ response.writeShortLE(4); // length
+ response.writeShortLE(packNum);
+ response.writeShortLE(deviceId);
+ appendChecksum(response, 14);
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+
+ private void decodeStructure(ByteBuf buf, Position position) {
+ short flags = buf.readUnsignedByte();
+ position.setValid(BitUtil.check(flags, 7));
+ if (BitUtil.check(flags, 1)) {
+ position.set(Position.KEY_ALARM, Position.ALARM_GENERAL);
+ }
+
+ short satDel = buf.readUnsignedByte();
+ position.set(Position.KEY_SATELLITES, BitUtil.from(satDel, 4));
+
+ int pdop = BitUtil.to(satDel, 4);
+ position.set(Position.KEY_PDOP, pdop);
+
+ int lonDegrees = buf.readUnsignedByte();
+ int latDegrees = buf.readUnsignedByte();
+ int lonMinutes = buf.readUnsignedShortLE();
+ int latMinutes = buf.readUnsignedShortLE();
+
+ double latitude = latDegrees + latMinutes / 60000.0;
+ double longitude = lonDegrees + lonMinutes / 60000.0;
+
+ if (position.getValid()) {
+ if (!BitUtil.check(flags, 4)) {
+ latitude = -latitude;
+ }
+ if (!BitUtil.check(flags, 5)) {
+ longitude = -longitude;
+ }
+ }
+
+ position.setLongitude(longitude);
+ position.setLatitude(latitude);
+
+ position.setSpeed(buf.readUnsignedByte());
+
+ int course = buf.readUnsignedByte();
+ if (BitUtil.check(flags, 6)) {
+ course = course | 0x100;
+ }
+ position.setCourse(course);
+
+ position.set(Position.KEY_DISTANCE, buf.readShortLE());
+
+ int analogIn1 = buf.readUnsignedByte();
+ int analogIn2 = buf.readUnsignedByte();
+ int analogIn3 = buf.readUnsignedByte();
+ int analogIn4 = buf.readUnsignedByte();
+
+ int analogInHi = buf.readUnsignedByte();
+
+ analogIn1 = analogInHi << 8 & 0x300 | analogIn1;
+ analogIn2 = analogInHi << 6 & 0x300 | analogIn2;
+ analogIn3 = analogInHi << 4 & 0x300 | analogIn3;
+ analogIn4 = analogInHi << 2 & 0x300 | analogIn4;
+
+ position.set(Position.PREFIX_ADC + 1, analogIn1 * adc1Ratio);
+ position.set(Position.PREFIX_ADC + 2, analogIn2 * adc2Ratio);
+ position.set(Position.PREFIX_ADC + 3, analogIn3 * adc3Ratio);
+ position.set(Position.PREFIX_ADC + 4, analogIn4 * adc4Ratio);
+
+ position.setAltitude(buf.readUnsignedByte() * 10);
+
+ int output = buf.readUnsignedByte();
+ for (int i = 0; i < 8; i++) {
+ position.set(Position.PREFIX_IO + (i + 1), BitUtil.check(output, i));
+ }
+ buf.readUnsignedByte(); // status message buffer
+ }
+
+ @Override
+ protected Object decode(Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+
+ int indexTilde = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) '~');
+
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress);
+
+ if (deviceSession != null && indexTilde == -1) {
+ String bufString = buf.toString(StandardCharsets.US_ASCII);
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(new Date());
+ getLastLocation(position, new Date());
+ position.setValid(false);
+ position.set(Position.KEY_RESULT, bufString);
+ return position;
+ }
+
+ if (buf.readableBytes() < HEADER_LENGTH) {
+ return null;
+ }
+ String header = buf.readSlice(HEADER_LENGTH).toString(StandardCharsets.US_ASCII);
+
+ if (header.equals("+RRCB~")) {
+
+ buf.skipBytes(2); // binary length 26
+ int deviceId = buf.readUnsignedShortLE();
+ deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId));
+ if (deviceSession == null) {
+ return null;
+ }
+ long unixTime = buf.readUnsignedIntLE();
+ if (channel != null) {
+ sendResponseCurrent(channel, deviceId, unixTime);
+ }
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ position.setTime(new Date(unixTime * 1000));
+
+ decodeStructure(buf, position);
+ return position;
+
+ } else if (header.equals("+DDAT~")) {
+
+ buf.skipBytes(2); // binary length
+ int deviceId = buf.readUnsignedShortLE();
+ deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId));
+ if (deviceSession == null) {
+ return null;
+ }
+ byte format = buf.readByte();
+ if (format != 4) {
+ return null;
+ }
+ byte nblocks = buf.readByte();
+ int packNum = buf.readUnsignedShortLE();
+ if (channel != null) {
+ sendResponseArchive(channel, deviceId, packNum);
+ }
+ List<Position> positions = new ArrayList<>();
+ while (nblocks > 0) {
+ nblocks--;
+ long unixTime = buf.readUnsignedIntLE();
+ int timeIncrement = buf.getUnsignedShortLE(buf.readerIndex() + 120);
+ for (int i = 0; i < 6; i++) {
+ if (buf.getUnsignedByte(buf.readerIndex()) != 0xFE) {
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+ position.setTime(new Date((unixTime + i * timeIncrement) * 1000));
+ decodeStructure(buf, position);
+ position.set(Position.KEY_ARCHIVE, true);
+ positions.add(position);
+ } else {
+ buf.skipBytes(20); // skip filled 0xFE structure
+ }
+ }
+ buf.skipBytes(2); // increment
+ }
+ return positions;
+
+ }
+
+ return null;
+ }
+
+}