aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar
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/main/java/org/traccar
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/main/java/org/traccar')
-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
6 files changed, 188 insertions, 42 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;
+ }
+
+ }
+
+}