aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/org/traccar/protocol/TeltonikaProtocolDecoder.java85
-rw-r--r--test/org/traccar/protocol/TeltonikaProtocolDecoderTest.java20
2 files changed, 96 insertions, 9 deletions
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<Long, ByteBuf> 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
diff --git a/test/org/traccar/protocol/TeltonikaProtocolDecoderTest.java b/test/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
index 6450a8591..37dc21f55 100644
--- a/test/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/TeltonikaProtocolDecoderTest.java
@@ -96,6 +96,26 @@ public class TeltonikaProtocolDecoderTest extends ProtocolTest {
}
+
+ @Test
+ public void testDecodePhoto() throws Exception {
+
+ TeltonikaProtocolDecoder decoder = new TeltonikaProtocolDecoder(null, false);
+
+ verifyNull(decoder, binary(
+ "000F313233343536373839303132333435"));
+
+ verifyNull(decoder, binary(
+ "00000000000000090c010D00000001000100000CD8"));
+
+ verifyNull(decoder, binary(
+ "000000000000000D0c010D0000000501598493ED01000018B2"));
+
+ verifyNull(decoder, binary(
+ "00000000000008130c010d0000080b02598493ED000000000800ffd8ffe000104a46494600010201006000600000ffdb0084000202020202020202020202020202020403020202020504040304060506060605060606070908060709070606080b08090a0a0a0a0a06080b0c0b0a0c090a0a0a01020202020202050303050a0706070a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0affc401a20000010501010101010100000000000000000102030405060708090a0b0100030101010101010101010000000000000102030405060708090a0b100002010303020403050504040000017d01020300041105122131410613516107227114328191a1082342b1c11552d1f02433627282090a161718191a25262728292a3435363738393a434445464748494a535455565758595a636465666768696a737475767778797a838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae1e2e3e4e5e6e7e8e9eaf1f2f3f4f5f6f7f8f9fa1100020102040403040705040400010277000102031104052131061241510761711322328108144291a1b1c109233352f0156272d10a162434e125f11718191a262728292a35363738393a434445464748494a535455565758595a636465666768696a737475767778797a82838485868788898a92939495969798999aa2a3a4a5a6a7a8a9aab2b3b4b5b6b7b8b9bac2c3c4c5c6c7c8c9cad2d3d4d5d6d7d8d9dae2e3e4e5e6e7e8e9eaf2f3f4f5f6f7f8f9faffc000110801e002d003012200021101031101ffdd00040054ffda000c03010002110311003f00fc918d555b0c0007a8cd4e3054a8c807a64540a32d900f07e626ac2fce0f4014718ae652ba68e87ef222238da4f20f415226d00823a838045210149cf24fbd30305c31391cf18a52574269ad2c49b9be518c60d4dbb0194003232323b542092509e840c0a9304120e4827068924e5a8db761a0971b402011c1229ea02608f5ebeb4a09fba00001eb520552a001c83d09a1493d1149a48455009dc4020f029f210a98079a52a32a091b8f24d23042c41c93fc38a517a89b6f61b19dc371c923a1a9631f2b8639c1e326a280124ae0804fe55232153819c93924d17698452488c079380792dc1c5491a80e10825327f3a7b60052a36e4f069e9d77b1c053d4d24d3634aeee3769f31890060f03da9f364c6029cee5e09a7125db790013d69a49200000eb4ecbd0a5749d86a10ab1a827716c64d3d57639181ce4e29c880216246e43c12296360e0c8c4865e9c76a7169bb762534da18b1b3fdfc003a0a951004236e5b77ca49a8959c48a013f33723daa74604c84f0a0e011d8d36d27a83936c67987718c819cf20fa54ae018f6648048c0c74a810b1901009603a9ed53005958900943920f7a9d131deeb41854064519c01c934e000914124865e49a7901ca903af63da918a96418c91c311495a49836e438f240ebb4fcbf4a7a16219828000232691f3953c023a01446e49008da83ef0f5a6acb71249a41b4642331e0f5a7b8023193f311c81e94e5da496750016ea476a64b2050495279f9463b51aa2ac86a200c1412081c9a9810d2384206178c1a8d010a1ca92d8fc853e37551900167079a4db6ef70bc799362f963682c3073dfbd2e49508848e7e6a79264894e3033900543138569093b8b82001d8d5dd2d985d262801880bcaaf1c1ef52b92c91c600c27041f4a6292acaa9c0c724fad293f3161938eb93509b6f713695d844586e00e060e38a72260b9e8aab9247a546a30c40242b8e07a54b9fbca092ae3814ee9ec4ab45a60c140c81b8b918149b0b2ba3120eee707a5354b052554e54f04f6a7a9561b413bcb0249345d25a17a5ee3c81e5a804860723029502bec2fdb8201ea69db39c90403f74fb5359580057ef6780294527aafe98ad1487ae017278dab900d47390c8072188f978ef52e0b48c41fbc3048a6381bd41390a791449a6c4af263f0ab124472485cb0f53532c8137a6727193c77a81582caace7200e38a7be7cc9b6a819192cc78a77567b84795a240ac0463000232e734b127254fde4078a7021d22232d82371f4a69251caa900b3e4367b526928bbf512516eec91103204271d720fd6a605427cbc04e066aaa615d482483d81ef53306024503049c939e94dd96a55d5c6c6518b316e431c71d6a78c99033138c1f947ad5451f3aae7193f301eb5ad02a44c430071cb023a524d3d413e87b0e829245f0f3c73242e229e4f095e209ca062a1e278fa3023a39edeb5f13ce144ae01dc3770718afb5bc3e88fe0bf1c1b8252de4f09de9322a92630b113b801d4fe07815f155cb0695982ed19e00aceed55934fa47ff6ef989abc48c0cf23800f5cd3d4f24e38238a8949c00304e79a901e7a678a1b5ad898b492255c024824e48e2a55272307041a8548c01d077352a9c923ae2a559bd4a6d264c0e060f04f7a9800001ebd6a01b7007383d2a75008039a4d3be80f995c82f0916f290707674f5aa5e1c19b9949e3e4e3f3ab97b9fb34a31c05e9557c3a019a50724003b576615e8ccab5940ee62c123af5e302aeaf0463a8355225276e3a0ee2ad82474e83b115d8d7bda9cea4921aec76b678c83d6a9d9f319c1c658f22adc9cab6464e0e00aab60018723b93835c59838ac3dcebc124f106cdaa8dcbcf39afa13e0fc65b5e848c83b3e519ef91fe35f3f5a801973d8f15f48fc148fcdf10da2f003ca8a491dcbafaf7e2be2f3549e1a4bb9f474928c6ecfdd5d1a309a7d928fe1b68c0c7fbb5d1c28db01c77ac7d3d156de054185f2d76fd315d25a85d801e7938cd7d2609bf6d7f53e66ac7f77a010000b7cd"));
+
+ }
+
@Ignore
@Test
public void testDecodeConnectionless() throws Exception {