aboutsummaryrefslogtreecommitdiff
path: root/src/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/net')
-rw-r--r--src/net/sourceforge/opentracking/Daemon.java180
-rw-r--r--src/net/sourceforge/opentracking/DataManager.java36
-rw-r--r--src/net/sourceforge/opentracking/Device.java49
-rw-r--r--src/net/sourceforge/opentracking/Position.java129
-rw-r--r--src/net/sourceforge/opentracking/TrackerEventHandler.java63
-rw-r--r--src/net/sourceforge/opentracking/TrackerServer.java114
-rw-r--r--src/net/sourceforge/opentracking/helper/NamedParameterStatement.java181
-rw-r--r--src/net/sourceforge/opentracking/protocol/xexun/XexunFrameDecoder.java105
-rw-r--r--src/net/sourceforge/opentracking/protocol/xexun/XexunProtocolDecoder.java153
9 files changed, 1010 insertions, 0 deletions
diff --git a/src/net/sourceforge/opentracking/Daemon.java b/src/net/sourceforge/opentracking/Daemon.java
new file mode 100644
index 000000000..aff742fa0
--- /dev/null
+++ b/src/net/sourceforge/opentracking/Daemon.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2010 Anton Tananaev (anton@tananaev.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 net.sourceforge.opentracking;
+
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Properties;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.sql.DriverManager;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import net.sourceforge.opentracking.helper.NamedParameterStatement;
+import org.jboss.netty.handler.codec.frame.FixedLengthFrameDecoder;
+import org.jboss.netty.handler.codec.string.StringDecoder;
+import net.sourceforge.opentracking.protocol.xexun.XexunFrameDecoder;
+import net.sourceforge.opentracking.protocol.xexun.XexunProtocolDecoder;
+
+/**
+ * Daemon
+ */
+public class Daemon implements DataManager {
+
+ /**
+ * Server list
+ */
+ private List serverList;
+
+ public Daemon() {
+ serverList = new LinkedList();
+ }
+
+ /**
+ * Init
+ */
+ public void init(String[] arguments)
+ throws IOException, ClassNotFoundException, SQLException {
+
+ // Load properties
+ Properties properties = new Properties();
+ properties.loadFromXML(new FileInputStream(arguments[0]));
+ //properties.loadFromXML(Daemon.class.getResourceAsStream("/configuration.xml"));
+
+ initDatabase(properties);
+ initXexunServer(properties);
+ }
+
+ /**
+ * Database connection
+ */
+ private Connection connection;
+
+ private NamedParameterStatement selectDevice;
+
+ private NamedParameterStatement insertPosition;
+
+ /**
+ * Init database
+ */
+ private void initDatabase(Properties properties)
+ throws ClassNotFoundException, SQLException {
+
+ // Load driver
+ String driver = properties.getProperty("database.driver");
+ if (driver != null) {
+ Class.forName(driver);
+ }
+
+ // Connect database
+ String url = properties.getProperty("database.url");
+ String user = properties.getProperty("database.user");
+ String password = properties.getProperty("database.password");
+
+ if (user != null && password != null) {
+ connection = DriverManager.getConnection(url, user, password);
+ } else {
+ connection = DriverManager.getConnection(url);
+ }
+
+ // Init statements
+ String selectDeviceQuery = properties.getProperty("database.selectDevice");
+ if (selectDeviceQuery != null) {
+ selectDevice = new NamedParameterStatement(connection, selectDeviceQuery);
+ }
+
+ String insertPositionQuery = properties.getProperty("database.insertPosition");
+ if (insertPositionQuery != null) {
+ insertPosition = new NamedParameterStatement(connection, insertPositionQuery);
+ }
+ }
+
+ public synchronized List readDevice() throws SQLException {
+
+ List deviceList = new LinkedList();
+
+ ResultSet result = selectDevice.executeQuery();
+ while (result.next()) {
+ Device device = new Device();
+ device.setId(result.getLong("id"));
+ device.setImei(result.getString("imei"));
+ deviceList.add(device);
+ }
+
+ return deviceList;
+ }
+
+ public synchronized void writePosition(Position position) throws SQLException {
+
+ insertPosition.setInt("device_id", position.getDeviceId().intValue());
+ insertPosition.setTimestamp("time", position.getTime());
+ insertPosition.setBoolean("valid", position.getValid());
+ insertPosition.setDouble("latitude", position.getLatitude());
+ insertPosition.setDouble("longitude", position.getLongitude());
+ insertPosition.setDouble("speed", position.getSpeed());
+ insertPosition.setDouble("course", position.getCourse());
+
+ insertPosition.executeUpdate();
+ }
+
+ /**
+ * Init Xexun server
+ */
+ public void initXexunServer(Properties properties) throws SQLException {
+
+ boolean enable = Boolean.valueOf(properties.getProperty("xexun.enable"));
+ if (enable) {
+
+ TrackerServer server = new TrackerServer(
+ Integer.valueOf(properties.getProperty("xexun.port")));
+
+ server.getPipeline().addLast("frameDecoder", new XexunFrameDecoder());
+ server.getPipeline().addLast("stringDecoder", new StringDecoder());
+ server.getPipeline().addLast("objectDecoder", new XexunProtocolDecoder(this));
+
+ server.getPipeline().addLast("handler", new TrackerEventHandler(this));
+
+ serverList.add(server);
+ }
+ }
+
+ /**
+ * Start
+ */
+ public void start() {
+ for (Object server: serverList) {
+ ((TrackerServer) server).start();
+ }
+ }
+
+ /**
+ * Stop
+ */
+ public void stop() {
+ for (Object server: serverList) {
+ ((TrackerServer) server).stop();
+ }
+ }
+
+ /**
+ * Destroy
+ */
+ public void destroy() {
+ serverList.clear();
+ }
+
+}
diff --git a/src/net/sourceforge/opentracking/DataManager.java b/src/net/sourceforge/opentracking/DataManager.java
new file mode 100644
index 000000000..ed4523d9f
--- /dev/null
+++ b/src/net/sourceforge/opentracking/DataManager.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2010 Anton Tananaev (anton@tananaev.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 net.sourceforge.opentracking;
+
+import java.util.List;
+import java.sql.SQLException;
+
+/**
+ * Data manager
+ */
+public interface DataManager {
+
+ /**
+ * Get device list
+ */
+ public List readDevice() throws SQLException;
+
+ /**
+ * Write position
+ */
+ public void writePosition(Position position) throws SQLException;
+
+}
diff --git a/src/net/sourceforge/opentracking/Device.java b/src/net/sourceforge/opentracking/Device.java
new file mode 100644
index 000000000..9c949b6ca
--- /dev/null
+++ b/src/net/sourceforge/opentracking/Device.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2010 Anton Tananaev (anton@tananaev.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 net.sourceforge.opentracking;
+
+/**
+ * Device
+ */
+public class Device {
+
+ /**
+ * Id
+ */
+ private Long id;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long newId) {
+ id = newId;
+ }
+
+ /**
+ * International mobile equipment identity (IMEI)
+ */
+ private String imei;
+
+ public String getImei() {
+ return imei;
+ }
+
+ public void setImei(String newImei) {
+ imei = newImei;
+ }
+
+}
diff --git a/src/net/sourceforge/opentracking/Position.java b/src/net/sourceforge/opentracking/Position.java
new file mode 100644
index 000000000..0e20c22c1
--- /dev/null
+++ b/src/net/sourceforge/opentracking/Position.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2010 Anton Tananaev (anton@tananaev.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 net.sourceforge.opentracking;
+
+import java.util.Date;
+
+/**
+ * Position information
+ */
+public class Position {
+
+ /**
+ * Id
+ */
+ private Long id;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long newId) {
+ id = newId;
+ }
+
+ /**
+ * Device
+ */
+ private Long deviceId;
+
+ public Long getDeviceId() {
+ return deviceId;
+ }
+
+ public void setDeviceId(Long newDeviceId) {
+ deviceId = newDeviceId;
+ }
+
+ /**
+ * Time (UTC)
+ */
+ private Date time;
+
+ public Date getTime() {
+ return time;
+ }
+
+ public void setTime(Date newTime) {
+ time = newTime;
+ }
+
+ /**
+ * Validity flag
+ */
+ private Boolean valid;
+
+ public Boolean getValid() {
+ return valid;
+ }
+
+ public void setValid(Boolean newValid) {
+ valid = newValid;
+ }
+
+ /**
+ * Latitude
+ */
+ private Double latitude;
+
+ public Double getLatitude() {
+ return latitude;
+ }
+
+ public void setLatitude(Double newLatitude) {
+ latitude = newLatitude;
+ }
+
+ /**
+ * Longitude
+ */
+ private Double longitude;
+
+ public Double getLongitude() {
+ return longitude;
+ }
+
+ public void setLongitude(Double newLongitude) {
+ longitude = newLongitude;
+ }
+
+ /**
+ * Speed (knots)
+ */
+ private Double speed;
+
+ public Double getSpeed() {
+ return speed;
+ }
+
+ public void setSpeed(Double newSpeed) {
+ speed = newSpeed;
+ }
+
+ /**
+ * Course
+ */
+ private Double course;
+
+ public Double getCourse() {
+ return course;
+ }
+
+ public void setCourse(Double newCourse) {
+ course = newCourse;
+ }
+
+}
diff --git a/src/net/sourceforge/opentracking/TrackerEventHandler.java b/src/net/sourceforge/opentracking/TrackerEventHandler.java
new file mode 100644
index 000000000..845ba650c
--- /dev/null
+++ b/src/net/sourceforge/opentracking/TrackerEventHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010 Anton Tananaev (anton@tananaev.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 net.sourceforge.opentracking;
+
+import org.jboss.netty.channel.SimpleChannelHandler;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.ChannelPipelineCoverage;
+import java.sql.SQLException;
+
+/**
+ * Tracker message handler
+ */
+@ChannelPipelineCoverage("all")
+public class TrackerEventHandler extends SimpleChannelHandler {
+
+ /**
+ * Data manager
+ */
+ private DataManager dataManager;
+
+ TrackerEventHandler(DataManager newDataManager) {
+ super();
+ dataManager = newDataManager;
+ }
+
+ @Override
+ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
+
+ System.out.println("message recieved");
+
+ if (e.getMessage() instanceof Position) {
+
+ // Write position to database
+ try {
+ dataManager.writePosition((Position) e.getMessage());
+ } catch (Exception error) {
+ System.out.println("write error");
+ }
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
+ System.out.println("error: " + e.getCause().getMessage());
+ e.getChannel().close();
+ }
+
+}
diff --git a/src/net/sourceforge/opentracking/TrackerServer.java b/src/net/sourceforge/opentracking/TrackerServer.java
new file mode 100644
index 000000000..76e5b1bbf
--- /dev/null
+++ b/src/net/sourceforge/opentracking/TrackerServer.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2010 Anton Tananaev (anton@tananaev.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 net.sourceforge.opentracking;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.Executors;
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
+import org.jboss.netty.channel.group.ChannelGroup;
+import org.jboss.netty.channel.group.DefaultChannelGroup;
+import org.jboss.netty.channel.group.ChannelGroupFuture;
+import org.jboss.netty.channel.SimpleChannelHandler;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.ChannelPipelineCoverage;
+
+/**
+ * Tracker server
+ */
+public class TrackerServer extends ServerBootstrap {
+
+ /**
+ * Open channel handler
+ */
+ @ChannelPipelineCoverage("all")
+ protected class OpenChannelHandler extends SimpleChannelHandler {
+
+ TrackerServer server;
+
+ public OpenChannelHandler(TrackerServer newServer) {
+ server = newServer;
+ }
+
+ @Override
+ public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
+ System.out.println("connected: " + e.getChannel().getRemoteAddress());
+ server.getChannelGroup().add(e.getChannel());
+ }
+ }
+
+ /**
+ * Initialization
+ */
+ private void init(Integer port, Integer threadPoolSize) {
+
+ setPort(port);
+
+ // Create channel factory
+ setFactory(new NioServerSocketChannelFactory(
+ Executors.newFixedThreadPool(threadPoolSize),
+ Executors.newFixedThreadPool(threadPoolSize)));
+
+ // Add open channel handler
+ getPipeline().addLast("openHandler", new OpenChannelHandler(this));
+ }
+
+ public TrackerServer(Integer port) {
+ init(port, 1);
+ }
+
+ /**
+ * Server port
+ */
+ private Integer port;
+
+ public Integer getPort() {
+ return port;
+ }
+
+ private void setPort(Integer newPort) {
+ port = newPort;
+ }
+
+ /**
+ * Opened channels
+ */
+ private ChannelGroup allChannels = new DefaultChannelGroup();
+
+ public ChannelGroup getChannelGroup() {
+ return allChannels;
+ }
+
+ /**
+ * Start server
+ */
+ public void start() {
+ Channel channel = bind(new InetSocketAddress(getPort()));
+ getChannelGroup().add(channel);
+ }
+
+ /**
+ * Stop server
+ */
+ public void stop() {
+ ChannelGroupFuture future = getChannelGroup().close();
+ future.awaitUninterruptibly();
+ getFactory().releaseExternalResources();
+ }
+
+}
diff --git a/src/net/sourceforge/opentracking/helper/NamedParameterStatement.java b/src/net/sourceforge/opentracking/helper/NamedParameterStatement.java
new file mode 100644
index 000000000..2480a15e2
--- /dev/null
+++ b/src/net/sourceforge/opentracking/helper/NamedParameterStatement.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2010 Anton Tananaev (anton@tananaev.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 net.sourceforge.opentracking.helper;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Date;
+
+public class NamedParameterStatement {
+
+ /**
+ * Native statement
+ */
+ private final PreparedStatement statement;
+
+ /**
+ * Index mapping
+ */
+ private final Map indexMap;
+
+ /**
+ * Initialize statement
+ */
+ public NamedParameterStatement(Connection connection, String query)
+ throws SQLException {
+
+ indexMap = new HashMap();
+ String parsedQuery = parse(query, indexMap);
+ statement = connection.prepareStatement(parsedQuery);
+ }
+
+ /**
+ * Parse query
+ */
+ static final String parse(String query, Map paramMap) {
+
+ int length = query.length();
+ StringBuffer parsedQuery = new StringBuffer(length);
+ boolean inSingleQuote = false;
+ boolean inDoubleQuote = false;
+ int index = 1;
+
+ for(int i = 0; i < length; i++) {
+
+ char c = query.charAt(i);
+
+ // String end
+ if (inSingleQuote) {
+ if (c == '\'') inSingleQuote = false;
+ } else if (inDoubleQuote) {
+ if (c == '"') inDoubleQuote = false;
+ } else {
+
+ // String begin
+ if (c == '\'') {
+ inSingleQuote = true;
+ } else if (c == '"') {
+ inDoubleQuote = true;
+ } else if (c == ':' && i + 1 < length &&
+ Character.isJavaIdentifierStart(query.charAt(i + 1))) {
+
+ // Identifier name
+ int j = i + 2;
+ while (j < length && Character.isJavaIdentifierPart(query.charAt(j))) j++;
+
+ String name = query.substring(i + 1, j);
+ c = '?';
+ i += name.length();
+
+ // Add to list
+ List indexList = (List) paramMap.get(name);
+ if (indexList == null) {
+ indexList = new LinkedList();
+ paramMap.put(name, indexList);
+ }
+ indexList.add(new Integer(index));
+
+ index++;
+ }
+ }
+
+ parsedQuery.append(c);
+ }
+
+ return parsedQuery.toString();
+ }
+
+ /**
+ * Execute query with result
+ */
+ public ResultSet executeQuery() throws SQLException {
+ return statement.executeQuery();
+ }
+
+
+ /**
+ * Executes query without result
+ */
+ public int executeUpdate() throws SQLException {
+ return statement.executeUpdate();
+ }
+
+ /**
+ * Immediately closes the statement
+ */
+ public void close() throws SQLException {
+ statement.close();
+ }
+
+ /**
+ * Set a parameter
+ */
+ /*public void setObject(String name, Object value) throws SQLException {
+
+ List indexList = (List) indexMap.get(name);
+ if (indexList != null) {
+ for (Object index: indexList) {
+ statement.setObject((Integer) index, value);
+ }
+ }
+ else {
+ throw new IllegalArgumentException();
+ }
+ }*/
+
+ public void setInt(String name, int value) throws SQLException {
+
+ List indexList = (List) indexMap.get(name);
+ for (Object index: indexList) {
+ statement.setInt((Integer) index, value);
+ }
+ }
+
+ public void setBoolean(String name, boolean value) throws SQLException {
+
+ List indexList = (List) indexMap.get(name);
+ for (Object index: indexList) {
+ statement.setBoolean((Integer) index, value);
+ }
+ }
+
+ public void setDouble(String name, double value) throws SQLException {
+
+ List indexList = (List) indexMap.get(name);
+ for (Object index: indexList) {
+ statement.setDouble((Integer) index, value);
+ }
+ }
+
+ public void setTimestamp(String name, Date value) throws SQLException {
+
+ List indexList = (List) indexMap.get(name);
+ for (Object index: indexList) {
+ statement.setTimestamp(
+ (Integer) index,
+ new Timestamp(value.getTime()));
+ }
+ }
+
+}
diff --git a/src/net/sourceforge/opentracking/protocol/xexun/XexunFrameDecoder.java b/src/net/sourceforge/opentracking/protocol/xexun/XexunFrameDecoder.java
new file mode 100644
index 000000000..338c7bec9
--- /dev/null
+++ b/src/net/sourceforge/opentracking/protocol/xexun/XexunFrameDecoder.java
@@ -0,0 +1,105 @@
+/*
+ * Минимум - 100
+ * Начало - (10 цифр) + ','
+ * Длина 16 x ','
+ */
+
+package net.sourceforge.opentracking.protocol.xexun;
+
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.buffer.ChannelBuffer;
+
+/**
+ *
+ */
+public class XexunFrameDecoder extends FrameDecoder {
+
+ protected Object decode(
+ ChannelHandlerContext ctx,
+ Channel channel,
+ ChannelBuffer buf) throws Exception {
+
+ System.out.println("read: " + buf.readableBytes());
+
+ // Check minimum length
+ int length = buf.readableBytes();
+ if (length < 100) {
+ return null;
+ }
+
+ // Find identifier
+ int index = 0;
+ int countDigit = 0;
+ for (; index < length; index++) {
+
+ // Check byte
+ char c = (char) buf.getByte(index);
+ if (Character.isDigit(c)) {
+ countDigit++;
+ }
+
+ // Check count
+ if (countDigit == 10) {
+ break;
+ }
+ }
+
+ if (countDigit < 10) {
+ return null;
+ }
+
+ // Find begin
+ int beginIndex = 0;
+ for (; index < length; index++) {
+
+ char c = (char) buf.getByte(index);
+ if (c == ',') {
+ beginIndex = index;
+ break;
+ }
+ }
+
+ if (beginIndex == 0) {
+ return null;
+ }
+
+ // Find imei
+ int imeiIndex = 0;
+ for (; index < length; index++) {
+
+ char c = (char) buf.getByte(index);
+ if (c == ':') {
+ imeiIndex = index;
+ break;
+ }
+ }
+
+ if (imeiIndex == 0) {
+ return null;
+ }
+
+ // Find end
+ int endIndex = 0;
+ for (; index < length; index++) {
+
+ char c = (char) buf.getByte(index);
+ if (c == ',') {
+ endIndex = index;
+ break;
+ }
+ }
+
+ if (endIndex == 0) {
+ return null;
+ }
+
+ // Read buffer
+ buf.skipBytes(beginIndex);
+ ChannelBuffer frame = buf.readBytes(endIndex - beginIndex + 1);
+
+ return frame;
+ }
+
+}
diff --git a/src/net/sourceforge/opentracking/protocol/xexun/XexunProtocolDecoder.java b/src/net/sourceforge/opentracking/protocol/xexun/XexunProtocolDecoder.java
new file mode 100644
index 000000000..cfd88fe6e
--- /dev/null
+++ b/src/net/sourceforge/opentracking/protocol/xexun/XexunProtocolDecoder.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2010 Anton Tananaev (anton@tananaev.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 net.sourceforge.opentracking.protocol.xexun;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.text.ParseException;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.oneone.OneToOneDecoder;
+import org.jboss.netty.channel.ChannelPipelineCoverage;
+import net.sourceforge.opentracking.Position;
+import net.sourceforge.opentracking.Device;
+import net.sourceforge.opentracking.DataManager;
+import java.sql.SQLException;
+
+/**
+ * Xexun tracker protocol decoder
+ */
+@ChannelPipelineCoverage("all")
+public class XexunProtocolDecoder extends OneToOneDecoder {
+
+ /**
+ * Init device table
+ */
+ public XexunProtocolDecoder(DataManager dataManager) throws SQLException {
+
+ // Create map
+ setDevices(new HashMap());
+
+ if (dataManager != null) {
+ List deviceList = dataManager.readDevice();
+
+ for (Object device: deviceList) {
+ getDevices().put(((Device) device).getImei(), device);
+ }
+ }
+ }
+
+ /**
+ * Devices
+ */
+ private Map devices;
+
+ public Map getDevices() {
+ return devices;
+ }
+
+ private void setDevices(Map newDevices) {
+ devices = newDevices;
+ }
+
+ /**
+ * Regular expressions pattern
+ */
+ static private Pattern pattern = Pattern.compile(
+ "," + // Old: [\\d]+,
+ "\\+?[\\d]+," + // Trusted phone number
+ "GPRMC," +
+ "([\\d]{2})([\\d]{2})([\\d]{2}).([\\d]{3})," + // Time (HHMMSS.SSS)
+ "([AV])," + // Validity
+ "([\\d]{2})([\\d]{2}.[\\d]{4})," + // Latitude (DDMM.MMMM)
+ "([NS])," +
+ "([\\d]{3})([\\d]{2}.[\\d]{4})," + // Longitude (DDDMM.MMMM)
+ "([EW])," +
+ "([\\d]+.[\\d]{2})," + // Speed
+ "([\\d]+.[\\d]{2})," + // Course
+ "([\\d]{2})([\\d]{2})([\\d]{2})," + // Date (DDMMYY)
+ ".*imei:" +
+ "([\\d]+)," + // IMEI
+ ".*");
+
+ /**
+ * Decode message
+ */
+ protected Object decode(
+ ChannelHandlerContext ctx, Channel channel, Object msg)
+ throws ParseException {
+
+ // Parse message
+ String sentence = (String) msg;
+ System.out.println("parse: " + sentence);
+ Matcher parser = pattern.matcher(sentence);
+ if (!parser.matches()) {
+ throw new ParseException(null, 0);
+ }
+
+ // Create new position
+ Position position = new Position();
+
+ Integer index = 1;
+
+ // Time
+ Calendar time = new GregorianCalendar();
+ time.clear();
+ time.set(Calendar.HOUR, Integer.valueOf(parser.group(index++)));
+ time.set(Calendar.MINUTE, Integer.valueOf(parser.group(index++)));
+ time.set(Calendar.SECOND, Integer.valueOf(parser.group(index++)));
+ time.set(Calendar.MILLISECOND, Integer.valueOf(parser.group(index++)));
+
+ // Validity
+ position.setValid(parser.group(index++).compareTo("A") == 0 ? true : false);
+
+ // Latitude
+ Double latitude = Double.valueOf(parser.group(index++));
+ latitude += Double.valueOf(parser.group(index++)) / 60;
+ if (parser.group(index++).compareTo("S") == 0) latitude = -latitude;
+ position.setLatitude(latitude);
+
+ // Longitude
+ Double lonlitude = Double.valueOf(parser.group(index++));
+ lonlitude += Double.valueOf(parser.group(index++)) / 60;
+ if (parser.group(index++).compareTo("W") == 0) lonlitude = -lonlitude;
+ position.setLongitude(lonlitude);
+
+ // Speed and course
+ position.setSpeed(Double.valueOf(parser.group(index++)));
+ position.setCourse(Double.valueOf(parser.group(index++)));
+
+ // Date
+ time.set(Calendar.DAY_OF_MONTH, Integer.valueOf(parser.group(index++)));
+ time.set(Calendar.MONTH, Integer.valueOf(parser.group(index++)) - 1);
+ time.set(Calendar.YEAR, 2000 + Integer.valueOf(parser.group(index++)));
+ position.setTime(time.getTime());
+
+ // Get device by IMEI
+ String imei = parser.group(index++);
+ position.setDeviceId(((Device) getDevices().get(imei)).getId());
+
+ return position;
+ }
+
+}