From 8d49ced822a1ae5c8b3f065178116e95a4f55c85 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Tue, 4 Dec 2012 21:24:41 +1300 Subject: Added Navis protocol (fix #99) --- src/org/traccar/Server.java | 27 +- src/org/traccar/TrackerEventHandler.java | 58 ++-- src/org/traccar/protocol/NavisProtocolDecoder.java | 348 +++++++++++++++++++++ 3 files changed, 406 insertions(+), 27 deletions(-) create mode 100644 src/org/traccar/protocol/NavisProtocolDecoder.java (limited to 'src') diff --git a/src/org/traccar/Server.java b/src/org/traccar/Server.java index 38f0a1388..b80d22c8f 100644 --- a/src/org/traccar/Server.java +++ b/src/org/traccar/Server.java @@ -17,6 +17,7 @@ package org.traccar; import java.io.FileInputStream; import java.io.IOException; +import java.nio.ByteOrder; import java.sql.SQLException; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -105,6 +106,7 @@ public class Server { initV680Server(properties); initPt502Server(properties); initTr20Server(properties); + initNavisServer(properties); // Initialize web server if (Boolean.valueOf(properties.getProperty("http.enable"))) { @@ -517,7 +519,7 @@ public class Server { if (isProtocolEnabled(properties, protocol)) { TrackerServer server = new TrackerServer(getProtocolPort(properties, protocol)); - server.setEndianness(java.nio.ByteOrder.LITTLE_ENDIAN); + server.setEndianness(ByteOrder.LITTLE_ENDIAN); final Integer resetDelay = getProtocolResetDelay(properties, protocol); server.setPipelineFactory(new GenericPipelineFactory(server, dataManager, isLoggerEnabled(), resetDelay, geocoder) { @@ -679,4 +681,27 @@ public class Server { serverList.add(server); } } + + /** + * Init Navis server + */ + private void initNavisServer(Properties properties) throws SQLException { + + String protocol = "navis"; + if (isProtocolEnabled(properties, protocol)) { + + TrackerServer server = new TrackerServer(getProtocolPort(properties, protocol)); + server.setEndianness(ByteOrder.LITTLE_ENDIAN); + final Integer resetDelay = getProtocolResetDelay(properties, protocol); + + server.setPipelineFactory(new GenericPipelineFactory(server, dataManager, isLoggerEnabled(), resetDelay, geocoder) { + protected void addSpecificHandlers(ChannelPipeline pipeline) { + pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(4 * 1024, 12, 2, 2, 0)); + pipeline.addLast("objectDecoder", new NavisProtocolDecoder(getDataManager())); + } + }); + + serverList.add(server); + } + } } diff --git a/src/org/traccar/TrackerEventHandler.java b/src/org/traccar/TrackerEventHandler.java index 6483e52e7..11cf41fad 100644 --- a/src/org/traccar/TrackerEventHandler.java +++ b/src/org/traccar/TrackerEventHandler.java @@ -15,6 +15,7 @@ */ package org.traccar; +import java.util.List; import org.jboss.netty.channel.*; import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler; import org.jboss.netty.handler.timeout.IdleStateEvent; @@ -37,36 +38,41 @@ public class TrackerEventHandler extends IdleStateAwareChannelHandler { super(); dataManager = newDataManager; } + + private void processSinglePosition(Position position) { + if (position == null) { + Log.info("null message"); + } else { + Log.info( + "id: " + position.getId() + + ", deviceId: " + position.getDeviceId() + + ", valid: " + position.getValid() + + ", time: " + position.getTime() + + ", latitude: " + position.getLatitude() + + ", longitude: " + position.getLongitude() + + ", altitude: " + position.getAltitude() + + ", speed: " + position.getSpeed() + + ", course: " + position.getCourse() + + ", power: " + position.getPower()); + } + + // Write position to database + try { + dataManager.addPosition(position); + } catch (Exception error) { + Log.info("Exception during query execution"); + Log.warning(error.getMessage()); + } + } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { - if (e.getMessage() instanceof Position) { - - Position position = (Position) e.getMessage(); - - if (position == null) { - Log.info("null message"); - } else { - Log.info( - "id: " + position.getId() + - ", deviceId: " + position.getDeviceId() + - ", valid: " + position.getValid() + - ", time: " + position.getTime() + - ", latitude: " + position.getLatitude() + - ", longitude: " + position.getLongitude() + - ", altitude: " + position.getAltitude() + - ", speed: " + position.getSpeed() + - ", course: " + position.getCourse() + - ", power: " + position.getPower()); - } - - // Write position to database - try { - dataManager.addPosition(position); - } catch (Exception error) { - Log.info("Exception during query execution"); - Log.warning(error.getMessage()); + processSinglePosition((Position) e.getMessage()); + } else if (e.getMessage() instanceof List) { + List positions = (List) e.getMessage(); + for (Position position : positions) { + processSinglePosition(position); } } } diff --git a/src/org/traccar/protocol/NavisProtocolDecoder.java b/src/org/traccar/protocol/NavisProtocolDecoder.java new file mode 100644 index 000000000..1b3cd80f7 --- /dev/null +++ b/src/org/traccar/protocol/NavisProtocolDecoder.java @@ -0,0 +1,348 @@ +/* + * Copyright 2012 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.util.Calendar; +import java.util.LinkedList; +import java.util.List; +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.GenericProtocolDecoder; +import org.traccar.helper.Log; +import org.traccar.model.DataManager; +import org.traccar.model.Position; + +/** + * Navis protocol decoder + */ +public class NavisProtocolDecoder extends GenericProtocolDecoder { + + private String prefix; + private long deviceId, serverId; + + private static final Charset charset = Charset.defaultCharset(); + + private String imei; + private Long databaseDeviceId; + + /** + * Initialize + */ + public NavisProtocolDecoder(DataManager dataManager) { + super(dataManager); + } + + // Format types + public static final int F10 = 0x01; + public static final int F20 = 0x02; + public static final int F30 = 0x03; + public static final int F40 = 0x04; + public static final int F50 = 0x05; + public static final int F51 = 0x15; + public static final int F52 = 0x25; + + private static boolean isFormat(int type, int... types) { + for (int i : types) { + if (type == i) { + return true; + } + } + return false; + } + + private Position parsePosition(ChannelBuffer buf) { + Position position = new Position(); + StringBuilder extendedInfo = new StringBuilder("navis"); + + position.setDeviceId(databaseDeviceId); + position.setAltitude(0.0); + + // Format type + int format; + if (buf.getUnsignedByte(buf.readerIndex()) == 0) { + format = buf.readUnsignedShort(); + } else { + format = buf.readUnsignedByte(); + } + extendedInfo.append(""); + extendedInfo.append(format); + extendedInfo.append(""); + + position.setId(buf.readUnsignedInt()); // sequence number + + // Event type + extendedInfo.append(""); + extendedInfo.append(buf.readUnsignedShort()); + extendedInfo.append(""); + + // Event time + Calendar time = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + time.clear(); + time.set(Calendar.HOUR, buf.readUnsignedByte()); + time.set(Calendar.MINUTE, buf.readUnsignedByte()); + time.set(Calendar.SECOND, buf.readUnsignedByte()); + time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); + time.set(Calendar.MONTH, buf.readUnsignedByte()); + time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); + extendedInfo.append(""); + + // Alarm status + extendedInfo.append(""); + extendedInfo.append(buf.readUnsignedByte()); + extendedInfo.append(""); + + // Modules status + extendedInfo.append(""); + extendedInfo.append(buf.readUnsignedByte()); + extendedInfo.append(""); + + // GSM signal + extendedInfo.append(""); + extendedInfo.append(buf.readUnsignedByte()); + extendedInfo.append(""); + + // Output + extendedInfo.append(""); + if (isFormat(format, F10, F20, F30)) { + extendedInfo.append(buf.readUnsignedShort()); + } else if (isFormat(format, F40, F50, F51, F52)) { + extendedInfo.append(buf.readUnsignedByte()); + } + extendedInfo.append(""); + + // Input + extendedInfo.append(""); + if (isFormat(format, F10, F20, F30, F40)) { + extendedInfo.append(buf.readUnsignedShort()); + } else if (isFormat(format, F50, F51, F52)) { + extendedInfo.append(buf.readUnsignedByte()); + } + extendedInfo.append(""); + + position.setPower(buf.readUnsignedShort() / 1000.0); // power + + // Battery power + extendedInfo.append(""); + extendedInfo.append(buf.readUnsignedShort()); + extendedInfo.append(""); + + // Temperature + if (isFormat(format, F10, F20, F30)) { + extendedInfo.append(""); + extendedInfo.append(buf.readShort()); + extendedInfo.append(""); + } + + if (isFormat(format, F10, F20, F50, F52)) { + // ADC 1 + extendedInfo.append(""); + extendedInfo.append(buf.readUnsignedShort()); + extendedInfo.append(""); + + // ADC 2 + extendedInfo.append(""); + extendedInfo.append(buf.readUnsignedShort()); + extendedInfo.append(""); + } + + if (isFormat(format, F20, F50, F51, F52)) { + // Impulse counters + buf.readUnsignedInt(); + buf.readUnsignedInt(); + } + + if (isFormat(format, F20, F50, F51, F52)) { + // Validity + int locationStatus = buf.readUnsignedByte(); + position.setValid((locationStatus & 0x02) == 0x02); + + // Location time + time.clear(); + time.set(Calendar.HOUR, buf.readUnsignedByte()); + time.set(Calendar.MINUTE, buf.readUnsignedByte()); + time.set(Calendar.SECOND, buf.readUnsignedByte()); + time.set(Calendar.DAY_OF_MONTH, buf.readUnsignedByte()); + time.set(Calendar.MONTH, buf.readUnsignedByte()); + time.set(Calendar.YEAR, 2000 + buf.readUnsignedByte()); + position.setTime(time.getTime()); + + // Location data + position.setLatitude(buf.readFloat() / Math.PI * 180); + position.setLongitude(buf.readFloat() / Math.PI * 180); + position.setSpeed((double) buf.readFloat()); + position.setCourse((double) buf.readUnsignedShort()); + + // Milage + extendedInfo.append(""); + extendedInfo.append(buf.readFloat()); + extendedInfo.append(""); + + // Last segment + extendedInfo.append(""); + extendedInfo.append(buf.readFloat()); + extendedInfo.append(""); + + // Segment times + buf.readUnsignedShort(); + buf.readUnsignedShort(); + } + + if (isFormat(format, F51, F52)) { + // Other stuff + buf.readUnsignedShort(); + buf.readByte(); + buf.readUnsignedShort(); + buf.readUnsignedShort(); + buf.readByte(); + buf.readUnsignedShort(); + buf.readUnsignedShort(); + buf.readByte(); + buf.readUnsignedShort(); + } + + if (isFormat(format, F40, F52)) { + // Four temperature sensors + buf.readByte(); + buf.readByte(); + buf.readByte(); + buf.readByte(); + } + + // Extended info + position.setExtendedInfo(extendedInfo.toString()); + + return position; + } + + private Object processSingle(Channel channel, ChannelBuffer buf) { + Position position = parsePosition(buf); + + ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 8); + response.writeBytes(ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, "* positions = new LinkedList(); + int count = buf.readUnsignedByte(); + + for (int i = 0; i < count; i++) { + Position position = parsePosition(buf); + if (position.getValid() != null) { + positions.add(position); + } + } + + ChannelBuffer response = ChannelBuffers.dynamicBuffer(ByteOrder.LITTLE_ENDIAN, 8); + response.writeBytes(ChannelBuffers.copiedBuffer(ByteOrder.LITTLE_ENDIAN, "*T")) { + return processSingle(channel, buf); + } else if (type.equals("*>A")) { + return processArray(channel, buf); + } else if (type.equals("*>S")) { + return processHandshake(channel, buf); + } + + return null; + } + +} -- cgit v1.2.3