From aa7f0f856ebe5dee7e6d6639db468e3095a5b84b Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Thu, 3 Dec 2015 13:35:18 +1300 Subject: Implement command support for Meiligao --- src/org/traccar/model/Command.java | 3 + src/org/traccar/protocol/Gt06ProtocolEncoder.java | 1 - src/org/traccar/protocol/MeiligaoProtocol.java | 5 + .../traccar/protocol/MeiligaoProtocolEncoder.java | 114 +++++++++++++++++++++ test/org/traccar/ProtocolEncoderTest.java | 52 ++++++++++ .../protocol/MeiligaoProtocolEncoderTest.java | 41 ++++++++ 6 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/org/traccar/protocol/MeiligaoProtocolEncoder.java create mode 100644 test/org/traccar/ProtocolEncoderTest.java create mode 100644 test/org/traccar/protocol/MeiligaoProtocolEncoderTest.java diff --git a/src/org/traccar/model/Command.java b/src/org/traccar/model/Command.java index abca811a2..fb3a37795 100644 --- a/src/org/traccar/model/Command.java +++ b/src/org/traccar/model/Command.java @@ -26,11 +26,14 @@ public class Command extends Extensible implements Factory { public static final String TYPE_ALARM_DISARM = "alarmDisarm"; public static final String TYPE_SET_TIMEZONE = "setTimezone"; public static final String TYPE_REQUEST_PHOTO = "requestPhoto"; + public static final String TYPE_REBOOT_DEVICE = "rebootDevice"; + public static final String TYPE_MOVEMENT_ALARM = "movementAlarm"; public static final String KEY_UNIQUE_ID = "uniqueId"; public static final String KEY_FREQUENCY = "frequency"; public static final String KEY_TIMEZONE = "timezone"; public static final String KEY_DEVICE_PASSWORD = "devicePassword"; + public static final String KEY_RADIUS = "radius"; @Override public Command create() { diff --git a/src/org/traccar/protocol/Gt06ProtocolEncoder.java b/src/org/traccar/protocol/Gt06ProtocolEncoder.java index 7c9d921dd..6b5f0fc6c 100644 --- a/src/org/traccar/protocol/Gt06ProtocolEncoder.java +++ b/src/org/traccar/protocol/Gt06ProtocolEncoder.java @@ -62,7 +62,6 @@ public class Gt06ProtocolEncoder extends BaseProtocolEncoder { default: Log.warning(new UnsupportedOperationException(command.getType())); break; - } return null; diff --git a/src/org/traccar/protocol/MeiligaoProtocol.java b/src/org/traccar/protocol/MeiligaoProtocol.java index 9e8da7dc2..627e75948 100644 --- a/src/org/traccar/protocol/MeiligaoProtocol.java +++ b/src/org/traccar/protocol/MeiligaoProtocol.java @@ -21,11 +21,14 @@ import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipeline; import org.traccar.BaseProtocol; import org.traccar.TrackerServer; +import org.traccar.model.Command; public class MeiligaoProtocol extends BaseProtocol { public MeiligaoProtocol() { super("meiligao"); + setSupportedCommands( + Command.TYPE_POSITION_SINGLE); } @Override @@ -35,12 +38,14 @@ public class MeiligaoProtocol extends BaseProtocol { protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("frameDecoder", new MeiligaoFrameDecoder()); pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this)); + pipeline.addLast("objectEncoder", new MeiligaoProtocolEncoder()); } }); serverList.add(new TrackerServer(new ConnectionlessBootstrap(), this.getName()) { @Override protected void addSpecificHandlers(ChannelPipeline pipeline) { pipeline.addLast("objectDecoder", new MeiligaoProtocolDecoder(MeiligaoProtocol.this)); + pipeline.addLast("objectEncoder", new MeiligaoProtocolEncoder()); } }); } diff --git a/src/org/traccar/protocol/MeiligaoProtocolEncoder.java b/src/org/traccar/protocol/MeiligaoProtocolEncoder.java new file mode 100644 index 000000000..ae93e8b73 --- /dev/null +++ b/src/org/traccar/protocol/MeiligaoProtocolEncoder.java @@ -0,0 +1,114 @@ +/* + * Copyright 2015 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 org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.traccar.BaseProtocolEncoder; +import org.traccar.helper.Checksum; +import org.traccar.helper.Log; +import org.traccar.model.Command; + +import javax.xml.bind.DatatypeConverter; +import java.nio.charset.Charset; +import java.util.Map; + +public class MeiligaoProtocolEncoder extends BaseProtocolEncoder { + + public static final int MSG_TRACK_ON_DEMAND = 0x4101; + public static final int MSG_TRACK_BY_INTERVAL = 0x4102; + public static final int MSG_MOVEMENT_ALARM = 0x4106; + public static final int MSG_TIME_ZONE = 0x4132; + public static final int MSG_REBOOT_GPS = 0x4902; + + private ChannelBuffer encodeContent(long deviceId, int type, ChannelBuffer content) { + + ChannelBuffer buf = ChannelBuffers.dynamicBuffer(); + + buf.writeByte('@'); + buf.writeByte('@'); + + buf.writeShort(2 + 2 + 7 + 2 + content.readableBytes() + 2 + 2); // message length + + buf.writeBytes(DatatypeConverter.parseHexBinary((getUniqueId(deviceId) + "FFFFFFFFFFFFFF").substring(0, 14))); + + buf.writeShort(type); + + buf.writeBytes(content); + + buf.writeShort(Checksum.crc16(Checksum.CRC16_CCITT_FALSE, buf.toByteBuffer())); + + buf.writeByte('\r'); + buf.writeByte('\n'); + + return buf; + } + + // TODO: remove if not needed + private static int radiusToArea(int radius) { + if (radius == 0) { + return 0x00; + } else if (radius <= 30) { + return 0x01; + } else if (radius <= 50) { + return 0x02; + } else if (radius <= 100) { + return 0x03; + } else if (radius <= 200) { + return 0x04; + } else if (radius <= 300) { + return 0x05; + } else if (radius <= 500) { + return 0x06; + } else if (radius <= 1000) { + return 0x07; + } else if (radius <= 2000) { + return 0x08; + } else { + return 0xff; + } + } + + @Override + protected Object encodeCommand(Command command) { + + ChannelBuffer content = ChannelBuffers.dynamicBuffer(); + Map attributes = command.getAttributes(); + + switch (command.getType()) { + case Command.TYPE_POSITION_SINGLE: + return encodeContent(command.getDeviceId(), MSG_TRACK_ON_DEMAND, content); + case Command.TYPE_POSITION_PERIODIC: + content.writeShort(((Number) attributes.get(Command.KEY_FREQUENCY)).intValue() / 10); + return encodeContent(command.getDeviceId(), MSG_TRACK_BY_INTERVAL, content); + case Command.TYPE_MOVEMENT_ALARM: + content.writeShort(((Number) attributes.get(Command.KEY_RADIUS)).intValue()); + return encodeContent(command.getDeviceId(), MSG_MOVEMENT_ALARM, content); + case Command.TYPE_SET_TIMEZONE: + int offset = ((Number) attributes.get(Command.KEY_TIMEZONE)).intValue() / 60; + content.writeBytes(String.valueOf(offset).getBytes(Charset.defaultCharset())); + return encodeContent(command.getDeviceId(), MSG_TIME_ZONE, content); + case Command.TYPE_REBOOT_DEVICE: + return encodeContent(command.getDeviceId(), MSG_REBOOT_GPS, content); + default: + Log.warning(new UnsupportedOperationException(command.getType())); + break; + } + + return null; + } + +} diff --git a/test/org/traccar/ProtocolEncoderTest.java b/test/org/traccar/ProtocolEncoderTest.java new file mode 100644 index 000000000..e8b5bb4eb --- /dev/null +++ b/test/org/traccar/ProtocolEncoderTest.java @@ -0,0 +1,52 @@ +package org.traccar; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.junit.Assert; +import org.traccar.model.Command; + +import javax.xml.bind.DatatypeConverter; +import java.nio.ByteOrder; +import java.nio.charset.Charset; + +public class ProtocolEncoderTest { + + private String concatenateStrings(String... strings) { + StringBuilder builder = new StringBuilder(); + for (String s : strings) { + builder.append(s); + } + return builder.toString(); + } + + protected ChannelBuffer binary(String... data) { + return binary(ByteOrder.BIG_ENDIAN, data); + } + + protected ChannelBuffer binary(ByteOrder endianness, String... data) { + return ChannelBuffers.wrappedBuffer( + endianness, DatatypeConverter.parseHexBinary(concatenateStrings(data))); + } + + protected String text(String... data) { + return concatenateStrings(data); + } + + protected ChannelBuffer buffer(String... data) { + return ChannelBuffers.copiedBuffer(concatenateStrings(data), Charset.defaultCharset()); + } + + protected void verifyCommand( + BaseProtocolEncoder encoder, Command command, ChannelBuffer expected) throws Exception { + verifyDecodedCommand(encoder.encodeCommand(command), expected); + } + + private void verifyDecodedCommand(Object decodedObject, ChannelBuffer expected) { + + Assert.assertNotNull("command is null", decodedObject); + Assert.assertTrue("not a buffer", decodedObject instanceof ChannelBuffer); + Assert.assertEquals(ChannelBuffers.hexDump(expected), ChannelBuffers.hexDump((ChannelBuffer) decodedObject)); + + } + +} diff --git a/test/org/traccar/protocol/MeiligaoProtocolEncoderTest.java b/test/org/traccar/protocol/MeiligaoProtocolEncoderTest.java new file mode 100644 index 000000000..b4a8b9f02 --- /dev/null +++ b/test/org/traccar/protocol/MeiligaoProtocolEncoderTest.java @@ -0,0 +1,41 @@ +package org.traccar.protocol; + +import org.junit.Test; +import org.traccar.ProtocolEncoderTest; +import org.traccar.model.Command; + +public class MeiligaoProtocolEncoderTest extends ProtocolEncoderTest { + + @Test + public void testEncode() throws Exception { + + MeiligaoProtocolEncoder encoder = new MeiligaoProtocolEncoder(); + + Command command = new Command(); + command.setDeviceId(1); + command.setType(Command.TYPE_POSITION_SINGLE); + + verifyCommand(encoder, command, binary("404000111234567890123441016cf70d0a")); + + command.setType(Command.TYPE_POSITION_PERIODIC); + command.set(Command.KEY_FREQUENCY, 100); + + verifyCommand(encoder, command, binary("40400013123456789012344102000a2f4f0d0a")); + + command.setType(Command.TYPE_SET_TIMEZONE); + command.set(Command.KEY_TIMEZONE, 480 * 60); + + verifyCommand(encoder, command, binary("4040001412345678901234413234383030ad0d0a")); + + command.setType(Command.TYPE_REBOOT_DEVICE); + + verifyCommand(encoder, command, binary("40400011123456789012344902d53d0d0a")); + + command.setType(Command.TYPE_MOVEMENT_ALARM); + command.set(Command.KEY_RADIUS, 1000); + + verifyCommand(encoder, command, binary("4040001312345678901234410603e87bb00d0a")); + + } + +} -- cgit v1.2.3