From 3d2a3ee8ccf6f0989a149eec74b3d69d42963756 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sun, 10 Feb 2019 13:49:44 -0800 Subject: Support camera for Teltonika --- .../traccar/protocol/TeltonikaProtocolDecoder.java | 85 +++++++++++++++++++--- 1 file changed, 76 insertions(+), 9 deletions(-) (limited to 'src/org') diff --git a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java index f69059104..0e1fd6280 100644 --- a/src/org/traccar/protocol/TeltonikaProtocolDecoder.java +++ b/src/org/traccar/protocol/TeltonikaProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2013 - 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. @@ -25,6 +25,7 @@ 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.UnitsConverter; import org.traccar.model.CellTower; import org.traccar.model.Network; @@ -33,13 +34,18 @@ import org.traccar.model.Position; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; import java.util.Date; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { + private static final int IMAGE_PACKET_MAX = 2048; + private boolean connectionless; private boolean extended; + private Map photos = new HashMap<>(); public void setExtended(boolean extended) { this.extended = extended; @@ -51,7 +57,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { this.extended = Context.getConfig().getBoolean(getProtocolName() + ".extended"); } - private DeviceSession parseIdentification(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { + private void parseIdentification(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { int length = buf.readUnsignedShort(); String imei = buf.toString(buf.readerIndex(), length, StandardCharsets.US_ASCII); @@ -66,7 +72,6 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } - return deviceSession; } public static final int CODEC_GH3000 = 0x07; @@ -75,14 +80,74 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { public static final int CODEC_12 = 0x0C; public static final int CODEC_16 = 0x10; - private void decodeSerial(Position position, ByteBuf buf) { + private void sendImageRequest(Channel channel, SocketAddress remoteAddress, long id, int offset, int size) { + if (channel != null) { + ByteBuf response = Unpooled.buffer(); + response.writeInt(0); + response.writeShort(0); + response.writeShort(19); // length + response.writeByte(CODEC_12); + response.writeByte(1); // nod + response.writeByte(0x0D); // camera + response.writeInt(11); // payload length + response.writeByte(2); // command + response.writeInt((int) id); + response.writeInt(offset); + response.writeShort(size); + response.writeByte(1); // nod + response.writeShort(0); + response.writeShort(Checksum.crc16( + Checksum.CRC16_IBM, response.nioBuffer(8, response.readableBytes() - 8))); + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); + } + } + + private void decodeSerial(Channel channel, SocketAddress remoteAddress, Position position, ByteBuf buf) { getLastLocation(position, null); - position.set(Position.KEY_TYPE, buf.readUnsignedByte()); + int type = buf.readUnsignedByte(); + if (type == 0x0D) { + + buf.readInt(); // length + int subtype = buf.readUnsignedByte(); + if (subtype == 0x01) { + + long photoId = buf.readUnsignedInt(); + ByteBuf photo = Unpooled.buffer(buf.readInt()); + photos.put(photoId, photo); + sendImageRequest( + channel, remoteAddress, photoId, + 0, Math.min(IMAGE_PACKET_MAX, photo.capacity())); + + } else if (subtype == 0x02) { + + long photoId = buf.readUnsignedInt(); + buf.readInt(); // offset + ByteBuf photo = photos.get(photoId); + photo.writeBytes(buf, buf.readUnsignedShort()); + if (photo.writableBytes() > 0) { + sendImageRequest( + channel, remoteAddress, photoId, + photo.writerIndex(), Math.min(IMAGE_PACKET_MAX, photo.writableBytes())); + } else { + String uniqueId = Context.getIdentityManager().getById(position.getDeviceId()).getUniqueId(); + photos.remove(photoId); + try { + position.set(Position.KEY_IMAGE, Context.getMediaManager().writeFile(uniqueId, photo, "jpg")); + } finally { + photo.release(); + } + } + + } - position.set(Position.KEY_RESULT, buf.readSlice(buf.readInt()).toString(StandardCharsets.US_ASCII)); + } else { + position.set(Position.KEY_TYPE, type); + position.set(Position.KEY_RESULT, buf.readSlice(buf.readInt()).toString(StandardCharsets.US_ASCII)); + + } } private long readValue(ByteBuf buf, int length, boolean signed) { @@ -462,12 +527,14 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { position.setValid(true); if (codec == CODEC_12) { - decodeSerial(position, buf); + decodeSerial(channel, remoteAddress, position, buf); } else { decodeLocation(position, buf, codec); } - positions.add(position); + if (!position.getOutdated() || !position.getAttributes().isEmpty()) { + positions.add(position); + } } if (channel != null) { @@ -486,7 +553,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } } - return positions; + return positions.isEmpty() ? null : positions; } @Override -- cgit v1.2.3