aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVitaly Litvak <vitavaque@gmail.com>2015-02-23 20:23:35 +0300
committerVitaly Litvak <vitavaque@gmail.com>2015-02-23 20:23:35 +0300
commitd96638ab083af4134a54536abd00d3810627699d (patch)
treeaf48a4d62266d09d8e7f42f38eafba43d7fa8692
parentd437564381fafb807f61da0d2477e50a5d14d381 (diff)
downloadtrackermap-server-d96638ab083af4134a54536abd00d3810627699d.tar.gz
trackermap-server-d96638ab083af4134a54536abd00d3810627699d.tar.bz2
trackermap-server-d96638ab083af4134a54536abd00d3810627699d.zip
Implemented AutoFon 4.5 protocol (also works for StarLine M10/M11/M16/M17 devices)
-rw-r--r--default.cfg4
-rw-r--r--src/org/traccar/ServerManager.java12
-rw-r--r--src/org/traccar/protocol/AutoFon45FrameDecoder.java57
-rw-r--r--src/org/traccar/protocol/AutoFon45ProtocolDecoder.java138
-rw-r--r--test/org/traccar/protocol/AutoFon45ProtocolDecoderTest.java22
5 files changed, 233 insertions, 0 deletions
diff --git a/default.cfg b/default.cfg
index 072e90fef..c0adcc97c 100644
--- a/default.cfg
+++ b/default.cfg
@@ -387,4 +387,8 @@
<entry key='gosafe.enable'>true</entry>
<entry key='gosafe.port'>5078</entry>
+ <!-- AutoFon 4.5 / Starline M10/M11/M16/M17 server configuration -->
+ <entry key='autofon45.enable'>true</entry>
+ <entry key='autofon45.port'>5079</entry>
+
</properties>
diff --git a/src/org/traccar/ServerManager.java b/src/org/traccar/ServerManager.java
index d7bfb511b..6b46079e9 100644
--- a/src/org/traccar/ServerManager.java
+++ b/src/org/traccar/ServerManager.java
@@ -179,6 +179,7 @@ public class ServerManager {
initXt013Server("xt013");
initAutoFonServer("autofon");
initGoSafeServer("gosafe");
+ initAutoFon45Server("autofon45");
initProtocolDetector();
@@ -1310,4 +1311,15 @@ public class ServerManager {
}
}
+ private void initAutoFon45Server(final String protocol) throws SQLException {
+ if (isProtocolEnabled(properties, protocol)) {
+ serverList.add(new TrackerServer(this, new ServerBootstrap(), protocol) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new AutoFon45FrameDecoder());
+ pipeline.addLast("objectDecoder", new AutoFon45ProtocolDecoder(dataManager, protocol, properties));
+ }
+ });
+ }
+ }
}
diff --git a/src/org/traccar/protocol/AutoFon45FrameDecoder.java b/src/org/traccar/protocol/AutoFon45FrameDecoder.java
new file mode 100644
index 000000000..e388273cf
--- /dev/null
+++ b/src/org/traccar/protocol/AutoFon45FrameDecoder.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 Vitaly Litvak (vitavaque@gmail.com)
+ *
+ * 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.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+
+public class AutoFon45FrameDecoder extends FrameDecoder {
+
+ static final int MSG_LOGIN = 0x41;
+ static final int MSG_LOCATION = 0x02;
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx,
+ Channel channel,
+ ChannelBuffer buf) throws Exception {
+
+ // Check minimum length
+ if (buf.readableBytes() < 12) {
+ return null;
+ }
+
+ int length = 0;
+ switch (buf.getUnsignedByte(buf.readerIndex())) {
+ case MSG_LOGIN:
+ length = 19;
+ break;
+ case MSG_LOCATION:
+ length = 34;
+ break;
+ }
+
+ // Check length and return buffer
+ if (length != 0 && buf.readableBytes() >= length) {
+ return buf.readBytes(length);
+ }
+
+ return null;
+ }
+
+}
diff --git a/src/org/traccar/protocol/AutoFon45ProtocolDecoder.java b/src/org/traccar/protocol/AutoFon45ProtocolDecoder.java
new file mode 100644
index 000000000..df98fa93b
--- /dev/null
+++ b/src/org/traccar/protocol/AutoFon45ProtocolDecoder.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2015 Vitaly Litvak (vitavaque@gmail.com)
+ *
+ * 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 static org.traccar.protocol.AutoFon45FrameDecoder.MSG_LOGIN;
+import static org.traccar.protocol.AutoFon45FrameDecoder.MSG_LOCATION;
+
+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.ChannelBufferTools;
+import org.traccar.helper.Log;
+import org.traccar.model.ExtendedInfoFormatter;
+import org.traccar.model.Position;
+
+import java.util.*;
+
+public class AutoFon45ProtocolDecoder extends BaseProtocolDecoder {
+ private long deviceId;
+
+ private static double convertCoordinate(short degrees, int raw) {
+ double seconds = (raw >> 4 & 0xffffff) / 600000.0;
+ return (degrees + seconds) * ((raw & 0x0f) == 0 ? -1 : 1);
+ }
+
+ public AutoFon45ProtocolDecoder(DataManager dataManager, String protocol, Properties properties) {
+ super(dataManager, protocol, properties);
+ }
+
+ @Override
+ protected Object decode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
+ ChannelBuffer buf = (ChannelBuffer) msg;
+
+ int type = buf.getUnsignedByte(0);
+
+ if (type == MSG_LOGIN) {
+ byte[] bytes = new byte[19];
+ buf.readBytes(bytes);
+
+ String imei = ChannelBufferTools.readHexString(ChannelBuffers.wrappedBuffer(bytes, 1, 16), 16).substring(1);
+ try {
+ deviceId = getDataManager().getDeviceByImei(imei).getId();
+ } catch(Exception error) {
+ Log.warning("Unknown device - " + imei);
+ return null;
+ }
+
+ // Send response (CRC)
+ if (channel != null) {
+ byte[] response = "resp_crc=".getBytes("US-ASCII");
+ response = Arrays.copyOf(response, response.length + 1);
+ response[response.length - 1] = crc(bytes, 0, 18);
+ channel.write(ChannelBuffers.wrappedBuffer(response));
+ }
+ } else if (type == MSG_LOCATION) {
+ buf.readUnsignedByte();
+
+ // Create new position
+ Position position = new Position();
+ ExtendedInfoFormatter extendedInfo = new ExtendedInfoFormatter(getProtocol());
+ position.setDeviceId(deviceId);
+
+ short status = buf.readUnsignedByte();
+ extendedInfo.set("alarm", (status & 0x80) != 0);
+ extendedInfo.set("battery", status & 0x7F);
+
+ buf.skipBytes(2); // remaining time
+
+ extendedInfo.set("temperature", buf.readByte());
+
+ buf.skipBytes(2); // timer (interval and units)
+ buf.readByte(); // mode
+ buf.readByte(); // gprs sending interval
+
+ buf.skipBytes(6); // MCC, MNC, LAC, CID
+
+ // GPS status
+ int valid = buf.readUnsignedByte();
+ position.setValid((valid & 0xc0) != 0);
+ extendedInfo.set("satellites", valid & 0x3f);
+
+ // Date and time
+ int timeOfDay = buf.readUnsignedByte() << 16 | buf.readUnsignedByte() << 8 | buf.readUnsignedByte();
+ int date = buf.readUnsignedByte() << 16 | buf.readUnsignedByte() << 8 | buf.readUnsignedByte();
+
+ Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ time.clear();
+ time.set(Calendar.HOUR_OF_DAY, timeOfDay / 10000);
+ time.set(Calendar.MINUTE, (timeOfDay - time.get(Calendar.HOUR_OF_DAY) * 10000) / 100);
+ time.set(Calendar.SECOND, (timeOfDay - time.get(Calendar.HOUR_OF_DAY) * 10000 - time.get(Calendar.MINUTE) * 100));
+ time.set(Calendar.DAY_OF_MONTH, date / 10000);
+ time.set(Calendar.MONTH, (date - time.get(Calendar.DAY_OF_MONTH) * 10000) / 100 - 1);
+ time.set(Calendar.YEAR, 2000 + (date - time.get(Calendar.DAY_OF_MONTH) * 10000 - (time.get(Calendar.MONTH) + 1) * 100));
+ position.setTime(time.getTime());
+
+ // Location
+ position.setLatitude(convertCoordinate(buf.readUnsignedByte(), buf.readUnsignedByte() << 16 | buf.readUnsignedByte() << 8 | buf.readUnsignedByte()));
+ position.setLongitude(convertCoordinate(buf.readUnsignedByte(), buf.readUnsignedByte() << 16 | buf.readUnsignedByte() << 8 | buf.readUnsignedByte()));
+ position.setSpeed((double) buf.readUnsignedByte());
+ position.setCourse((double) (buf.readUnsignedByte() << 8 | buf.readUnsignedByte()));
+ position.setAltitude(0.0);
+
+ buf.readUnsignedByte(); // checksum
+
+ position.setExtendedInfo(extendedInfo.toString());
+ return position;
+ }
+
+ return null;
+ }
+
+ private byte crc(byte[] bytes, int offset, int len) {
+ byte GPRS_CRC = 0x3B;
+ for (int i = offset; i < offset + len; i++) {
+ GPRS_CRC += 0x56 ^ bytes[i];
+ GPRS_CRC++;
+ GPRS_CRC ^= 0xC5 + bytes[i];
+ GPRS_CRC--;
+ }
+ return GPRS_CRC;
+ }
+}
diff --git a/test/org/traccar/protocol/AutoFon45ProtocolDecoderTest.java b/test/org/traccar/protocol/AutoFon45ProtocolDecoderTest.java
new file mode 100644
index 000000000..2665c7cab
--- /dev/null
+++ b/test/org/traccar/protocol/AutoFon45ProtocolDecoderTest.java
@@ -0,0 +1,22 @@
+package org.traccar.protocol;
+
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.junit.Test;
+import org.traccar.helper.ChannelBufferTools;
+import org.traccar.helper.TestDataManager;
+
+import static org.junit.Assert.assertNull;
+import static org.traccar.helper.DecoderVerifier.verify;
+
+public class AutoFon45ProtocolDecoderTest {
+ @Test
+ public void testDecode() throws Exception {
+ AutoFon45ProtocolDecoder decoder = new AutoFon45ProtocolDecoder(new TestDataManager(), null, null);
+
+ assertNull(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ChannelBufferTools.convertHexString(
+ "41032125656985547543619173484002123481"))));
+
+ verify(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ChannelBufferTools.convertHexString(
+ "023E00001E004D411EFA01772F185285009C48041F1E366C2961380F26B10B00911C"))));
+ }
+}