Skip to content

Commit

Permalink
Implement VLT protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
tananaev committed Mar 26, 2023
1 parent 7c67b6f commit ac60477
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 1 deletion.
2 changes: 1 addition & 1 deletion gradle/checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
<module name="EmptyStatement"/>
<module name="EqualsHashCode"/>
<module name="IllegalInstantiation"/>
<module name="InnerAssignment"/>
<!--<module name="InnerAssignment"/>-->
<module name="MissingSwitchDefault"/>
<module name="SimplifyBooleanExpression"/>
<module name="SimplifyBooleanReturn"/>
Expand Down
1 change: 1 addition & 0 deletions setup/default.xml
Original file line number Diff line number Diff line change
Expand Up @@ -287,5 +287,6 @@
<entry key='ndtpv6.port'>5243</entry>
<entry key='g1rus.port'>5244</entry>
<entry key='rftrack.port'>5245</entry>
<entry key='vlt.port'>5246</entry>

</properties>
43 changes: 43 additions & 0 deletions src/main/java/org/traccar/protocol/VltProtocol.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2023 Anton Tananaev ([email protected])
*
* 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.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import org.traccar.BaseProtocol;
import org.traccar.PipelineBuilder;
import org.traccar.TrackerServer;
import org.traccar.config.Config;

import javax.inject.Inject;

public class VltProtocol extends BaseProtocol {

@Inject
public VltProtocol(Config config) {
addServer(new TrackerServer(config, getName(), false) {
@Override
protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) {
pipeline.addLast(new HttpResponseEncoder());
pipeline.addLast(new HttpRequestDecoder());
pipeline.addLast(new HttpObjectAggregator(65535));
pipeline.addLast(new VltProtocolDecoder(VltProtocol.this));
}
});
}

}
137 changes: 137 additions & 0 deletions src/main/java/org/traccar/protocol/VltProtocolDecoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* Copyright 2023 Anton Tananaev ([email protected])
*
* 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.channel.Channel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder;
import org.traccar.BaseHttpProtocolDecoder;
import org.traccar.Protocol;
import org.traccar.helper.Parser;
import org.traccar.helper.PatternBuilder;
import org.traccar.model.CellTower;
import org.traccar.model.Network;
import org.traccar.model.Position;
import org.traccar.session.DeviceSession;

import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;

public class VltProtocolDecoder extends BaseHttpProtocolDecoder {

public VltProtocolDecoder(Protocol protocol) {
super(protocol);
}

private static final Pattern PATTERN = new PatternBuilder()
.number("(dd)") // alert id
.expression("([HL])") // history
.number("([01])") // validity
.number("(dd)(dd)(dd)") // date (ddmmyy)
.number("(dd)(dd)(dd)") // time (hhmmss)
.number("(d{3}.d{6})([NS])") // latitude
.number("(d{3}.d{6})([EW])") // longitude
.number("(d{3})") // mcc
.expression("(x*[0-9]+)") // mnc
.number("(x{4})") // lac
.number("(d{9})") // cid
.number("(d{3}.d{2})") // speed
.number("(d{3}.d{2})") // course
.number("(d{2})") // satellites
.number("(d{2})") // hdop
.number("(d{2})") // rssi
.number("([01])") // ignition
.number("([01])") // charging
.expression("([HMS])") // vehicle mode
.compile();

private Position decodePosition(DeviceSession deviceSession, String sentence) {

Parser parser = new Parser(PATTERN, sentence);
if (!parser.matches()) {
return null;
}

Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());

position.set(Position.KEY_EVENT, parser.nextInt());
position.set(Position.KEY_ARCHIVE, parser.next().equals("H") ? true : null);

position.setValid(parser.nextInt() > 0);
position.setTime(parser.nextDateTime(Parser.DateTimeFormat.DMY_HMS));
position.setLatitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));
position.setLongitude(parser.nextCoordinate(Parser.CoordinateFormat.DEG_HEM));

int mcc = parser.nextInt();
int mnc = Integer.parseInt(parser.next().replaceAll("x", ""));
int lac = parser.nextHexInt();
int cid = parser.nextInt();

position.setSpeed(parser.nextDouble());
position.setCourse(parser.nextDouble());

position.set(Position.KEY_SATELLITES, parser.nextInt());
position.set(Position.KEY_HDOP, parser.nextInt());

position.setNetwork(new Network(CellTower.from(mcc, mnc, lac, cid, parser.nextInt())));

position.set(Position.KEY_IGNITION, parser.nextInt() > 0);
position.set(Position.KEY_CHARGE, parser.nextInt() > 0);
position.set(Position.KEY_MOTION, parser.next().equals("M"));

return position;
}

@Override
protected Object decode(
Channel channel, SocketAddress remoteAddress, Object msg) throws Exception {

FullHttpRequest request = (FullHttpRequest) msg;
QueryStringDecoder decoder = new QueryStringDecoder(
request.content().toString(StandardCharsets.US_ASCII), false);
String sentence = decoder.parameters().get("vltdata").iterator().next();

int index = 0;
String type = sentence.substring(index, index += 3);
String imei = sentence.substring(index, index += 15);

DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei);
if (deviceSession == null) {
sendResponse(channel, HttpResponseStatus.BAD_REQUEST);
return null;
}

sendResponse(channel, HttpResponseStatus.OK);

switch (type) {
case "NRM":
return decodePosition(deviceSession, sentence.substring(3 + 15));
default:
List<Position> positions = new LinkedList<>();
int count = Integer.parseInt(sentence.substring(index, index += 3));
for (int i = 0; i < count; i++) {
positions.add(decodePosition(deviceSession, sentence.substring(index, index += 78)));
}
return positions;
}
}

}
22 changes: 22 additions & 0 deletions src/test/java/org/traccar/protocol/VltProtocolDecoderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.traccar.protocol;

import io.netty.handler.codec.http.HttpMethod;
import org.junit.jupiter.api.Test;
import org.traccar.ProtocolTest;

public class VltProtocolDecoderTest extends ProtocolTest {

@Test
public void testDecode() throws Exception {

var decoder = inject(new VltProtocolDecoder(null));

verifyPosition(decoder, request(HttpMethod.POST, "/",
buffer("vltdata=NRM12345678901234501L1060418102230023.125503N080.068033E4041231234123456789070.48120.5025273011M")));

verifyPositions(decoder, request(HttpMethod.POST, "/",
buffer("vltdata=BTH86123004167306301301L1240323181909009.226018N076.794980E404x19601d000037596000.00198.7013011401S02H1240323181807009.226018N076.794980E404x72090a000000000000.00198.7013011101S02H1240323181707009.226018N076.794980E404x72090a000014598000.00198.7013011101S02H1240323181605009.226018N076.794982E404x72090a000014596000.00198.7013011101S02H1240323181504009.226018N076.794982E404x72090a000014596000.00198.7013010901S02H1240323181402009.226018N076.794980E404x72090a000014596001.67198.0013021301S02H1240323181306009.226010N076.794980E404x72090a000014596000.00174.0013021401S02H1240323181155009.226010N076.794980E404x72090a088511008000.00174.0013011201S02H1240323181057009.226010N076.794980E404x72090a000014596000.00174.0013011201S02H1240323180958009.226010N076.794980E404x72090a000014596000.00174.0013021401S02H1240323180858009.226010N076.794980E404x72090a000014596001.48174.0013021201S02H1240323180755009.226005N076.794982E404x72090a000014598000.00164.4013011301S02H1240323180652009.226005N076.794982E404x72090a000014598000.00164.4013021101S")));

}

}

0 comments on commit ac60477

Please sign in to comment.