aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2015-03-25 18:31:39 +1300
committerAnton Tananaev <anton.tananaev@gmail.com>2015-03-25 18:31:39 +1300
commitd5589821cd437fcce5f12e82a6dfe05f57262542 (patch)
treeb4b62458b74eee6c6518c4a5b2dcc3841e7aaa1b
parentd11137df0802863b6148efd9e2a6d9fb1f9c2ee6 (diff)
downloadtraccar-server-d5589821cd437fcce5f12e82a6dfe05f57262542.tar.gz
traccar-server-d5589821cd437fcce5f12e82a6dfe05f57262542.tar.bz2
traccar-server-d5589821cd437fcce5f12e82a6dfe05f57262542.zip
Implement BCE protocol (fix #805)
-rw-r--r--src/org/traccar/protocol/BceProtocolDecoder.java164
-rw-r--r--test/org/traccar/protocol/BceProtocolDecoderTest.java4
2 files changed, 105 insertions, 63 deletions
diff --git a/src/org/traccar/protocol/BceProtocolDecoder.java b/src/org/traccar/protocol/BceProtocolDecoder.java
index d07be6ef4..a1c45758c 100644
--- a/src/org/traccar/protocol/BceProtocolDecoder.java
+++ b/src/org/traccar/protocol/BceProtocolDecoder.java
@@ -15,15 +15,16 @@
*/
package org.traccar.protocol;
-import java.util.Calendar;
+import java.nio.ByteOrder;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Properties;
-import java.util.TimeZone;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
-
import org.traccar.BaseProtocolDecoder;
import org.traccar.database.DataManager;
import org.traccar.helper.Log;
@@ -36,12 +37,18 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
super(dataManager, protocol, properties);
}
+ private static final int DATA_TYPE = 7;
+
private static final int MSG_ASYNC_STACK = 0xA5;
private static final int MSG_STACK_COFIRM = 0x19;
private static final int MSG_TIME_TRIGGERED = 0xA0;
private static final int MSG_OUTPUT_CONTROL = 0x41;
private static final int MSG_OUTPUT_CONTROL_ACK = 0xC1;
+ private static boolean checkBit(int mask, int bit) {
+ return (mask & (1 << bit)) != 0;
+ }
+
@Override
protected Object decode(
ChannelHandlerContext ctx, Channel channel, Object msg)
@@ -50,81 +57,116 @@ public class BceProtocolDecoder extends BaseProtocolDecoder {
ChannelBuffer buf = (ChannelBuffer) msg;
String imei = String.format("%015d", buf.readLong());
-
+ long deviceId;
+ try {
+ deviceId = getDataManager().getDeviceByImei(imei).getId();
+ } catch (Exception error) {
+ Log.warning("Unknown device - " + imei);
+ return null;
+ }
- /*buf.skipBytes(2); // header
- buf.readByte(); // size
+ List<Position> positions = new LinkedList<Position>();
- // Zero for location messages
- buf.readByte(); // voltage
- buf.readByte(); // gsm signal
+ while (buf.readableBytes() > 1) {
- String imei = readImei(buf);
- long index = buf.readUnsignedShort();
- int type = buf.readUnsignedByte();
+ int dataEnd = buf.readUnsignedShort() + buf.readerIndex();
+ int type = buf.readUnsignedByte();
+ int confirmKey = buf.readUnsignedByte();
- if (type == MSG_HEARTBEAT) {
- if (channel != null) {
- byte[] response = {0x54, 0x68, 0x1A, 0x0D, 0x0A};
- channel.write(ChannelBuffers.wrappedBuffer(response));
- }
- }
+ while (buf.readerIndex() < dataEnd) {
- else if (type == MSG_DATA) {
+ Position position = new Position();
+ ExtendedInfoFormatter extendedInfo = new ExtendedInfoFormatter(getProtocol());
+ position.setDeviceId(deviceId);
- // Create new position
- Position position = new Position();
- ExtendedInfoFormatter extendedInfo = new ExtendedInfoFormatter(getProtocol());
- extendedInfo.set("index", index);
+ int structEnd = buf.readUnsignedByte() + buf.readerIndex();
- // Get device id
- try {
- position.setDeviceId(getDataManager().getDeviceByImei(imei).getId());
- } catch(Exception error) {
- Log.warning("Unknown device - " + imei);
- return null;
- }
+ long time = buf.readUnsignedInt();
+ if ((time & 0x0f) == DATA_TYPE) {
- // Date and time
- Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- time.clear();
- time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte());
- time.set(Calendar.MONTH, buf.readUnsignedByte() - 1);
- time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte());
- time.set(Calendar.HOUR_OF_DAY, buf.readUnsignedByte());
- time.set(Calendar.MINUTE, buf.readUnsignedByte());
- time.set(Calendar.SECOND, buf.readUnsignedByte());
- position.setTime(time.getTime());
+ time = time >> 4 << 1;
+ time += 0x47798280; // 01/01/2008
+ position.setTime(new Date(time * 1000));
- // Latitude
- double latitude = buf.readUnsignedInt() / (60.0 * 30000.0);
+ // Read masks
+ int mask;
+ List<Integer> masks = new LinkedList<Integer>();
+ do {
+ mask = buf.readUnsignedShort();
+ masks.add(mask);
+ } while (checkBit(mask, 15));
- // Longitude
- double longitude = buf.readUnsignedInt() / (60.0 * 30000.0);
+ mask = masks.get(0);
- // Speed
- position.setSpeed((double) buf.readUnsignedByte());
+ if (checkBit(mask, 0)) {
+ position.setValid(true);
+ position.setLongitude((double) buf.readFloat());
+ position.setLatitude((double) buf.readFloat());
+ position.setSpeed((double) buf.readUnsignedByte());
- // Course
- position.setCourse((double) buf.readUnsignedShort());
+ int gps = buf.readUnsignedByte();
+ extendedInfo.set("satellites", gps & 0xf);
+ extendedInfo.set("hdop", gps >> 4);
- buf.skipBytes(3); // reserved
+ position.setCourse((double) buf.readUnsignedByte());
+ position.setAltitude((double) buf.readUnsignedShort());
- // Flags
- long flags = buf.readUnsignedInt();
- position.setValid((flags & 0x1) == 0x1);
- if ((flags & 0x2) == 0) latitude = -latitude;
- if ((flags & 0x4) == 0) longitude = -longitude;
+ extendedInfo.set("milage", buf.readUnsignedInt());
- position.setLatitude(latitude);
- position.setLongitude(longitude);
- position.setAltitude(0.0);
+ position.setExtendedInfo(extendedInfo.toString());
+ }
- position.setExtendedInfo(extendedInfo.toString());
- return position;
- }*/
+ if (checkBit(mask, 1)) {
+ extendedInfo.set("input", buf.readUnsignedShort());
+ }
+
+ for (int i = 1; i <= 8; i++) {
+ if (checkBit(mask, i + 1)) {
+ extendedInfo.set("adc" + i, buf.readUnsignedShort());
+ }
+ }
+
+ if (checkBit(mask, 10)) buf.skipBytes(4);
+ if (checkBit(mask, 11)) buf.skipBytes(4);
+ if (checkBit(mask, 12)) buf.skipBytes(2);
+ if (checkBit(mask, 13)) buf.skipBytes(2);
+
+ if (checkBit(mask, 14)) {
+ extendedInfo.set("mcc", buf.readUnsignedShort());
+ extendedInfo.set("mnc", buf.readUnsignedByte());
+ extendedInfo.set("lac", buf.readUnsignedShort());
+ extendedInfo.set("cell", buf.readUnsignedShort());
+ extendedInfo.set("gsm", buf.readUnsignedByte());
+ buf.readUnsignedByte();
+ }
+
+ if (position.getValid() != null) {
+ positions.add(position);
+ }
+ }
+
+ buf.readerIndex(structEnd);
+ }
+
+ // Send response
+ if (type == MSG_ASYNC_STACK && channel != null) {
+ ChannelBuffer response = ChannelBuffers.buffer(ByteOrder.LITTLE_ENDIAN, 8 + 2 + 2 + 1);
+ response.writeLong(Long.valueOf(imei));
+ response.writeShort(2);
+ response.writeByte(MSG_STACK_COFIRM);
+ response.writeByte(confirmKey);
+
+ int checksum = 0;
+ for (int i = 0; i < response.writerIndex(); i++) {
+ checksum += response.getUnsignedByte(i);
+ }
+ response.writeByte(checksum);
+
+ channel.write(response);
+ }
+ }
- return null;
+ return positions;
}
}
diff --git a/test/org/traccar/protocol/BceProtocolDecoderTest.java b/test/org/traccar/protocol/BceProtocolDecoderTest.java
index f8120bfa9..ded4ec291 100644
--- a/test/org/traccar/protocol/BceProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/BceProtocolDecoderTest.java
@@ -16,7 +16,7 @@ public class BceProtocolDecoderTest {
BceProtocolDecoder decoder = new BceProtocolDecoder(new TestDataManager(), null, null);
- /*verify(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, ChannelBufferTools.convertHexString(
+ verify(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, ChannelBufferTools.convertHexString(
"be76619c834601004200a0003fd769c568ffc3db0079161d420683a9414918b1150000000000d102660167040000000000009f06357f0000a401042ea415e10232000000000000000000000051"))));
verify(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, ChannelBufferTools.convertHexString(
@@ -32,7 +32,7 @@ public class BceProtocolDecoderTest {
"be76619c834601003302a5e8327764726bff432fc52a420e2c93410028afd2070000000080024a0005040000000000008e06547f0000a401043cf21f390e54328764726bff432fc52a420e2c93410028afd2070000000080024c0005040000000000008e064f7f0000a401043cf21f390e54329764726bff432fc52a420e2c93410028afd2070000000080024e0002040000000000008d064f7f0000a401043cf21f390e5432a764726bff432fc52a420e2c93410028afd2070000000080024e0004040000000000008e06587f0000a401043cf21f390e5432b764726bff432fc52a420e2c93410028afd207000000008002460005040000000000008e06557f0000a401043cf21f390e5432c764726bff432fc52a420e2c93410028afd2070000000080024e0004040000000000008e06347f0000a401043cf21f390e5432d764726bff432fc52a420e2c93410028afd2070000000080024e0002040000000000008e06547f0000a401043cf21f390e5432e764726bff432fc52a420e2c93410028afd207000000008002540002040000000000008e06477f0000a401043cf21f390e5432f764726bff432fc52a420e2c93410028afd207000000008002540004040000000000008d064f7f0000a401043cf21f390e54320765726bff432fc52a420e2c93410028afd207000000008002540004040000000000008e064d7f0000a401043cf21f390e54321765726bff432fc52a420e2c93410028afd207000000008002540004040000000000008e06467f0000a401043cf21f390e544200a0003f3743c96bffc3db0060c81c42d885ab41002aaf060000000000d102380167040000000000008a064f7f0000a4010412a46b330033000000000000000000000025"))));
verify(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, ChannelBufferTools.convertHexString(
- "ca07629c834601002702a58f3c278ff96a0bc000a0c00140bc3a42508bc541002a70a905000000009000c101a40103d904440e003000000000000000000000000000000000000001013c878ff96a0bc000a0c00140bc3a42508bc541002970a905000000009000c301a40103d904440e003000000000000000000000000000000000000001013cb7d2f96a0bc000a0c00124bc3a426b8fc5410428000404000000009000c401a40103d904440e003500000000000000000000000000000000000001013cc7d2f96a0bc000a0c00124bc3a426b8fc5410428000404000000009000c301a40103d904440e003500000000000000000000000000000000000001013cd7f2f96a0bc000a0c00114bc3a42a48fc5410029027e03000000009000c301a40103d904440e003000000000000000000000000000000000000001013c670dfa6a0bc000a0c001f1bb3a42418dc541002a484904000000009000c001a40103d904440e003a00000000000000000000000000000000000001013c770dfa6a0bc000a0c001f1bb3a42418dc5410028484904000000009000bf01a40103d904440e003a00000000000000000000000000000000000001013c470efa6a0bc000a0c001f1bb3a42418dc5410029484904000000009000bf01a40103d904440e003a00000000000000000000000000000000000001013c5711fa6a0bc000a0c001f1bb3a42418dc5410029484904000000009000c101a40103d904440e003000000000000000000000000000000000000001013f00a0003cc795866b0bc000a0c00144bc3a423a90c541003697cb03000000008000cf01a40103d9040d0f0030000000000000000000000000000000000000010100"))));*/
+ "ca07629c834601002702a58f3c278ff96a0bc000a0c00140bc3a42508bc541002a70a905000000009000c101a40103d904440e003000000000000000000000000000000000000001013c878ff96a0bc000a0c00140bc3a42508bc541002970a905000000009000c301a40103d904440e003000000000000000000000000000000000000001013cb7d2f96a0bc000a0c00124bc3a426b8fc5410428000404000000009000c401a40103d904440e003500000000000000000000000000000000000001013cc7d2f96a0bc000a0c00124bc3a426b8fc5410428000404000000009000c301a40103d904440e003500000000000000000000000000000000000001013cd7f2f96a0bc000a0c00114bc3a42a48fc5410029027e03000000009000c301a40103d904440e003000000000000000000000000000000000000001013c670dfa6a0bc000a0c001f1bb3a42418dc541002a484904000000009000c001a40103d904440e003a00000000000000000000000000000000000001013c770dfa6a0bc000a0c001f1bb3a42418dc5410028484904000000009000bf01a40103d904440e003a00000000000000000000000000000000000001013c470efa6a0bc000a0c001f1bb3a42418dc5410029484904000000009000bf01a40103d904440e003a00000000000000000000000000000000000001013c5711fa6a0bc000a0c001f1bb3a42418dc5410029484904000000009000c101a40103d904440e003000000000000000000000000000000000000001013f00a0003cc795866b0bc000a0c00144bc3a423a90c541003697cb03000000008000cf01a40103d9040d0f0030000000000000000000000000000000000000010100"))));
}