aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debug.xml6
-rw-r--r--setup/default.xml1
-rw-r--r--src/org/traccar/BaseProtocolDecoder.java9
-rw-r--r--src/org/traccar/api/resource/ServerResource.java6
-rw-r--r--src/org/traccar/protocol/AtrackFrameDecoder.java23
-rw-r--r--src/org/traccar/protocol/AtrackProtocolDecoder.java127
-rw-r--r--src/org/traccar/protocol/Gl200TextProtocolDecoder.java15
-rw-r--r--src/org/traccar/protocol/MeiligaoProtocolDecoder.java10
-rw-r--r--src/org/traccar/protocol/SanavProtocolDecoder.java2
-rw-r--r--src/org/traccar/protocol/WristbandProtocol.java42
-rw-r--r--src/org/traccar/protocol/WristbandProtocolDecoder.java146
-rw-r--r--test/org/traccar/protocol/AtrackFrameDecoderTest.java8
-rw-r--r--test/org/traccar/protocol/AtrackProtocolDecoderTest.java26
-rw-r--r--test/org/traccar/protocol/Gl200TextProtocolDecoderTest.java5
-rw-r--r--test/org/traccar/protocol/WristbandProtocolDecoderTest.java18
15 files changed, 403 insertions, 41 deletions
diff --git a/debug.xml b/debug.xml
index e8647b6fe..ed3520cfc 100644
--- a/debug.xml
+++ b/debug.xml
@@ -19,10 +19,6 @@
<entry key='database.user'>sa</entry>
<entry key='database.password'></entry>
- <entry key='intellitrac.port'>6037</entry>
-
- <!--<entry key='ldap.enable'>true</entry>
- <entry key='ldap.url'>ldap://ldap.forumsys.com:389</entry>
- <entry key='ldap.context'>dc=example,dc=com</entry>-->
+ <entry key='atrack.custom'>true</entry>
</properties>
diff --git a/setup/default.xml b/setup/default.xml
index 49c6bb4a2..9437747ed 100644
--- a/setup/default.xml
+++ b/setup/default.xml
@@ -251,5 +251,6 @@
<entry key='avema.port'>5171</entry>
<entry key='autotrack.port'>5172</entry>
<entry key='tek.port'>5173</entry>
+ <entry key='wristband.port'>5174</entry>
</properties>
diff --git a/src/org/traccar/BaseProtocolDecoder.java b/src/org/traccar/BaseProtocolDecoder.java
index d7ccb6460..d9ab60e3a 100644
--- a/src/org/traccar/BaseProtocolDecoder.java
+++ b/src/org/traccar/BaseProtocolDecoder.java
@@ -67,6 +67,15 @@ public abstract class BaseProtocolDecoder extends ExtendedObjectDecoder {
return protocol.getName();
}
+ public String getServer(Channel channel) {
+ String server = Context.getConfig().getString(getProtocolName() + ".server");
+ if (server == null && channel != null) {
+ InetSocketAddress address = (InetSocketAddress) channel.localAddress();
+ server = address.getAddress().getHostAddress() + ":" + address.getPort();
+ }
+ return server;
+ }
+
protected double convertSpeed(double value, String defaultUnits) {
switch (Context.getConfig().getString(getProtocolName() + ".speed", defaultUnits)) {
case "kmh":
diff --git a/src/org/traccar/api/resource/ServerResource.java b/src/org/traccar/api/resource/ServerResource.java
index 61d3221f0..e7cad2a0c 100644
--- a/src/org/traccar/api/resource/ServerResource.java
+++ b/src/org/traccar/api/resource/ServerResource.java
@@ -53,7 +53,11 @@ public class ServerResource extends BaseResource {
@Path("geocode")
@GET
public String geocode(@QueryParam("latitude") double latitude, @QueryParam("longitude") double longitude) {
- return Context.getGeocoder().getAddress(latitude, longitude, null);
+ if (Context.getGeocoder() != null) {
+ return Context.getGeocoder().getAddress(latitude, longitude, null);
+ } else {
+ throw new RuntimeException("Reverse geocoding is not enabled");
+ }
}
}
diff --git a/src/org/traccar/protocol/AtrackFrameDecoder.java b/src/org/traccar/protocol/AtrackFrameDecoder.java
index 3d33d9862..f071e2d97 100644
--- a/src/org/traccar/protocol/AtrackFrameDecoder.java
+++ b/src/org/traccar/protocol/AtrackFrameDecoder.java
@@ -21,6 +21,8 @@ import io.netty.channel.ChannelHandlerContext;
import org.traccar.BaseFrameDecoder;
import org.traccar.helper.BufferUtil;
+import java.nio.charset.StandardCharsets;
+
public class AtrackFrameDecoder extends BaseFrameDecoder {
private static final int KEEPALIVE_LENGTH = 12;
@@ -48,9 +50,24 @@ public class AtrackFrameDecoder extends BaseFrameDecoder {
} else {
- int endIndex = BufferUtil.indexOf("\r\n", buf);
- if (endIndex > 0) {
- return buf.readRetainedSlice(endIndex - buf.readerIndex() + 2);
+ int lengthStart = buf.indexOf(buf.readerIndex() + 3, buf.writerIndex(), (byte) ',') + 1;
+ if (lengthStart > 0) {
+ int lengthEnd = buf.indexOf(lengthStart, buf.writerIndex(), (byte) ',');
+ if (lengthEnd > 0) {
+ int length = lengthEnd + Integer.parseInt(buf.toString(
+ lengthStart, lengthEnd - lengthStart, StandardCharsets.US_ASCII));
+ if (buf.readableBytes() > length && buf.getByte(buf.readerIndex() + length) == '\n') {
+ length += 1;
+ }
+ if (buf.readableBytes() >= length) {
+ return buf.readRetainedSlice(length);
+ }
+ }
+ } else {
+ int endIndex = BufferUtil.indexOf("\r\n", buf);
+ if (endIndex > 0) {
+ return buf.readRetainedSlice(endIndex - buf.readerIndex() + 2);
+ }
}
}
diff --git a/src/org/traccar/protocol/AtrackProtocolDecoder.java b/src/org/traccar/protocol/AtrackProtocolDecoder.java
index 4a2d4b848..49f44a4f0 100644
--- a/src/org/traccar/protocol/AtrackProtocolDecoder.java
+++ b/src/org/traccar/protocol/AtrackProtocolDecoder.java
@@ -104,20 +104,81 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
}
private void readTextCustomData(Position position, String data, String form) {
+ CellTower cellTower = new CellTower();
String[] keys = form.substring(1).split("%");
String[] values = data.split(",|\r\n");
for (int i = 0; i < Math.min(keys.length, values.length); i++) {
switch (keys[i]) {
+ case "SA":
+ position.set(Position.KEY_SATELLITES, Integer.parseInt(values[i]));
+ break;
case "MV":
position.set(Position.KEY_POWER, Integer.parseInt(values[i]) * 0.1);
break;
case "BV":
position.set(Position.KEY_BATTERY, Integer.parseInt(values[i]) * 0.1);
break;
+ case "GQ":
+ cellTower.setSignalStrength(Integer.parseInt(values[i]));
+ break;
+ case "CE":
+ cellTower.setCellId(Long.parseLong(values[i]));
+ break;
+ case "LC":
+ cellTower.setLocationAreaCode(Integer.parseInt(values[i]));
+ break;
+ case "CN":
+ if (values[i].length() > 3) {
+ cellTower.setMobileCountryCode(Integer.parseInt(values[i].substring(0, 3)));
+ cellTower.setMobileNetworkCode(Integer.parseInt(values[i].substring(3)));
+ }
+ break;
+ case "PC":
+ position.set(Position.PREFIX_COUNT + 1, Integer.parseInt(values[i]));
+ break;
+ case "AT":
+ position.setAltitude(Integer.parseInt(values[i]));
+ break;
+ case "RP":
+ position.set(Position.KEY_RPM, Integer.parseInt(values[i]));
+ break;
+ case "GS":
+ position.set(Position.KEY_RSSI, Integer.parseInt(values[i]));
+ break;
+ case "DT":
+ position.set(Position.KEY_ARCHIVE, Integer.parseInt(values[i]) == 1);
+ break;
+ case "VN":
+ position.set(Position.KEY_VIN, values[i]);
+ break;
+ case "TR":
+ position.set(Position.KEY_THROTTLE, Integer.parseInt(values[i]));
+ break;
+ case "ET":
+ position.set(Position.PREFIX_TEMP + 1, Integer.parseInt(values[i]));
+ break;
+ case "FL":
+ position.set(Position.KEY_FUEL_LEVEL, Integer.parseInt(values[i]));
+ break;
+ case "FC":
+ position.set(Position.KEY_FUEL_CONSUMPTION, Integer.parseInt(values[i]));
+ break;
+ case "AV1":
+ position.set(Position.PREFIX_ADC + 1, Integer.parseInt(values[i]));
+ break;
default:
break;
}
}
+
+ if (cellTower.getMobileCountryCode() != null
+ && cellTower.getMobileNetworkCode() != null
+ && cellTower.getCellId() != null
+ && cellTower.getLocationAreaCode() != null) {
+ position.setNetwork(new Network(cellTower));
+ } else if (cellTower.getSignalStrength() != null) {
+ position.set(Position.KEY_RSSI, cellTower.getSignalStrength());
+ }
}
private void readBinaryCustomData(Position position, ByteBuf buf, String form) {
@@ -208,6 +269,30 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
case "MA":
readString(buf); // mac address
break;
+ case "PD":
+ buf.readUnsignedByte(); // pending code status
+ break;
+ case "CD":
+ readString(buf); // sim cid
+ break;
+ case "CM":
+ buf.readLong(); // imsi
+ break;
+ case "GN":
+ buf.skipBytes(60); // g sensor data
+ break;
+ case "GV":
+ buf.skipBytes(6); // maximum g force
+ break;
+ case "ME":
+ buf.readLong(); // imei
+ break;
+ case "IA":
+ buf.readUnsignedByte(); // intake air temperature
+ break;
+ case "MP":
+ buf.readUnsignedByte(); // manifold absolute pressure
+ break;
default:
break;
}
@@ -215,7 +300,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
if (cellTower.getMobileCountryCode() != null
&& cellTower.getMobileNetworkCode() != null
- && cellTower.getCellId() != null
+ && cellTower.getCellId() != null && cellTower.getCellId() != 0
&& cellTower.getLocationAreaCode() != null) {
position.setNetwork(new Network(cellTower));
} else if (cellTower.getSignalStrength() != null) {
@@ -282,11 +367,6 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
}
private static final Pattern PATTERN = new PatternBuilder()
- .text("@P,")
- .number("x+,") // checksum
- .number("d+,") // length
- .number("d+,") // index
- .number("(d+),") // imei
.number("(d+),") // date and time
.number("d+,") // rtc date and time
.number("d+,") // device date and time
@@ -308,18 +388,41 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
.optional(2)
.compile();
- private Position decodeText(Channel channel, SocketAddress remoteAddress, String sentence) {
+ private List<Position> decodeText(Channel channel, SocketAddress remoteAddress, String sentence) {
- Parser parser = new Parser(PATTERN, sentence);
- if (!parser.matches()) {
- return null;
+ int startIndex = 0;
+ for (int i = 0; i < 4; i++) {
+ startIndex = sentence.indexOf(',', startIndex + 1);
}
+ int endIndex = sentence.indexOf(',', startIndex + 1);
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ String imei = sentence.substring(startIndex + 1, endIndex);
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
return null;
}
+ List<Position> positions = new LinkedList<>();
+ String[] lines = sentence.substring(endIndex + 1).split("\r\n");
+
+ for (String line : lines) {
+ Position position = decodeTextLine(deviceSession, line);
+ if (position != null) {
+ positions.add(position);
+ }
+ }
+
+ return positions;
+ }
+
+
+ private Position decodeTextLine(DeviceSession deviceSession, String sentence) {
+
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
@@ -442,7 +545,7 @@ public class AtrackProtocolDecoder extends BaseProtocolDecoder {
if (custom) {
String form = this.form;
if (form == null) {
- form = readString(buf).substring("%CI".length());
+ form = readString(buf).trim().substring("%CI".length());
}
readBinaryCustomData(position, buf, form);
}
diff --git a/src/org/traccar/protocol/Gl200TextProtocolDecoder.java b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java
index 675a7c1fe..164f20135 100644
--- a/src/org/traccar/protocol/Gl200TextProtocolDecoder.java
+++ b/src/org/traccar/protocol/Gl200TextProtocolDecoder.java
@@ -175,14 +175,16 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.compile();
private static final Pattern PATTERN_FRI = new PatternBuilder()
- .text("+").expression("(?:RESP|BUFF):GTFRI,")
+ .text("+").expression("(?:RESP|BUFF):GT...,")
.number("(?:[0-9A-Z]{2}xxxx)?,") // protocol version
.number("(d{15}|x{14}),") // imei
.expression("(?:([0-9A-Z]{17}),)?") // vin
.expression("[^,]*,") // device name
.number("(d+)?,") // power
- .number("d{1,2},") // report type
- .number("d{1,2},") // count
+ .number("d{1,2},").optional() // report type
+ .number("d{1,2},").optional() // count
+ .number(",").optional() // reserved
+ .number("(d+),").optional() // battery
.expression("((?:")
.expression(PATTERN_LOCATION.pattern())
.expression(")+)")
@@ -200,6 +202,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
.number("(?:d+.?d*|Inf|NaN)?,") // fuel consumption
.number("(d+)?,") // fuel level
.groupEnd()
+ .any()
.number("(dddd)(dd)(dd)") // date (yyyymmdd)
.number("(dd)(dd)(dd)").optional(2) // time (hhmmss)
.text(",")
@@ -711,6 +714,7 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
String vin = parser.next();
Integer power = parser.nextInt();
+ Integer battery = parser.nextInt();
Parser itemParser = new Parser(PATTERN_LOCATION, parser.next());
while (itemParser.find()) {
@@ -731,6 +735,9 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
if (power != null && power > 10) {
position.set(Position.KEY_POWER, power * 0.001); // only on some devices
}
+ if (battery != null) {
+ position.set(Position.KEY_BATTERY_LEVEL, battery);
+ }
if (parser.hasNext()) {
position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
@@ -1094,6 +1101,8 @@ public class Gl200TextProtocolDecoder extends BaseProtocolDecoder {
result = decodeCan(channel, remoteAddress, sentence);
break;
case "FRI":
+ case "GEO":
+ case "STR":
result = decodeFri(channel, remoteAddress, sentence);
break;
case "ERI":
diff --git a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
index 30905076e..9fbd053eb 100644
--- a/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
+++ b/src/org/traccar/protocol/MeiligaoProtocolDecoder.java
@@ -29,7 +29,6 @@ import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.Position;
-import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
@@ -206,15 +205,6 @@ public class MeiligaoProtocolDecoder extends BaseProtocolDecoder {
}
}
- private String getServer(Channel channel) {
- String server = Context.getConfig().getString(getProtocolName() + ".server");
- if (server == null && channel != null) {
- InetSocketAddress address = (InetSocketAddress) channel.localAddress();
- server = address.getAddress().getHostAddress() + ":" + address.getPort();
- }
- return server;
- }
-
private String decodeAlarm(short value) {
switch (value) {
case 0x01:
diff --git a/src/org/traccar/protocol/SanavProtocolDecoder.java b/src/org/traccar/protocol/SanavProtocolDecoder.java
index 688157171..763b51d04 100644
--- a/src/org/traccar/protocol/SanavProtocolDecoder.java
+++ b/src/org/traccar/protocol/SanavProtocolDecoder.java
@@ -87,7 +87,7 @@ public class SanavProtocolDecoder extends BaseProtocolDecoder {
position.setTime(dateBuilder.getDate());
if (parser.hasNext()) {
- int io = parser.nextInt();
+ int io = parser.nextHexInt();
for (int i = 0; i < 5; i++) {
position.set(Position.PREFIX_IN + (i + 1), BitUtil.check(io, i));
}
diff --git a/src/org/traccar/protocol/WristbandProtocol.java b/src/org/traccar/protocol/WristbandProtocol.java
new file mode 100644
index 000000000..02db38f4f
--- /dev/null
+++ b/src/org/traccar/protocol/WristbandProtocol.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import org.traccar.BaseProtocol;
+import org.traccar.PipelineBuilder;
+import org.traccar.TrackerServer;
+
+import java.util.List;
+
+public class WristbandProtocol extends BaseProtocol {
+
+ public WristbandProtocol() {
+ super("wristband");
+ }
+
+ @Override
+ public void initTrackerServers(List<TrackerServer> serverList) {
+ serverList.add(new TrackerServer(false, getName()) {
+ @Override
+ protected void addProtocolHandlers(PipelineBuilder pipeline) {
+ pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 3, 2, 3, 0));
+ pipeline.addLast("objectDecoder", new WristbandProtocolDecoder(WristbandProtocol.this));
+ }
+ });
+ }
+
+}
diff --git a/src/org/traccar/protocol/WristbandProtocolDecoder.java b/src/org/traccar/protocol/WristbandProtocolDecoder.java
new file mode 100644
index 000000000..f62836765
--- /dev/null
+++ b/src/org/traccar/protocol/WristbandProtocolDecoder.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2018 Anton Tananaev (anton@traccar.org)
+ *
+ * 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 io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import org.traccar.BaseProtocolDecoder;
+import org.traccar.DeviceSession;
+import org.traccar.NetworkMessage;
+import org.traccar.helper.Parser;
+import org.traccar.helper.PatternBuilder;
+import org.traccar.helper.UnitsConverter;
+import org.traccar.model.Position;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class WristbandProtocolDecoder extends BaseProtocolDecoder {
+
+ public WristbandProtocolDecoder(WristbandProtocol protocol) {
+ super(protocol);
+ }
+
+ private void sendResponse(
+ Channel channel, String imei, String version, int type, String data) {
+
+ if (channel != null) {
+ String sentence = String.format("YX%s|%s|0|{F%d#%s}\r\n", imei, version, type, data);
+ ByteBuf response = Unpooled.buffer();
+ response.writeBytes(new byte[]{0x00, 0x01, 0x02});
+ response.writeShort(sentence.length());
+ response.writeCharSequence(sentence, StandardCharsets.US_ASCII);
+ response.writeBytes(new byte[]{(byte) 0xFF, (byte) 0xFE, (byte) 0xFC});
+ channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress()));
+ }
+ }
+
+ private static final Pattern PATTERN = new PatternBuilder()
+ .expression("..") // header
+ .number("(d+)|") // imei
+ .number("(vd+.d+)|") // version
+ .number("d+|") // model
+ .text("{")
+ .number("F(d+)") // function
+ .groupBegin()
+ .text("#")
+ .expression("(.*)") // data
+ .groupEnd("?")
+ .text("}")
+ .text("\r\n")
+ .compile();
+
+ private Position decodePosition(DeviceSession deviceSession, String sentence) throws ParseException {
+
+ Position position = new Position(getProtocolName());
+ position.setDeviceId(deviceSession.getDeviceId());
+
+ String[] values = sentence.split(",");
+
+ position.setValid(true);
+ position.setLongitude(Double.parseDouble(values[0]));
+ position.setLatitude(Double.parseDouble(values[1]));
+ position.setTime(new SimpleDateFormat("yyyyMMddHHmmss").parse(values[2]));
+ position.setSpeed(UnitsConverter.knotsFromKph(Double.parseDouble(values[3])));
+
+ return position;
+ }
+
+ private List<Position> decodeMessage(
+ Channel channel, SocketAddress remoteAddress, String sentence) throws ParseException {
+
+ Parser parser = new Parser(PATTERN, sentence);
+ if (!parser.matches()) {
+ return null;
+ }
+
+ String imei = parser.next();
+ DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
+ if (deviceSession == null) {
+ return null;
+ }
+
+ String version = parser.next();
+ int type = parser.nextInt();
+
+ List<Position> positions = new LinkedList<>();
+
+ switch (type) {
+ case 90:
+ sendResponse(channel, imei, version, type, getServer(channel));
+ break;
+ case 91:
+ String time = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
+ sendResponse(channel, imei, version, type, time + "|" + getServer(channel));
+ break;
+ case 1:
+ sendResponse(channel, imei, version, type, "0");
+ break;
+ case 2:
+ for (String fragment : parser.next().split("\\|")) {
+ positions.add(decodePosition(deviceSession, fragment));
+ }
+ break;
+ default:
+ break;
+ }
+
+ return positions.isEmpty() ? null : positions;
+ }
+
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ ByteBuf buf = (ByteBuf) msg;
+ buf.skipBytes(3); // header
+ buf.readUnsignedShort(); // length
+
+ String sentence = buf.toString(buf.readerIndex(), buf.readableBytes() - 3, StandardCharsets.US_ASCII);
+
+ buf.skipBytes(3); // footer
+
+ return decodeMessage(channel, remoteAddress, sentence);
+ }
+
+}
diff --git a/test/org/traccar/protocol/AtrackFrameDecoderTest.java b/test/org/traccar/protocol/AtrackFrameDecoderTest.java
index df775021a..3a26bb7a7 100644
--- a/test/org/traccar/protocol/AtrackFrameDecoderTest.java
+++ b/test/org/traccar/protocol/AtrackFrameDecoderTest.java
@@ -11,6 +11,14 @@ public class AtrackFrameDecoderTest extends ProtocolTest {
AtrackFrameDecoder decoder = new AtrackFrameDecoder();
verifyFrame(
+ binary("40502c414246342c3532362c3939312c3335363936313037353933313136352c313533343338313635362c313533343338313635392c313533343338313635392c2d38383432393138382c34343237313232352c37302c322c3230303536332c392c312c302c302c302c2c323030302c323030302c1a2c25434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d502c302c3331303236302c31382c392c302c302c3254314b523332453238433730363138352c302c302c302c35342c383930313236303838313231353234373735392c3235322c36302c3132322c34312c3331303236303838313532343737352c302c687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237313232352c2d38382e343239313834201a2c3030393830303442303346343030393830303442303346343030393830303442303346333030393830303442303346343030393830303442303346343030393930303442303346343030393830303442303346343030393830303442303346343030393830303442303346343030393830303442303346331a2c3030343846463130303345381a2c302c3335363936313037353933313136352c302c3938302c31322c302c31382c35302c300d0a"),
+ decoder.decode(null, null, binary("40502c414246342c3532362c3939312c3335363936313037353933313136352c313533343338313635362c313533343338313635392c313533343338313635392c2d38383432393138382c34343237313232352c37302c322c3230303536332c392c312c302c302c302c2c323030302c323030302c1a2c25434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d502c302c3331303236302c31382c392c302c302c3254314b523332453238433730363138352c302c302c302c35342c383930313236303838313231353234373735392c3235322c36302c3132322c34312c3331303236303838313532343737352c302c687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237313232352c2d38382e343239313834201a2c3030393830303442303346343030393830303442303346343030393830303442303346333030393830303442303346343030393830303442303346343030393930303442303346343030393830303442303346343030393830303442303346343030393830303442303346343030393830303442303346331a2c3030343846463130303345381a2c302c3335363936313037353933313136352c302c3938302c31322c302c31382c35302c300d0a")));
+
+ verifyFrame(
+ binary("40502c383732442c3731322c36353232312c3335373239383037303432363439382c313533343731353836322c313533343731353836322c313533343732363437342c2d38383531303939352c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353932312c313533343731353932322c313533343732363437342c2d38383531313032332c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32382c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353938322c313533343731353938322c313533343732363437342c2d38383531313034362c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c392c3331303236302c373838383637342c3134342c31360d0a313533343731363034312c313533343731363034322c313533343732363437342c2d38383531313031312c34343236303636332c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731363130322c313533343731363130322c313533343732363437342c2d38383531303938382c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134352c31360d0a"),
+ decoder.decode(null, null, binary("40502c383732442c3731322c36353232312c3335373239383037303432363439382c313533343731353836322c313533343731353836322c313533343732363437342c2d38383531303939352c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353932312c313533343731353932322c313533343732363437342c2d38383531313032332c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32382c31302c3331303236302c373838383637342c3134342c31360d0a313533343731353938322c313533343731353938322c313533343732363437342c2d38383531313034362c34343236303636382c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c392c3331303236302c373838383637342c3134342c31360d0a313533343731363034312c313533343731363034322c313533343732363437342c2d38383531313031312c34343236303636332c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134342c31360d0a313533343731363130322c313533343731363130322c313533343732363437342c2d38383531303938382c34343236303637362c3137362c322c35363832372c362c312c302c302c302c2c323030302c323030302c1a2c25434925475125475325434e254345254d562553412c32392c31302c3331303236302c373838383637342c3134352c31360d0a")));
+
+ verifyFrame(
binary("40502c373542332c3132302c37393737392c3335383930313034383039313535342c32303138303431323134323531342c32303138303431323134323531342c32303138303431333030303635352c31363432333338392c34383137383730302c3130382c322c362e352c392c302c302c302c302c302c323030302c323030302c1a0d0a"),
decoder.decode(null, null, binary("40502c373542332c3132302c37393737392c3335383930313034383039313535342c32303138303431323134323531342c32303138303431323134323531342c32303138303431333030303635352c31363432333338392c34383137383730302c3130382c322c362e352c392c302c302c302c302c302c323030302c323030302c1a0d0a")));
diff --git a/test/org/traccar/protocol/AtrackProtocolDecoderTest.java b/test/org/traccar/protocol/AtrackProtocolDecoderTest.java
index 7ac6635f7..d38cdc34e 100644
--- a/test/org/traccar/protocol/AtrackProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/AtrackProtocolDecoderTest.java
@@ -10,13 +10,13 @@ public class AtrackProtocolDecoderTest extends ProtocolTest {
AtrackProtocolDecoder decoder = new AtrackProtocolDecoder(new AtrackProtocol());
- verifyPosition(decoder, buffer(
+ verifyPositions(decoder, buffer(
"@P,3A34,146,41431,353816057242284,20180622015809,20180622015809,20180622015809,9720689,4014230,61,2,0,20,1,0,0,0,0,2000,2000,12160,42,624,002,20009,20014,\r\n"));
- verifyPosition(decoder, buffer(
+ verifyPositions(decoder, buffer(
"@P,1126,121,104547,358901048091554,20180412143513,20180412143514,20180413060000,16423389,48178700,108,2,6.5,9,0,0,0,0,0,2000,2000,\r\n"));
- verifyPosition(decoder, buffer(
+ verifyPositions(decoder, buffer(
"@P,434E,124,104655,358901048091554,20180412143706,20180412143706,20180413060107,16423389,48178700,108,121,6.5,10,0,0,0,0,0,2000,2000,\r\n"));
verifyPositions(decoder, binary(
@@ -34,12 +34,28 @@ public class AtrackProtocolDecoderTest extends ProtocolTest {
decoder.setCustom(true);
- verifyPosition(decoder, buffer(
+ verifyPositions(decoder, buffer(
+ "@P,0EBB,687,1917,357298070426498,1534718280,1534718279,1534739774,-88643875,44210148,270,2,57128,6,1,130,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,5,10,310260,7888593,137,16\r\n",
+ "1534718283,1534718283,1534739774,-88645243,44210145,269,2,57129,6,1,129,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,5,10,310260,7888593,137,16\r\n",
+ "1534718286,1534718285,1534739774,-88646581,44210136,269,2,57130,6,1,127,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,99,10,0,0,137,16\r\n",
+ "1534718289,1534718288,1534739774,-88647911,44210123,269,2,57131,6,1,127,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,99,10,0,0,137,16\r\n",
+ "1534718292,1534718291,1534739774,-88649229,44210111,269,2,57132,6,1,124,0,0,,2000,2000,,%CI%GQ%GS%CN%CE%MV%SA,99,10,0,0,136,16\r\n"));
+
+ verifyPositions(decoder, binary(
+ "4050b63b02c401af000144a77a21281d5b79d8ef5b79d8ef5b79d8f0fab84831029f35580056020003144d00080100130000000007d007d00025434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d5000000000000004bbf41f0900003254314b5233324532384337303631383500000000000053005f3839303132363038383132313532343737353900000000fd078e0085002900011a2e3da1882700687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34332e3938383331322c2d38382e35383631383920000013ff4e04190023ff13041100a3ffdc0402009affde03fc00a4ffe3040c0093ffab03ee004dffbb04130012ff7a04180010ff6e04100037ff4d0402ffd8000c04140000000144a77a21281d0009470c00131b2b005b79d8f05b79d8f05b79d8f0fab8488e029f356f0043020003144d00080100170000000007d007d00025434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d5000000000000004bbf41f0900003254314b5233324532384337303631383500000000000052005f3839303132363038383132313532343737353900000000fd09190085002900011a2e3da1882700687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34332e3938383333352c2d38382e35383630393820000013ff4e04190023ff1304110017ff0c0424000cff30041a00a4ffe3040c0093ffab03ee004dffbb04130012ff7a04180010ff6e04100037ff4d0402ffd8000c04140000000144a77a21281d0009470c00171c2b00"));
+
+ verifyPositions(decoder, binary(
+ "405ad77c01670410000144a77a21281d5b74d2335b74d2335b74d233fabaf3bc02a38d3d010c0200030f8e000701001a0000000007d007d00025434925434525434e25475125475325464c254d4c25564e25504425464325454c254554254344254154254d46254d5625425625434d25445425474c25474e254756254c43254d4525524c25525025534125534d255452254941254d5000000000000004bbf41c0900003254314b523332453238433730363138350000000000004800543839303132363038383132313532343737353900000000ec06a50089002900011a2e3da1882700687474703a2f2f6d6170732e676f6f676c652e636f6d2f6d6170733f713d34342e3237323935372c2d38382e34313132303120000075ff4903fb006fff63040a004dff5d04080030ffa10407003b001304060026000503f9001e002504020078ff6204000073ff7d03f9007aff6903f3ffc0001804040000000144a77a21281d00073f0c001a182400"));
+
+ verifyPositions(decoder, buffer(
+ "@P,6254,235,989,356961075931165,1534381563,1534381564,1534381564,-88429188,44271225,70,2,200563,8,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,18,9,0,0,2T1KR32E28C706185,0,0,0,54,8901260881215247759,252,489,123"));
+
+ verifyPositions(decoder, buffer(
"@P,27A6,663,707,356961075931165,1534211298,1534211297,1534211437,-88429190,44271135,288,2,200235,8,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,17,9,0,0,2T1KR32E28C706185,0,0,0,80,8901260881215247759,251,59,124\r\n",
"1534211353,1534211357,1534211437,-88429190,44271135,288,2,200235,7,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,17,2,0,0,2T1KR32E28C706185,0,0,0,79,8901260881215247759,251,60,124\r\n",
"1534211417,1534211417,1534211437,-88429190,44271135,288,2,200235,7,1,0,0,0,,2000,2000,,%CI%CE%CN%GQ%GS%FL%ML%VN%PD%FC%EL%ET%CD%AT%MF%MV,0,310260,17,2,0,0,2T1KR32E28C706185,0,0,0,78,8901260881215247759,251,56,124\r\n"));
- verifyPosition(decoder, buffer(
+ verifyPositions(decoder, buffer(
"@P,CA4B,122,1,358683064932578,1533633976,1533633975,1533633975,121562641,25082649,72,0,1638,15,0,0,1,0,,2000,2000,,%CI%MV%BV,143,0\r\n"));
verifyPositions(decoder, binary(
diff --git a/test/org/traccar/protocol/Gl200TextProtocolDecoderTest.java b/test/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
index 7269c0b78..3db155e70 100644
--- a/test/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
+++ b/test/org/traccar/protocol/Gl200TextProtocolDecoderTest.java
@@ -10,6 +10,9 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
Gl200TextProtocolDecoder decoder = new Gl200TextProtocolDecoder(new Gl200Protocol());
+ verifyPositions(decoder, buffer(
+ "+RESP:GTSTR,440502,866427030112088,GL530,0,0,2,,100,3,0.6,0,127.5,2.413963,48.877096,20180704180102,0208,0001,0310,E625,,,0000,20180704180100,004C$"));
+
verifyPosition(decoder, buffer(
"+RESP:GTLSW,300500,860599002636595,,0,0,0,0.0,0,2886.5,-78.467145,-0.165335,20180518221815,,,,,,20180518221817,B6FD$"));
@@ -178,7 +181,7 @@ public class Gl200TextProtocolDecoderTest extends ProtocolTest {
verifyAttributes(decoder, buffer(
"+BUFF:GTINF,1A0800,860599000773978,GL300,41,89701016426133851978,23,0,1,204.7,,3.84,1,1,0,0,0,20161006072548,62,1,38,,,20161006082343,0C98$"));
- verifyPosition(decoder, buffer(
+ verifyPositions(decoder, buffer(
"+RESP:GTFRI,360100,864251020141408,3VWGW6AJ0FM237324,gv500,,10,1,1,0.0,0,2258.4,-99.256948,19.555800,20160929214743,0334,0020,0084,65AC,00,0.0,,,,100,410000,0,nan,,20160929214743,13BA$"));
verifyPosition(decoder, buffer(
diff --git a/test/org/traccar/protocol/WristbandProtocolDecoderTest.java b/test/org/traccar/protocol/WristbandProtocolDecoderTest.java
new file mode 100644
index 000000000..e85b21a35
--- /dev/null
+++ b/test/org/traccar/protocol/WristbandProtocolDecoderTest.java
@@ -0,0 +1,18 @@
+package org.traccar.protocol;
+
+import org.junit.Test;
+import org.traccar.ProtocolTest;
+
+public class WristbandProtocolDecoderTest extends ProtocolTest {
+
+ @Test
+ public void testDecode() throws Exception {
+
+ WristbandProtocolDecoder decoder = new WristbandProtocolDecoder(new WristbandProtocol());
+
+ verifyNull(decoder, binary(
+ "000102004159583336373535313631303030303934347c56312e307c317c7b4639312330317c30307c30307c33475f7065745f323031382f30352f31362031313a30307d0d0afffefc"));
+
+ }
+
+}