aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2019-07-12 10:34:57 -0700
committerGitHub <noreply@github.com>2019-07-12 10:34:57 -0700
commit2ce793cfe7df9ec68708722489cec267ddda0b2b (patch)
tree31971fe8ac67abc607bcb315ada9667d309fda75 /src
parent0130b9093bca894268493d27173e8ecac02c0179 (diff)
parent1da58124acf0ce8c820b78519d17c2a408aced24 (diff)
downloadtraccar-server-2ce793cfe7df9ec68708722489cec267ddda0b2b.tar.gz
traccar-server-2ce793cfe7df9ec68708722489cec267ddda0b2b.tar.bz2
traccar-server-2ce793cfe7df9ec68708722489cec267ddda0b2b.zip
Merge pull request #4342 from edvalley/proposed
Enhance Laipac protocol decoder and encoder
Diffstat (limited to 'src')
-rw-r--r--src/main/java/org/traccar/BaseProtocolEncoder.java13
-rw-r--r--src/main/java/org/traccar/database/DeviceManager.java14
-rw-r--r--src/main/java/org/traccar/database/IdentityManager.java4
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocol.java10
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java137
-rw-r--r--src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java52
-rw-r--r--src/test/java/org/traccar/TestIdentityManager.java5
-rw-r--r--src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java23
8 files changed, 208 insertions, 50 deletions
diff --git a/src/main/java/org/traccar/BaseProtocolEncoder.java b/src/main/java/org/traccar/BaseProtocolEncoder.java
index d7625e4b8..6d96280f7 100644
--- a/src/main/java/org/traccar/BaseProtocolEncoder.java
+++ b/src/main/java/org/traccar/BaseProtocolEncoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2019 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.
@@ -22,7 +22,6 @@ import io.netty.channel.ChannelPromise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.model.Command;
-import org.traccar.model.Device;
public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter {
@@ -34,13 +33,9 @@ public abstract class BaseProtocolEncoder extends ChannelOutboundHandlerAdapter
protected void initDevicePassword(Command command, String defaultPassword) {
if (!command.getAttributes().containsKey(Command.KEY_DEVICE_PASSWORD)) {
- Device device = Context.getIdentityManager().getById(command.getDeviceId());
- String password = device.getString(Command.KEY_DEVICE_PASSWORD);
- if (password != null) {
- command.set(Command.KEY_DEVICE_PASSWORD, password);
- } else {
- command.set(Command.KEY_DEVICE_PASSWORD, defaultPassword);
- }
+ String password = Context.getIdentityManager()
+ .getDevicePassword(command.getDeviceId(), defaultPassword);
+ command.set(Command.KEY_DEVICE_PASSWORD, password);
}
}
diff --git a/src/main/java/org/traccar/database/DeviceManager.java b/src/main/java/org/traccar/database/DeviceManager.java
index de4607d1f..62e6de080 100644
--- a/src/main/java/org/traccar/database/DeviceManager.java
+++ b/src/main/java/org/traccar/database/DeviceManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2016 - 2019 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.
@@ -29,6 +29,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.traccar.config.Config;
import org.traccar.Context;
+import org.traccar.model.Command;
import org.traccar.model.Device;
import org.traccar.model.DeviceState;
import org.traccar.model.DeviceAccumulators;
@@ -114,6 +115,17 @@ public class DeviceManager extends BaseObjectManager<Device> implements Identity
return devicesByUniqueId.get(uniqueId);
}
+ @Override
+ public String getDevicePassword(long id, String defaultPassword) {
+
+ String password = getById(id).getString(Command.KEY_DEVICE_PASSWORD);
+ if (password != null) {
+ return password;
+ }
+
+ return defaultPassword;
+ }
+
public Device getDeviceByPhone(String phone) {
return devicesByPhone.get(phone);
}
diff --git a/src/main/java/org/traccar/database/IdentityManager.java b/src/main/java/org/traccar/database/IdentityManager.java
index 6228a0f75..add3e5a79 100644
--- a/src/main/java/org/traccar/database/IdentityManager.java
+++ b/src/main/java/org/traccar/database/IdentityManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2019 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.
@@ -26,6 +26,8 @@ public interface IdentityManager {
Device getByUniqueId(String uniqueId) throws Exception;
+ String getDevicePassword(long id, String defaultPassword);
+
Position getLastPosition(long deviceId);
boolean isLatestPosition(Position position);
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocol.java b/src/main/java/org/traccar/protocol/LaipacProtocol.java
index 923b08a16..b325755c8 100644
--- a/src/main/java/org/traccar/protocol/LaipacProtocol.java
+++ b/src/main/java/org/traccar/protocol/LaipacProtocol.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2015 - 2019 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.
@@ -18,6 +18,7 @@ package org.traccar.protocol;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
+import org.traccar.model.Command;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
@@ -25,12 +26,19 @@ import org.traccar.TrackerServer;
public class LaipacProtocol extends BaseProtocol {
public LaipacProtocol() {
+
+ setSupportedDataCommands(
+ Command.TYPE_CUSTOM,
+ Command.TYPE_POSITION_SINGLE,
+ Command.TYPE_REBOOT_DEVICE);
+
addServer(new TrackerServer(false, getName()) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline) {
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringEncoder());
pipeline.addLast(new StringDecoder());
+ pipeline.addLast(new LaipacProtocolEncoder());
pipeline.addLast(new LaipacProtocolDecoder(LaipacProtocol.this));
}
});
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
index 2f3cbb1b9..2302015f5 100644
--- a/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/LaipacProtocolDecoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 - 2018 Anton Tananaev (anton@traccar.org)
+ * Copyright 2013 - 2019 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.
@@ -17,6 +17,7 @@ package org.traccar.protocol;
import io.netty.channel.Channel;
import org.traccar.BaseProtocolDecoder;
+import org.traccar.Context;
import org.traccar.DeviceSession;
import org.traccar.NetworkMessage;
import org.traccar.Protocol;
@@ -37,29 +38,33 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
super(protocol);
}
+ public static final String DEFAULT_DEVICE_PASSWORD = "00000000";
+
private static final Pattern PATTERN = new PatternBuilder()
.text("$AVRMC,")
.expression("([^,]+),") // identifier
.number("(dd)(dd)(dd),") // time (hhmmss)
.expression("([AVRPavrp]),") // validity
.number("(dd)(dd.d+),") // latitude
- .expression("([NS]),")
+ .expression("([NS]),") // latitude hemisphere
.number("(ddd)(dd.d+),") // longitude
- .number("([EW]),")
+ .number("([EW]),") // longitude hemisphere
.number("(d+.d+),") // speed
.number("(d+.d+),") // course
.number("(dd)(dd)(dd),") // date (ddmmyy)
- .expression("([abZXTSMHFE86430]),") // event code
+ .expression("([0-9A-Za-z]),") // event code
.expression("([\\d.]+),") // battery voltage
.number("(d+),") // current mileage
.number("(d),") // gps status
.number("(d+),") // adc1
.number("(d+)") // adc2
- .number(",(xxxx)") // lac
- .number("(xxxx),") // cid
- .number("(ddd)") // mcc
- .number("(ddd)") // mnc
+ .number(",(xxxx|x)") // lac | lac+cid = 0
+ .number("(xxxx),") // cid | nothing
+ .number("(ddd|d)") // mcc | mcc+mnc = 0
+ .number("(ddd)") // mnc | nothing
.optional(4)
+ .expression(",([^*]*)") // anything remaining (be forward compatible)
+ .optional(1)
.text("*")
.number("(xx)") // checksum
.compile();
@@ -68,6 +73,8 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
switch (event) {
case "Z":
return Position.ALARM_LOW_BATTERY;
+ case "Y":
+ return Position.ALARM_TOW;
case "X":
return Position.ALARM_GEOFENCE_ENTER;
case "T":
@@ -81,6 +88,8 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
return Position.ALARM_GEOFENCE_EXIT;
case "6":
return Position.ALARM_OVERSPEED;
+ case "5":
+ return Position.ALARM_POWER_CUT;
case "3":
return Position.ALARM_SOS;
default:
@@ -88,30 +97,84 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
}
}
- @Override
- protected Object decode(
- Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+ private String decodeEvent(String event, Position position) {
- String sentence = (String) msg;
+ if (event.length() == 1) {
+ char inputStatus = event.charAt(0);
+ if (inputStatus >= 'A' && inputStatus <= 'D') {
+ int inputStatusInt = inputStatus - 'A';
+ position.set(Position.PREFIX_IN + 1, inputStatusInt & 1);
+ position.set(Position.PREFIX_IN + 2, inputStatusInt & 2);
+ return null;
+ }
+ }
- if (sentence.startsWith("$ECHK") && channel != null) {
- channel.writeAndFlush(new NetworkMessage(sentence + "\r\n", remoteAddress)); // heartbeat
- return null;
+ return event;
+ }
+
+ private void sendEventResponse(
+ String event, String devicePassword, Channel channel, SocketAddress remoteAddress) {
+
+ String responseCode = null;
+
+ switch (event) {
+ case "3":
+ responseCode = "d";
+ break;
+ case "S":
+ case "T":
+ responseCode = "t";
+ break;
+ case "X":
+ case "4":
+ responseCode = "x";
+ break;
+ case "Y":
+ responseCode = "y";
+ break;
+ case "Z":
+ responseCode = "z";
+ break;
+ default:
+ break;
+ }
+
+ if (responseCode != null) {
+ String response = "$AVCFG," + devicePassword + "," + responseCode;
+ response += Checksum.nmea(response) + "\r\n";
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
}
+ }
+
+ private void sendAcknowledge(
+ String status, String event, String checksum, Channel channel, SocketAddress remoteAddress) {
+
+ if (Character.isLowerCase(status.charAt(0))) {
+ String response = "$EAVACK," + event + "," + checksum;
+ response += Checksum.nmea(response) + "\r\n";
+ channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
+ }
+
+ }
+
+ protected Object decodeAvrmc(
+ String sentence, Channel channel, SocketAddress remoteAddress) {
+
Parser parser = new Parser(PATTERN, sentence);
if (!parser.matches()) {
return null;
}
- DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+ DeviceSession deviceSession =
+ getDeviceSession(channel, remoteAddress, parser.next());
if (deviceSession == null) {
return null;
}
Position position = new Position(getProtocolName());
- position.setDeviceId(deviceSession.getDeviceId());
+ position.setDeviceId(deviceSession.getDeviceId());
DateBuilder dateBuilder = new DateBuilder()
.setTime(parser.nextInt(0), parser.nextInt(0), parser.nextInt(0));
@@ -130,9 +193,9 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
String event = parser.next();
position.set(Position.KEY_ALARM, decodeAlarm(event));
- position.set(Position.KEY_EVENT, event);
+ position.set(Position.KEY_EVENT, decodeEvent(event, position));
position.set(Position.KEY_BATTERY, Double.parseDouble(parser.next().replaceAll("\\.", "")) * 0.001);
- position.set(Position.KEY_ODOMETER, parser.nextDouble());
+ position.set(Position.KEY_ODOMETER, parser.nextDouble() * 1000);
position.set(Position.KEY_GPS, parser.nextInt());
position.set(Position.PREFIX_ADC + 1, parser.nextDouble() * 0.001);
position.set(Position.PREFIX_ADC + 2, parser.nextDouble() * 0.001);
@@ -145,23 +208,37 @@ public class LaipacProtocolDecoder extends BaseProtocolDecoder {
position.setNetwork(new Network(CellTower.from(mcc, mnc, lac, cid)));
}
+ String unused = parser.next();
+
String checksum = parser.next();
if (channel != null) {
- if (event.equals("3")) {
- channel.writeAndFlush(new NetworkMessage("$AVCFG,00000000,d*31\r\n", remoteAddress));
- } else if (event.equals("X") || event.equals("4")) {
- channel.writeAndFlush(new NetworkMessage("$AVCFG,00000000,x*2D\r\n", remoteAddress));
- } else if (event.equals("Z")) {
- channel.writeAndFlush(new NetworkMessage("$AVCFG,00000000,z*2F\r\n", remoteAddress));
- } else if (Character.isLowerCase(status.charAt(0))) {
- String response = "$EAVACK," + event + "," + checksum;
- response += Checksum.nmea(response) + "\r\n";
- channel.writeAndFlush(new NetworkMessage(response, remoteAddress));
- }
+
+ sendAcknowledge(status, event, checksum, channel, remoteAddress);
+
+ String devicePassword = Context.getIdentityManager()
+ .getDevicePassword(deviceSession.getDeviceId(), DEFAULT_DEVICE_PASSWORD);
+ sendEventResponse(event, devicePassword, channel, remoteAddress);
}
return position;
}
+ @Override
+ protected Object decode(
+ Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {
+
+ String sentence = (String) msg;
+
+ if (sentence.startsWith("$ECHK")) {
+ if (channel != null) {
+ channel.writeAndFlush(new NetworkMessage(sentence + "\r\n", remoteAddress));
+ }
+ } else if (sentence.startsWith("$AVRMC")) {
+ return decodeAvrmc(sentence, channel, remoteAddress);
+ }
+
+ return null;
+ }
+
}
diff --git a/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
new file mode 100644
index 000000000..dec76b83c
--- /dev/null
+++ b/src/main/java/org/traccar/protocol/LaipacProtocolEncoder.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019 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 org.traccar.StringProtocolEncoder;
+import org.traccar.model.Command;
+import org.traccar.helper.Checksum;
+
+public class LaipacProtocolEncoder extends StringProtocolEncoder {
+
+ @Override
+ protected String formatCommand(Command command, String format, String... keys) {
+ String sentence = super.formatCommand(command, "$" + format, keys);
+ sentence += Checksum.nmea(sentence) + "\r\n";
+ return sentence;
+ }
+
+ @Override
+ protected Object encodeCommand(Command command) {
+
+ initDevicePassword(command, LaipacProtocolDecoder.DEFAULT_DEVICE_PASSWORD);
+
+ switch (command.getType()) {
+ case Command.TYPE_CUSTOM:
+ return formatCommand(command, "{%s}",
+ Command.KEY_DATA);
+ case Command.TYPE_POSITION_SINGLE:
+ return formatCommand(command, "AVREQ,{%s},1",
+ Command.KEY_DEVICE_PASSWORD);
+ case Command.TYPE_REBOOT_DEVICE:
+ return formatCommand(command, "AVRESET,{%s},{%s}",
+ Command.KEY_UNIQUE_ID, Command.KEY_DEVICE_PASSWORD);
+ default:
+ return null;
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/traccar/TestIdentityManager.java b/src/test/java/org/traccar/TestIdentityManager.java
index 0f7405dbd..98297c53c 100644
--- a/src/test/java/org/traccar/TestIdentityManager.java
+++ b/src/test/java/org/traccar/TestIdentityManager.java
@@ -30,6 +30,11 @@ public final class TestIdentityManager implements IdentityManager {
}
@Override
+ public String getDevicePassword(long id, String defaultPassword) {
+ return defaultPassword;
+ }
+
+ @Override
public Position getLastPosition(long deviceId) {
return null;
}
diff --git a/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java
index 31a0434bc..0bbb58490 100644
--- a/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java
+++ b/src/test/java/org/traccar/protocol/LaipacProtocolDecoderTest.java
@@ -15,10 +15,10 @@ public class LaipacProtocolDecoderTest extends ProtocolTest {
verifyNull(decoder, text(
"$AVSYS,99999999,V1.50,SN0000103,32768*15"));
-
+
verifyNull(decoder, text(
"$ECHK,99999999,0*35"));
-
+
verifyNull(decoder, text(
"$AVSYS,MSG00002,14406,7046811160,64*1A"));
@@ -85,10 +85,10 @@ public class LaipacProtocolDecoderTest extends ProtocolTest {
verifyPosition(decoder, text(
"$AVRMC,MSG00002,125517,R,5053.0442,N,00557.8694,E,0.00,0.00,240614,H,4076,167,1,0,0,0D7AB913,020408*75"));
-
+
verifyPosition(decoder, text(
"$AVRMC,MSG00002,043104,p,5114.4664,N,00534.3308,E,0.00,0.00,280614,0,4115,495,1,0,0,0D48C3DC,020408*52"));
-
+
verifyPosition(decoder, text(
"$AVRMC,MSG00002,050601,P,5114.4751,N,00534.3175,E,0.00,0.00,280614,0,4115,495,1,0,0,0D48C3DC,020408*7D"));
@@ -104,22 +104,29 @@ public class LaipacProtocolDecoderTest extends ProtocolTest {
verifyPosition(decoder, text(
"$AVRMC,999999999999999,084514,r,5050.1314,N,00419.9719,E,0.68,306.39,120318,0,3882,84,1,0,0,3EE4A617,020610*4D"));
- //Alarm button
+ // Alarm button
verifyPosition(decoder, text(
"$AVRMC,358174067149865,142945,R,5050.1254,N,00420.0490,E,0.00,0.00,190318,3,3455,119,1,0,0,3EE4A617,020610*53"));
- //G-Sensor
+ // G-Sensor
verifyPosition(decoder, text(
"$AVRMC,358174067149865,143407,R,5050.1254,N,00420.0490,E,0.00,0.00,190318,8,3455,119,1,0,0,3EE4A617,020610*52"));
- //Powered off
+ // Powered off
verifyPosition(decoder, text(
"$AVRMC,358174067149865,143648,A,5050.1141,N,00420.0525,E,1.24,174.38,190318,H,3455,119,1,0,0,3EE4A617,020610*3E"));
- //No network
+ // No network
verifyPosition(decoder, text(
"$AVRMC,358174067149865,143747,R,5050.1124,N,00420.0542,E,1.34,161.96,190318,a,3416,119,1,0,0*7D"));
+ // Zero LAC, CID, MCC, MNC
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143747,P,5050.1124,N,00420.0542,E,1.34,161.96,190318,A,3416,119,1,0,0,0,0*5F"));
+
+ // New unknown parameters
+ verifyPosition(decoder, text(
+ "$AVRMC,358174067149865,143747,P,5050.1124,N,00420.0542,E,1.34,161.96,190318,A,3416,119,1,0,0,0,0,0,0*5F"));
}
}