From f1b3036ab53cc6083ff644990249bf3a75969f70 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Tue, 19 Apr 2022 18:18:35 -0700 Subject: Add Suntech universal commands --- .../traccar/protocol/SuntechProtocolDecoder.java | 7 ++ .../traccar/protocol/SuntechProtocolEncoder.java | 92 ++++++++++++++++------ 2 files changed, 76 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java index 2f8e50c5e..8926f427e 100644 --- a/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/SuntechProtocolDecoder.java @@ -44,6 +44,7 @@ import java.util.TimeZone; public class SuntechProtocolDecoder extends BaseProtocolDecoder { + private boolean universal; private String prefix; private int protocolType; @@ -58,6 +59,10 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { super(protocol); } + public boolean getUniversal() { + return universal; + } + public String getPrefix() { return prefix; } @@ -831,6 +836,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { if (buf.getByte(buf.readerIndex() + 1) == 0) { + universal = true; return decodeBinary(channel, remoteAddress, buf); } else { @@ -841,6 +847,7 @@ public class SuntechProtocolDecoder extends BaseProtocolDecoder { if (prefix.equals("CRR")) { return decodeCrashReport(channel, remoteAddress, buf); } else if (prefix.length() < 5) { + universal = true; return decodeUniversal(channel, remoteAddress, values); } else if (prefix.endsWith("HTE")) { return decodeTravelReport(channel, remoteAddress, values); diff --git a/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java b/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java index 3b4995110..597acaae8 100644 --- a/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java +++ b/src/main/java/org/traccar/protocol/SuntechProtocolEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2015 - 2022 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. @@ -27,49 +27,95 @@ public class SuntechProtocolEncoder extends StringProtocolEncoder { super(protocol); } - private String getPrefix(Channel channel) { - String prefix = "SA200CMD"; + @Override + protected Object encodeCommand(Channel channel, Command command) { + + boolean universal = false; + String prefix = "SA200"; if (channel != null) { SuntechProtocolDecoder protocolDecoder = BasePipelineFactory.getHandler(channel.pipeline(), SuntechProtocolDecoder.class); if (protocolDecoder != null) { + universal = protocolDecoder.getUniversal(); String decoderPrefix = protocolDecoder.getPrefix(); if (decoderPrefix != null && decoderPrefix.length() > 5) { - prefix = decoderPrefix.substring(0, decoderPrefix.length() - 3) + "CMD"; + prefix = decoderPrefix.substring(0, decoderPrefix.length() - 3); } } } - return prefix; - } - - @Override - protected Object encodeCommand(Channel channel, Command command) { - String prefix = getPrefix(channel); + if (universal) { + return encodeUniversalCommand(channel, command); + } else { + return encodeLegacyCommand(channel, prefix, command); + } + } + protected Object encodeUniversalCommand(Channel channel, Command command) { switch (command.getType()) { case Command.TYPE_REBOOT_DEVICE: - return formatCommand(command, prefix + ";%s;02;Reboot\r", Command.KEY_UNIQUE_ID); + return formatCommand(command, "CMD;%s;03;03\r", Command.KEY_UNIQUE_ID); case Command.TYPE_POSITION_SINGLE: - return formatCommand(command, prefix + ";%s;02;\r", Command.KEY_UNIQUE_ID); + return formatCommand(command, "CMD;%s;03;01\r", Command.KEY_UNIQUE_ID); case Command.TYPE_OUTPUT_CONTROL: - if (command.getAttributes().containsKey(Command.KEY_DATA)) { - if (command.getAttributes().get(Command.KEY_DATA).equals("1")) { - return formatCommand(command, prefix + ";%s;02;Enable%s\r", - Command.KEY_UNIQUE_ID, Command.KEY_INDEX); - } else { - return formatCommand(command, prefix + ";%s;02;Disable%s\r", - Command.KEY_UNIQUE_ID, Command.KEY_INDEX); + if (command.getAttributes().get(Command.KEY_DATA).equals("1")) { + switch (command.getString(Command.KEY_INDEX)) { + case "1": + return formatCommand(command, "CMD;%s;04;01\r", Command.KEY_UNIQUE_ID); + case "2": + return formatCommand(command, "CMD;%s;04;03\r", Command.KEY_UNIQUE_ID); + case "3": + return formatCommand(command, "CMD;%s;04;09\r", Command.KEY_UNIQUE_ID); + default: + return null; } + } else { + switch (command.getString(Command.KEY_INDEX)) { + case "1": + return formatCommand(command, "CMD;%s;04;02\r", Command.KEY_UNIQUE_ID); + case "2": + return formatCommand(command, "CMD;%s;04;04\r", Command.KEY_UNIQUE_ID); + case "3": + return formatCommand(command, "CMD;%s;04;10\r", Command.KEY_UNIQUE_ID); + default: + return null; + } + } + case Command.TYPE_ENGINE_STOP: + return formatCommand(command, "CMD;%s;04;01\r", Command.KEY_UNIQUE_ID); + case Command.TYPE_ENGINE_RESUME: + return formatCommand(command, "CMD;%s;02;02\r", Command.KEY_UNIQUE_ID); + case Command.TYPE_ALARM_ARM: + return formatCommand(command, "CMD;%s;04;03\r", Command.KEY_UNIQUE_ID); + case Command.TYPE_ALARM_DISARM: + return formatCommand(command, "CMD;%s;04;04\r", Command.KEY_UNIQUE_ID); + default: + return null; + } + } + + protected Object encodeLegacyCommand(Channel channel, String prefix, Command command) { + switch (command.getType()) { + case Command.TYPE_REBOOT_DEVICE: + return formatCommand(command, prefix + "CMD;%s;02;Reboot\r", Command.KEY_UNIQUE_ID); + case Command.TYPE_POSITION_SINGLE: + return formatCommand(command, prefix + "CMD;%s;02;\r", Command.KEY_UNIQUE_ID); + case Command.TYPE_OUTPUT_CONTROL: + if (command.getAttributes().get(Command.KEY_DATA).equals("1")) { + return formatCommand(command, prefix + "CMD;%s;02;Enable%s\r", + Command.KEY_UNIQUE_ID, Command.KEY_INDEX); + } else { + return formatCommand(command, prefix + "CMD;%s;02;Disable%s\r", + Command.KEY_UNIQUE_ID, Command.KEY_INDEX); } case Command.TYPE_ENGINE_STOP: - return formatCommand(command, prefix + ";%s;02;Enable1\r", Command.KEY_UNIQUE_ID); + return formatCommand(command, prefix + "CMD;%s;02;Enable1\r", Command.KEY_UNIQUE_ID); case Command.TYPE_ENGINE_RESUME: - return formatCommand(command, prefix + ";%s;02;Disable1\r", Command.KEY_UNIQUE_ID); + return formatCommand(command, prefix + "CMD;%s;02;Disable1\r", Command.KEY_UNIQUE_ID); case Command.TYPE_ALARM_ARM: - return formatCommand(command, prefix + ";%s;02;Enable2\r", Command.KEY_UNIQUE_ID); + return formatCommand(command, prefix + "CMD;%s;02;Enable2\r", Command.KEY_UNIQUE_ID); case Command.TYPE_ALARM_DISARM: - return formatCommand(command, prefix + ";%s;02;Disable2\r", Command.KEY_UNIQUE_ID); + return formatCommand(command, prefix + "CMD;%s;02;Disable2\r", Command.KEY_UNIQUE_ID); default: return null; } -- cgit v1.2.3 From b574e5398a1dd4b4bd17b4b6d3023331dbf43882 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Tue, 19 Apr 2022 20:48:08 -0700 Subject: Support VXT-01 protocol --- .../org/traccar/protocol/Gt06ProtocolDecoder.java | 117 ++++++++++++++------- .../traccar/protocol/Gt06ProtocolDecoderTest.java | 4 + 2 files changed, 81 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index 4d3448e84..feacc1ae8 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -113,6 +113,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { public static final int MSG_MULTIMEDIA_2 = 0x41; public static final int MSG_ALARM = 0x95; + private enum Variant { + VXT01, + STANDARD, + } + + private Variant variant; + private static boolean isSupported(int type) { return hasGps(type) || hasLbs(type) || hasStatus(type); } @@ -333,8 +340,15 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case 4: position.set(Position.KEY_ALARM, Position.ALARM_SOS); break; + case 6: + position.set(Position.KEY_ALARM, Position.ALARM_GEOFENCE); + break; case 7: - position.set(Position.KEY_ALARM, Position.ALARM_REMOVING); + if (variant == Variant.VXT01) { + position.set(Position.KEY_ALARM, Position.ALARM_OVERSPEED); + } else { + position.set(Position.KEY_ALARM, Position.ALARM_REMOVING); + } break; default: break; @@ -346,7 +360,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_POWER, buf.readUnsignedShort() * 0.01); } position.set(Position.KEY_RSSI, buf.readUnsignedByte()); - position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); return true; } @@ -445,7 +458,7 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return position; } - private Object decodeBasic(Channel channel, SocketAddress remoteAddress, ByteBuf buf) throws Exception { + private Object decodeBasic(Channel channel, SocketAddress remoteAddress, ByteBuf buf) { int length = buf.readUnsignedByte(); int dataLength = length - 5; @@ -903,44 +916,47 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { if (hasStatus(type)) { decodeStatus(position, buf, true); + position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); } - if (type == MSG_GPS_LBS_1 && buf.readableBytes() > 75 + 6) { - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); - String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); - buf.readUnsignedByte(); // alarm - buf.readUnsignedByte(); // swiped - position.set("driverLicense", data.trim()); - } - - if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 18) { - decodeStatus(position, buf, false); - position.set("oil", buf.readUnsignedShort()); - int temperature = buf.readUnsignedByte(); - if (BitUtil.check(temperature, 7)) { - temperature = -BitUtil.to(temperature, 7); - } - position.set(Position.PREFIX_TEMP + 1, temperature); - position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 10); - } - - if (type == MSG_GPS_LBS_1 && buf.readableBytes() == 2 + 6) { - int mask = buf.readUnsignedShort(); - position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7)); - position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6)); - if (BitUtil.check(mask, 8 + 4)) { - int value = BitUtil.to(mask, 8 + 1); - if (BitUtil.check(mask, 8 + 1)) { - value = -value; - } - position.set(Position.PREFIX_TEMP + 1, value); - } else { - int value = BitUtil.to(mask, 8 + 2); - if (BitUtil.check(mask, 8 + 5)) { - position.set(Position.PREFIX_ADC + 1, value); + if (type == MSG_GPS_LBS_1) { + if (buf.readableBytes() > 75 + 6) { + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt()); + String data = buf.readCharSequence(buf.readUnsignedByte(), StandardCharsets.US_ASCII).toString(); + buf.readUnsignedByte(); // alarm + buf.readUnsignedByte(); // swiped + position.set("driverLicense", data.trim()); + } else if (buf.readableBytes() == 8) { + int mask = buf.readUnsignedShort(); + position.set(Position.KEY_IGNITION, BitUtil.check(mask, 8 + 7)); + position.set(Position.PREFIX_IN + 2, BitUtil.check(mask, 8 + 6)); + if (BitUtil.check(mask, 8 + 4)) { + int value = BitUtil.to(mask, 8 + 1); + if (BitUtil.check(mask, 8 + 1)) { + value = -value; + } + position.set(Position.PREFIX_TEMP + 1, value); } else { - position.set(Position.PREFIX_ADC + 1, value * 0.1); + int value = BitUtil.to(mask, 8 + 2); + if (BitUtil.check(mask, 8 + 5)) { + position.set(Position.PREFIX_ADC + 1, value); + } else { + position.set(Position.PREFIX_ADC + 1, value * 0.1); + } } + } else if (buf.readableBytes() == 11) { + decodeStatus(position, buf, false); + buf.readUnsignedByte(); // alarm extension + } else if (buf.readableBytes() == 18) { + decodeStatus(position, buf, false); + position.set(Position.KEY_ALARM, decodeAlarm(buf.readUnsignedByte())); + position.set("oil", buf.readUnsignedShort()); + int temperature = buf.readUnsignedByte(); + if (BitUtil.check(temperature, 7)) { + temperature = -BitUtil.to(temperature, 7); + } + position.set(Position.PREFIX_TEMP + 1, temperature); + position.set(Position.KEY_ODOMETER, buf.readUnsignedInt() * 10); } } @@ -1354,21 +1370,42 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return null; } + private void decodeVariant(ByteBuf buf) { + int header = buf.getUnsignedShort(buf.readerIndex()); + int length; + int type; + if (header == 0x7878) { + length = buf.getUnsignedByte(buf.readerIndex() + 2); + type = buf.getUnsignedByte(buf.readerIndex() + 2 + 1); + } else { + length = buf.getUnsignedShort(buf.readerIndex() + 2); + type = buf.getUnsignedByte(buf.readerIndex() + 2 + 2); + } + + if (header == 0x7878 && type == MSG_GPS_LBS_1 && length == 0x24) { + variant = Variant.VXT01; + } else if (header == 0x7878 && type == MSG_GPS_LBS_STATUS_1 && length == 0x24) { + variant = Variant.VXT01; + } else { + variant = Variant.STANDARD; + } + } + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; + decodeVariant(buf); + int header = buf.readShort(); if (header == 0x7878) { return decodeBasic(channel, remoteAddress, buf); - } else if (header == 0x7979) { + } else { return decodeExtended(channel, remoteAddress, buf); } - - return null; } } diff --git a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java index ad65ec960..b65b6709e 100644 --- a/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java +++ b/src/test/java/org/traccar/protocol/Gt06ProtocolDecoderTest.java @@ -17,6 +17,10 @@ public class Gt06ProtocolDecoderTest extends ProtocolTest { verifyNull(decoder, binary( "78780D01086471700328358100093F040D0A")); + verifyAttribute(decoder, binary( + "7878241216040e102c22cf00915ffb04c6016300195a02d402283b00753f400571040001dda4880d0a"), + Position.KEY_IGNITION, false); + verifyNotNull(decoder, binary( "787831241603060c231e000194620213ee00606a3413ee0060692e000000000000000000000000000000000000000000003a0cb70d0a")); -- cgit v1.2.3