aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2013-05-15 22:18:04 +1200
committerAnton Tananaev <anton.tananaev@gmail.com>2013-05-15 22:18:04 +1200
commitb3d221372103a677002428a57686b61e3facf32c (patch)
tree494b2df06aaa1fd675e48bb16cf74fe5f77bafb4
parent14b7897b6df25a144da1a329f4a088b52a01571b (diff)
downloadtraccar-server-b3d221372103a677002428a57686b61e3facf32c.tar.gz
traccar-server-b3d221372103a677002428a57686b61e3facf32c.tar.bz2
traccar-server-b3d221372103a677002428a57686b61e3facf32c.zip
Initial apel support
-rw-r--r--default.cfg4
-rw-r--r--src/org/traccar/ServerManager.java15
-rw-r--r--src/org/traccar/protocol/ApelProtocolDecoder.java298
-rw-r--r--test/org/traccar/protocol/ApelProtocolDecoderTest.java28
4 files changed, 345 insertions, 0 deletions
diff --git a/default.cfg b/default.cfg
index 487e9fe23..112a76931 100644
--- a/default.cfg
+++ b/default.cfg
@@ -225,4 +225,8 @@
<entry key='carscop.enable'>true</entry>
<entry key='carscop.port'>5040</entry>
+ <!-- Apel server configuration -->
+ <entry key='apel.enable'>true</entry>
+ <entry key='apel.port'>5041</entry>
+
</properties>
diff --git a/src/org/traccar/ServerManager.java b/src/org/traccar/ServerManager.java
index ac97da4f2..971859099 100644
--- a/src/org/traccar/ServerManager.java
+++ b/src/org/traccar/ServerManager.java
@@ -147,6 +147,7 @@ public class ServerManager {
initXt7Server("xt7");
initWialonServer("wialon");
initCarscopServer("carscop");
+ initApelServer("apel");
// Initialize web server
if (Boolean.valueOf(properties.getProperty("http.enable"))) {
@@ -842,4 +843,18 @@ public class ServerManager {
}
}
+ private void initApelServer(String protocol) throws SQLException {
+ if (isProtocolEnabled(properties, protocol)) {
+ TrackerServer server = new TrackerServer(this, new ServerBootstrap(), protocol) {
+ @Override
+ protected void addSpecificHandlers(ChannelPipeline pipeline) {
+ pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 2, 2, 4, 0));
+ pipeline.addLast("objectDecoder", new ApelProtocolDecoder(ServerManager.this));
+ }
+ };
+ server.setEndianness(ByteOrder.LITTLE_ENDIAN);
+ serverList.add(server);
+ }
+ }
+
}
diff --git a/src/org/traccar/protocol/ApelProtocolDecoder.java b/src/org/traccar/protocol/ApelProtocolDecoder.java
new file mode 100644
index 000000000..fec92b33e
--- /dev/null
+++ b/src/org/traccar/protocol/ApelProtocolDecoder.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2013 Anton Tananaev (anton.tananaev@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 java.nio.ByteOrder;
+import java.nio.charset.Charset;
+import java.sql.ResultSet;
+import java.util.Calendar;
+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.ServerManager;
+import org.traccar.helper.AdvancedConnection;
+import org.traccar.helper.Log;
+import org.traccar.helper.NamedParameterStatement;
+import org.traccar.model.ExtendedInfoFormatter;
+import org.traccar.model.Position;
+
+public class ApelProtocolDecoder extends BaseProtocolDecoder {
+
+ private long deviceId;
+ private long lastIndex;
+ private long newIndex;
+
+ public ApelProtocolDecoder(ServerManager serverManager) {
+ super(serverManager);
+ }
+
+ /*
+ * Message types
+ */
+ /*private static final int MSG_NULL = 0;
+ private static final int MSG_IDENT = 1;
+ private static final int MSG_IDENT_FULL = 2;
+ private static final int MSG_POINT = 10;
+ private static final int MSG_LOG_SYNC = 100;
+ private static final int MSG_LOGMSG = 101;
+ private static final int MSG_TEXT = 102;
+ private static final int MSG_ALARM = 200;
+ private static final int MSG_ALARM_RECIEVED = 201;*/
+ private static final int MSG_TYPE_NULL = 0;
+ private static final int MSG_TYPE_REQUEST_TRACKER_ID = 10;
+ private static final int MSG_TYPE_TRACKER_ID = 11;
+ private static final int MSG_TYPE_TRACKER_ID_EXT = 12;
+ private static final int MSG_TYPE_DISCONNECT = 20;
+ private static final int MSG_TYPE_REQUEST_PASSWORD = 30;
+ private static final int MSG_TYPE_PASSWORD = 31;
+ private static final int MSG_TYPE_REQUEST_STATE_FULL_INFO = 90;
+ private static final int MSG_TYPE_STATE_FULL_INFO_T104 = 92;
+ private static final int MSG_TYPE_REQUEST_CURRENT_GPS_DATA = 100;
+ private static final int MSG_TYPE_CURRENT_GPS_DATA = 101;
+ private static final int MSG_TYPE_REQUEST_SENSORS_STATE = 110;
+ private static final int MSG_TYPE_SENSORS_STATE = 111;
+ private static final int MSG_TYPE_SENSORS_STATE_T100 = 112;
+ private static final int MSG_TYPE_SENSORS_STATE_T100_4 = 113;
+ private static final int MSG_TYPE_REQUEST_LAST_LOG_INDEX = 120;
+ private static final int MSG_TYPE_LAST_LOG_INDEX = 121;
+ private static final int MSG_TYPE_REQUEST_LOG_RECORDS = 130;
+ private static final int MSG_TYPE_LOG_RECORDS = 131;
+ private static final int MSG_TYPE_EVENT = 141;
+ private static final int MSG_TYPE_TEXT = 150;
+ private static final int MSG_TYPE_ACK_ALARM = 160;
+ private static final int MSG_TYPE_SET_TRACKER_MODE = 170;
+ private static final int MSG_TYPE_GPRS_COMMAND = 180;
+
+ private static final String HEX_CHARS = "0123456789ABCDEF";
+
+ /*private void loadLastIndex() {
+ try {
+ Properties p = getServerManager().getProperties();
+ if (p.contains("database.selectLastIndex")) {
+ AdvancedConnection connection = new AdvancedConnection(
+ p.getProperty("database.url"), p.getProperty("database.user"), p.getProperty("database.password"));
+ NamedParameterStatement queryLastIndex = new NamedParameterStatement(connection, p.getProperty("database.selectLastIndex"));
+ queryLastIndex.prepare();
+ queryLastIndex.setLong("device_id", deviceId);
+ ResultSet result = queryLastIndex.executeQuery();
+ if (result.next()) {
+ lastIndex = result.getLong(1);
+ }
+ }
+ } catch(Exception error) {
+ }
+ }*/
+
+ /*private void requestArchive(Channel channel) {
+ if (lastIndex == 0) {
+ lastIndex = newIndex;
+ } else if (newIndex > lastIndex) {
+ ChannelBuffer request = ChannelBuffers.directBuffer(ByteOrder.LITTLE_ENDIAN, 12);
+ request.writeShort(MSG_LOG_SYNC);
+ request.writeShort(4);
+ request.writeInt((int) lastIndex);
+ request.writeInt(0);
+ channel.write(request);
+ }
+ }*/
+
+ @Override
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, Object msg)
+ throws Exception {
+
+ ChannelBuffer buf = (ChannelBuffer) msg;
+ int type = buf.readUnsignedShort();
+ boolean alarm = (type & 0x8000) != 0;
+ type = type & 0x7FFF;
+ buf.readUnsignedShort(); // length
+
+ if (type == MSG_TYPE_TRACKER_ID) {
+ Log.warning("Unsupported authentication type");
+ return null;
+ }
+
+ if (type == MSG_TYPE_TRACKER_ID_EXT) {
+ long id = buf.readUnsignedInt();
+ int length = buf.readUnsignedShort();
+ buf.skipBytes(length);
+ length = buf.readUnsignedShort();
+ String imei = buf.readBytes(length).toString(Charset.defaultCharset());
+ try {
+ deviceId = getDataManager().getDeviceByImei(imei).getId();
+ //loadLastIndex();
+ } catch(Exception error) {
+ Log.warning("Unknown device - " + imei + " (id - " + id + ")");
+ }
+ }
+
+ // Position
+ else if (deviceId != 0 && (type == MSG_TYPE_CURRENT_GPS_DATA || type == MSG_TYPE_STATE_FULL_INFO_T104)) {
+ List<Position> positions = new LinkedList<Position>();
+
+ int recordCount = 1;
+ /*if (type == MSG_LOGMSG) {
+ recordCount = buf.readUnsignedShort();
+ }*/
+
+ for (int j = 0; j < recordCount; j++) {
+ Position position = new Position();
+ ExtendedInfoFormatter extendedInfo = new ExtendedInfoFormatter("apel");
+ position.setDeviceId(deviceId);
+
+ // Message index
+ /*if (type == MSG_LOGMSG) {
+ extendedInfo.set("archive", true);
+ int subtype = buf.readUnsignedShort();
+ if (subtype == MSG_ALARM) {
+ extendedInfo.set("alarm", true);
+ }
+ if (buf.readUnsignedShort() > buf.readableBytes()) {
+ lastIndex += 1;
+ break; // workaround for device bug
+ }
+ lastIndex = buf.readUnsignedInt();
+ position.setId(lastIndex);
+ } else {
+ newIndex = buf.readUnsignedInt();
+ }*/
+
+ // Time
+ Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ time.clear();
+ time.setTimeInMillis(buf.readUnsignedInt() * 1000);
+ position.setTime(time.getTime());
+
+ // Latitude
+ position.setLatitude(buf.readInt() * 180.0 / 0x7FFFFFFF);
+
+ // Longitude
+ position.setLongitude(buf.readInt() * 180.0 / 0x7FFFFFFF);
+
+ // Speed and Validity
+ if (type == MSG_TYPE_CURRENT_GPS_DATA) {
+ int speed = buf.readShort();
+ position.setValid(speed != -1);
+ position.setSpeed(speed / 100.0 * 0.539957);
+ } else if (type == MSG_TYPE_STATE_FULL_INFO_T104) {
+ int speed = buf.readUnsignedByte();
+ position.setValid(speed != 255);
+ position.setSpeed(speed * 0.539957);
+ extendedInfo.set("hdop", buf.readByte());
+ }
+
+ // Course
+ position.setCourse(buf.readShort() / 100.0);
+
+ // Altitude
+ position.setAltitude((double) buf.readShort());
+
+ if (type == MSG_TYPE_STATE_FULL_INFO_T104) {
+
+ // Satellites
+ extendedInfo.set("satellites", buf.readUnsignedByte());
+
+ // Cell signal
+ extendedInfo.set("gsm", buf.readUnsignedByte());
+
+ // Event type
+ extendedInfo.set("event", buf.readUnsignedShort());
+
+ // Milage
+ extendedInfo.set("milage", buf.readUnsignedInt());
+
+ // Input/Output
+ extendedInfo.set("input", buf.readUnsignedByte());
+ extendedInfo.set("output", buf.readUnsignedByte());
+
+ // Analog sensors
+ for (int i = 1; i <= 8; i++) {
+ extendedInfo.set("adc" + i, buf.readUnsignedShort());
+ }
+
+ // Counters
+ extendedInfo.set("c0", buf.readUnsignedInt());
+ extendedInfo.set("c1", buf.readUnsignedInt());
+ extendedInfo.set("c2", buf.readUnsignedInt());
+
+ /*long extraFlags = buf.readLong();
+
+ // Analog inputs
+ if ((extraFlags & 0x1) == 0x1) {
+ int count = buf.readUnsignedShort();
+ for (int i = 1; i <= count; i++) {
+ extendedInfo.set("adc" + i, buf.readUnsignedShort());
+ }
+
+ }
+
+ // CAN adapter
+ if ((extraFlags & 0x2) == 0x2) {
+ int size = buf.readUnsignedShort();
+ extendedInfo.set("can", buf.toString(buf.readerIndex(), size, Charset.defaultCharset()));
+ buf.skipBytes(size);
+ }
+
+ // Passenger sensor
+ if ((extraFlags & 0x4) == 0x4) {
+ int size = buf.readUnsignedShort();
+
+ // Convert binary data to hex
+ StringBuilder hex = new StringBuilder();
+ for (int i = buf.readerIndex(); i < buf.readerIndex() + size; i++) {
+ byte b = buf.getByte(i);
+ hex.append(HEX_CHARS.charAt((b & 0xf0) >> 4));
+ hex.append(HEX_CHARS.charAt((b & 0x0F)));
+ }
+
+ extendedInfo.set("passenger", hex);
+
+ buf.skipBytes(size);
+ }
+
+ // Send response for alarm message
+ if (type == MSG_ALARM) {
+ byte[] response = {(byte)0xC9,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ channel.write(ChannelBuffers.wrappedBuffer(response));
+
+ extendedInfo.set("alarm", true);
+ }*/
+ }
+
+ // Skip CRC
+ buf.readUnsignedInt();
+
+ // Extended info
+ position.setExtendedInfo(extendedInfo.toString());
+
+ positions.add(position);
+ }
+
+ //requestArchive(channel);
+
+ return positions;
+ }
+
+ return null;
+ }
+
+}
diff --git a/test/org/traccar/protocol/ApelProtocolDecoderTest.java b/test/org/traccar/protocol/ApelProtocolDecoderTest.java
new file mode 100644
index 000000000..1cd1291f4
--- /dev/null
+++ b/test/org/traccar/protocol/ApelProtocolDecoderTest.java
@@ -0,0 +1,28 @@
+package org.traccar.protocol;
+
+import java.nio.ByteOrder;
+import org.jboss.netty.buffer.ChannelBuffers;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import org.junit.Test;
+
+public class ApelProtocolDecoderTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ ApelProtocolDecoder decoder = new ApelProtocolDecoder(null);
+ decoder.setDataManager(new TestDataManager());
+
+ /*byte[] buf1 = {0x40,0x4E,0x54,0x43,0x01,0x00,0x00,0x00,0x7B,0x00,0x00,0x00,0x13,0x00,0x44,0x34,0x2A,0x3E,0x53,0x3A,0x38,0x36,0x31,0x37,0x38,0x35,0x30,0x30,0x35,0x32,0x30,0x35,0x30,0x37,0x39};
+ assertNull(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, buf1)));
+
+ byte[] buf2 = {0x40,0x4E,0x54,0x43,0x01,0x00,0x00,0x00,0x7B,0x00,0x00,0x00,0x5A,0x00,0x50,0x69,0x2A,0x3E,0x41,0x01,0x25,(byte)0xDB,0x0E,0x00,0x00,0x00,0x15,0x11,0x07,0x07,0x11,0x0A,0x0C,0x08,(byte)0x80,0x63,0x00,0x00,(byte)0xAA,0x39,(byte)0xA2,0x38,0x16,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0C,0x11,0x07,0x08,0x11,0x0A,0x0C,(byte)0xB3,(byte)0x89,(byte)0x79,0x3F,0x1A,(byte)0xEF,0x26,0x3F,0x00,0x00,0x00,0x00,0x12,0x00,0x34,(byte)0xF5,0x16,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,(byte)0xFA,(byte)0xFF,0x00,0x00,0x00,(byte)0xFA,(byte)0xFF,0x00,0x00,0x00,(byte)0xFA,(byte)0xFF,(byte)0x80,(byte)0x80,(byte)0x80,(byte)0x80};
+ assertNotNull(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, buf2)));
+
+ byte[] buf3 = {0x40,0x4E,0x54,0x43,0x01,0x00,0x00,0x00,0x7B,0x00,0x00,0x00,0x13,0x00,0x47,0x37,0x2A,0x3E,0x53,0x3A,0x38,0x36,0x31,0x37,0x38,0x35,0x30,0x30,0x35,0x31,0x32,0x36,0x30,0x36,0x39};
+ assertNull(decoder.decode(null, null, ChannelBuffers.wrappedBuffer(ByteOrder.LITTLE_ENDIAN, buf3)));*/
+
+ }
+
+}