Add code in H02ProtocolDecoder,java
To build the server binary file (JAR) use following command: ./gradlew assemble
More info here: https://www.traccar.org/build/
PATTERN:
private static final Pattern PATTERN_V1 = new PatternBuilder()
.text("*HQ,")
.number("(d{10}),")
.text("V1,")
.number("(dd)(dd)(dd),")
.expression("([AV]),")
.number("(dd)(dd.d+),")
.expression("([NS]),")
.number("(ddd)(dd.d+),")
.expression("([EW]),")
.number("(d+.d+),")
.number("(d+),")
.number("(dd)(dd)(dd),")
.expression("([^,]+),")
.number("(d+),")
.number("(d+),")
.number("(d+),")
.number("(d+),")
.number("(d+)")
.text("#")
.compile();
private Position decodeV1(String sentence, Channel channel, SocketAddress remoteAddress) {
Parser parser = new Parser(PATTERN_V1, sentence);
if (!parser.matches()) {
return null;
}
String id = parser.next();
DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id);
if (deviceSession == null) {
return null;
}
Position position = new Position(getProtocolName());
position.setDeviceId(deviceSession.getDeviceId());
int hours = parser.nextInt();
int minutes = parser.nextInt();
int seconds = parser.nextInt();
DateBuilder dateBuilder = new DateBuilder();
dateBuilder.setTime(hours, minutes, seconds);
String validity = parser.next();
position.setValid("A".equals(validity));
int latDegrees = Integer.parseInt(parser.next());
double latMinutes = Double.parseDouble(parser.next());
String latHem = parser.next();
double latitude = latDegrees + (latMinutes / 60);
if ("S".equalsIgnoreCase(latHem)) {
latitude = -latitude;
}
position.setLatitude(latitude);
int lonDegrees = Integer.parseInt(parser.next());
double lonMinutes = Double.parseDouble(parser.next());
String lonHem = parser.next();
double longitude = lonDegrees + (lonMinutes / 60);
if ("W".equalsIgnoreCase(lonHem)) {
longitude = -longitude;
}
position.setLongitude(longitude);
position.setSpeed(parser.nextDouble());
position.setCourse(parser.nextDouble());
int day = parser.nextInt();
int month = parser.nextInt();
int year = parser.nextInt();
dateBuilder.setDateReverse(day, month, year);
position.setTime(dateBuilder.getDate());
sendResponse(channel, remoteAddress, id, "V1");
String status = parser.next();
long statusValue = Long.parseLong(status, 16);
int b1 = (int) ((statusValue >> 24) & 0xFF);
int b2 = (int) ((statusValue >> 16) & 0xFF);
int b3 = (int) ((statusValue >> 8) & 0xFF);
int b4 = (int) (statusValue & 0xFF);
if (((b1 >> 2) & 1) == 0) {
position.addAlarm(Position.ALARM_BLIND_GPS_IN);
}
if (((b2 >> 1) & 1) == 0) {
position.addAlarm(Position.ALARM_VIBRATION);
}
if (((b4 >> 1) & 1) == 0) {
position.addAlarm(Position.ALARM_SOS);
}
if (((b4 >> 2) & 1) == 0) {
position.addAlarm(Position.ALARM_OVERSPEED);
}
if (((b2 >> 3) & 1) == 0) {
position.addAlarm(Position.ALARM_POWER_CUT);
}
position.set(Position.KEY_STATUS, statusValue);
int mcc = parser.nextInt();
int mnc = parser.nextInt();
int lac = parser.nextInt();
int cellId = parser.nextInt();
position.setNetwork(new Network(CellTower.from(mcc, mnc, lac, cellId)));
int battery = parser.nextInt();
position.set(Position.KEY_BATTERY_LEVEL, decodeBattery(battery));
return position;
}
Dont forget to add at bottom of code this line under case "VP1" -> decodeVp1(sentence, channel, remoteAddress);
case "V1" -> decodeV1(sentence, channel, remoteAddress);
Add code in H02ProtocolDecoder,java
To build the server binary file (JAR) use following command: ./gradlew assemble
More info here: https://www.traccar.org/build/
PATTERN:
private static final Pattern PATTERN_V1 = new PatternBuilder() .text("*HQ,") .number("(d{10}),") // IMEI: 1234567899 .text("V1,") .number("(dd)(dd)(dd),") // Time: 085548 -> groups: 08, 55, 48 .expression("([AV]),") // Validity: A (or V) .number("(dd)(dd.d+),") // Latitude: 4342.6075 -> groups: 43, 42.6075 .expression("([NS]),") // Latitude hemisphere: N .number("(ddd)(dd.d+),") // Longitude: 01555.2399 -> groups: 015, 55.2399 .expression("([EW]),") // Longitude hemisphere: E .number("(d+.d+),") // Speed: 003.04 .number("(d+),") // Course: 061 .number("(dd)(dd)(dd),") // Date: 010325 -> groups: 01, 03, 25 .expression("([^,]+),") // Status: FFFFF9FF .number("(d+),") // MCC: 219 .number("(d+),") // MNC: 10 .number("(d+),") // LAC: 442 .number("(d+),") // Cell ID: 1059135 .number("(d+)") // Battery Level: 28 .text("#") .compile();
private Position decodeV1(String sentence, Channel channel, SocketAddress remoteAddress) { // Create a parser based on the PATTERN_V1 pattern we defined. Parser parser = new Parser(PATTERN_V1, sentence); if (!parser.matches()) { return null; } // Group 1: IMEI (e.g. "1234567899") String id = parser.next(); DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, id); if (deviceSession == null) { return null; } Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); // Groups 2-4: Time (hh, mm, ss) from "085548" int hours = parser.nextInt(); int minutes = parser.nextInt(); int seconds = parser.nextInt(); DateBuilder dateBuilder = new DateBuilder(); dateBuilder.setTime(hours, minutes, seconds); // Group 5: Validity (e.g. "A") String validity = parser.next(); position.setValid("A".equals(validity)); // Groups 6-8: Latitude (e.g. "4342.6075", "N") int latDegrees = Integer.parseInt(parser.next()); double latMinutes = Double.parseDouble(parser.next()); String latHem = parser.next(); double latitude = latDegrees + (latMinutes / 60); if ("S".equalsIgnoreCase(latHem)) { latitude = -latitude; } position.setLatitude(latitude); // Groups 9-11: Longitude (e.g. "01555.2399", "E") int lonDegrees = Integer.parseInt(parser.next()); double lonMinutes = Double.parseDouble(parser.next()); String lonHem = parser.next(); double longitude = lonDegrees + (lonMinutes / 60); if ("W".equalsIgnoreCase(lonHem)) { longitude = -longitude; } position.setLongitude(longitude); // Group 12: Speed (e.g. "003.04") position.setSpeed(parser.nextDouble()); // Group 13: Course (e.g. "061") position.setCourse(parser.nextDouble()); // Groups 14-16: Date (dd, mm, yy) from "010325" int day = parser.nextInt(); int month = parser.nextInt(); int year = parser.nextInt(); dateBuilder.setDateReverse(day, month, year); position.setTime(dateBuilder.getDate()); sendResponse(channel, remoteAddress, id, "V1"); // Group 17: Status (e.g. "FFFFF9FF") String status = parser.next(); long statusValue = Long.parseLong(status, 16); // Directly decode the status bits for alarms int b1 = (int) ((statusValue >> 24) & 0xFF); // First byte int b2 = (int) ((statusValue >> 16) & 0xFF); // Second byte int b3 = (int) ((statusValue >> 8) & 0xFF); // Third byte int b4 = (int) (statusValue & 0xFF); // Fourth byte // Bit 2: Blind record alarm if (((b1 >> 2) & 1) == 0) { position.addAlarm(Position.ALARM_BLIND_GPS_IN); } // Vibration alarm: check Byte2, bit 1 if (((b2 >> 1) & 1) == 0) { position.addAlarm(Position.ALARM_VIBRATION); } // SOS alarm: check Byte4, bit 1 if (((b4 >> 1) & 1) == 0) { position.addAlarm(Position.ALARM_SOS); } // Overspeed alarm: check Byte4, bit 2 if (((b4 >> 2) & 1) == 0) { position.addAlarm(Position.ALARM_OVERSPEED); } // Power cut alarm: check Byte2, bit 3 if (((b2 >> 3) & 1) == 0) { position.addAlarm(Position.ALARM_POWER_CUT); } // Save the full status value for reference position.set(Position.KEY_STATUS, statusValue); // Groups 18-21: Network info (MCC, MNC, LAC, Cell ID) int mcc = parser.nextInt(); int mnc = parser.nextInt(); int lac = parser.nextInt(); int cellId = parser.nextInt(); position.setNetwork(new Network(CellTower.from(mcc, mnc, lac, cellId))); // Group 22: Battery Level (percentage, e.g. "28") int battery = parser.nextInt(); position.set(Position.KEY_BATTERY_LEVEL, decodeBattery(battery)); return position; }
Dont forget to add at bottom of code this line under case "VP1" -> decodeVp1(sentence, channel, remoteAddress);
case "V1" -> decodeV1(sentence, channel, remoteAddress);