diff options
3 files changed, 397 insertions, 0 deletions
diff --git a/src/main/java/org/traccar/protocol/TranSyncProtocol.java b/src/main/java/org/traccar/protocol/TranSyncProtocol.java new file mode 100644 index 000000000..0634642df --- /dev/null +++ b/src/main/java/org/traccar/protocol/TranSyncProtocol.java @@ -0,0 +1,22 @@ +package org.traccar.protocol; + +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import javax.inject.Inject; + +public class TranSyncProtocol extends BaseProtocol { + + @Inject + public TranSyncProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new TranSyncProtocolDecoder(TranSyncProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/TranSyncProtocolDecoder.java b/src/main/java/org/traccar/protocol/TranSyncProtocolDecoder.java new file mode 100644 index 000000000..7cafc09ee --- /dev/null +++ b/src/main/java/org/traccar/protocol/TranSyncProtocolDecoder.java @@ -0,0 +1,353 @@ +package org.traccar.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.UnitsConverter; +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.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class TranSyncProtocolDecoder extends BaseProtocolDecoder { + + public TranSyncProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private boolean isOptionalParameters; + private boolean extended; + private final String STX = "3A3A"; + private final String ETX = "2323"; + private String lac; + private String deviceId; + private Date datePacket; + private double latitude; + private double longitude; + private double speed; + private int course; + private int mobileNetworkCode; + private int gsmSignalStrength; + private double batteryVoltage; + private int satellitesNumber; + private int hdopProtocol; + private short adcVoltageInMilliVolts; + private String rfidTagName; + private int adc2; + private int cellIdInt; + private int lacInt; + private int odometer; + private boolean isGpsFix; + private boolean isLiveData; + private boolean hasGpsAlert; + private boolean isIgnitionOff; + private boolean isPowerOff; + private int deviceAlert; + private String gpsTrackerModel; + private boolean isOutPutTwo; + private boolean isOutPutOne; + private boolean isInPutThree; + + + + public String getLac() { + return lac; + } + + public void setLac(String lac) { + this.lac = lac; + } + + public String getDeviceId() { + return deviceId; + } + + public void setDeviceId(String deviceId) { + this.deviceId = deviceId; + } + + public void setDatePacket(Date datePacket) { + this.datePacket = datePacket; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } + + public int getCourse() { + return course; + } + + public void setCourse(int course) { + this.course = course; + } + + public void setCellId(String cellId) { + } + + public int getAdc2() { + return adc2; + } + + public void setAdc2(String adc2) { + this.adc2 = Integer.parseInt(adc2); + } + + public double getSpeed() { + return speed; + } + + public void setSpeed(double speed) { + this.speed = speed; + } + + public void setTimestampPacket(int year, int month, int day, int hours, int minutes, int seconds) { + + String dataString = ""+year + "-" + month + "-" + day + " " + hours + ":" + minutes + ":" + seconds; + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss"); + simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + try { + Date deviceTime = simpleDateFormat.parse(dataString); + long timestampPacket = deviceTime.toInstant().toEpochMilli(); + this.setDatePacket(deviceTime); + + } catch (ParseException e) { + e.printStackTrace(); + } + } + + protected void setDeviceAlert(Position position, int alert) { + switch (alert) { + case 10: + position.set(Position.ALARM_SOS, true); + break; + case 11: + position.set(Position.ALARM_SOS, false); + break; + case 16: + position.set(Position.KEY_EVENT, 16); + break; + case 3: + position.set("distressCutOff", true); + break; + case 22: + position.set("tilt", true); + break; + case 9: + position.set(Position.KEY_EVENT, 9); + case 17: + position.set(Position.ALARM_OVERSPEED, true); + break; + case 13: + position.set(Position.ALARM_BRAKING, true); + break; + case 14: + position.set(Position.ALARM_ACCELERATION, true); + break; + case 15: + position.set(Position.KEY_EVENT, 15); + break; + case 23: + position.set(Position.ALARM_ACCIDENT, true); + break; + case 12: + position.set(Position.KEY_EVENT, 12); + break; + case 6: + position.set(Position.ALARM_POWER_RESTORED, true); + break; + case 4: + position.set(Position.ALARM_LOW_BATTERY, true); + break; + case 5: + position.set(Position.KEY_EVENT, 5); + break; + + } + + } + + protected void setInstanceParameters(Object msg) throws Exception { + ByteBuf buf = (ByteBuf) msg; + + if (ByteBufUtil.hexDump(buf, 0, 2).toUpperCase().equals(this.STX)) { + buf.readUnsignedShort(); + } + int packetLength = buf.readByte(); + this.lacInt = buf.readUnsignedShort(); + this.deviceId = ByteBufUtil.hexDump(buf, buf.readerIndex(), 8).toUpperCase().trim().replaceFirst("^0+(?!$)", ""); + buf.readBytes(8); + int informationSerialNumber = buf.readUnsignedShort(); + int protocolNumber = buf.readUnsignedByte(); + // Information Content + int year = buf.readUnsignedByte(); + int month = buf.readUnsignedByte(); + int day = buf.readUnsignedByte(); + int hour = buf.readUnsignedByte(); + int minute = buf.readUnsignedByte(); + int second = buf.readUnsignedByte(); + this.setTimestampPacket(year, month, day, hour, minute, second); + this.latitude = (long) buf.readUnsignedInt() / 1800000.0; + this.longitude = (long) buf.readUnsignedInt() / 1800000.0; + this.speed = UnitsConverter.knotsFromKph(buf.readUnsignedByte()); + this.course = buf.readUnsignedShort(); + this.mobileNetworkCode = buf.readUnsignedByte(); + this.cellIdInt = buf.readUnsignedShort(); + // Status Bytes + int zeroByte = buf.readUnsignedByte(); + this.isOutPutTwo = BitUtil.check(zeroByte, 0); + this.isOutPutOne = BitUtil.check(zeroByte, 1); + this.isInPutThree = BitUtil.check(zeroByte, 2); + this.isPowerOff = BitUtil.check(zeroByte, 3); + this.isIgnitionOff = BitUtil.check(zeroByte, 4); + boolean isgpsFixed = BitUtil.check(zeroByte, 7); + + + this.isGpsFix = BitUtil.check(zeroByte, 0); + int oneByte = buf.readUnsignedByte(); + this.deviceAlert = buf.readUnsignedByte(); + int threeByte = buf.readUnsignedByte(); + this.isLiveData = (threeByte & 0b10000000) == 0; + this.hasGpsAlert = (threeByte & 0b00100000) != 0; + int trackerType = threeByte & 0b00001111; + + switch (trackerType) { + case 1: + this.gpsTrackerModel = "basic"; + break; + case 2: + this.gpsTrackerModel = "asset"; + break; + case 3: + this.gpsTrackerModel = "bike"; + break; + case 4: + this.gpsTrackerModel = "serial"; + break; + case 5: + this.gpsTrackerModel = "obd"; + break; + case 6: + this.gpsTrackerModel = "l1"; + break; + case 7: + this.gpsTrackerModel = "ais"; + break; + default: + this.gpsTrackerModel = "unknown"; + break; + } + // Additional Info + this.gsmSignalStrength = buf.readUnsignedByte(); + this.batteryVoltage = (double) (buf.readUnsignedByte() / 10); + this.satellitesNumber = buf.readUnsignedByte(); + this.hdopProtocol = buf.readUnsignedByte(); + this.adcVoltageInMilliVolts = (short) buf.readUnsignedShort(); + this.isOptionalParameters = (boolean) (buf.readableBytes() > 2); + // Optional parameters + if (this.isOptionalParameters) { // Always True + int odometerIndex = buf.readUnsignedByte(); + int odometerLength = buf.readUnsignedByte(); + if (odometerLength > 0) { + String odometerReading = ByteBufUtil.hexDump(buf, buf.readerIndex(), odometerLength).toUpperCase().trim().replaceFirst("^0+(?!$)", ""); + this.odometer = Integer.parseInt(odometerReading); + buf.readBytes(odometerLength); + } + if ((buf.readableBytes() > 2)) { + int rfidIndex = buf.readUnsignedByte(); + int rfidLength = buf.readUnsignedByte(); + if(rfidLength > 0) { + this.rfidTagName = ByteBufUtil.hexDump(buf, buf.readerIndex(), rfidLength).toUpperCase().trim(); + buf.readBytes(rfidLength); + } + } + if ((buf.readableBytes() > 5)) { + int adcTwoIndex = buf.readUnsignedByte(); + int adcTwoLength = buf.readUnsignedByte(); + if (adcTwoLength > 0){ + this.adc2 = buf.readUnsignedShort(); + } + } + } + + } + + private Position positionHandler(Channel channel, SocketAddress remoteAddress) { + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, this.deviceId); + if (deviceSession == null) { + return null; + } + Position position = new Position(getProtocolName()); + long devicesessionId = deviceSession.getDeviceId(); + position.setDeviceId(devicesessionId) ; + position.setValid(true); + position.setTime(this.datePacket); + position.setLatitude(this.latitude); + position.setLongitude(this.longitude); + position.setSpeed(this.speed); + position.setCourse(this.course); + position.set("model", this.gpsTrackerModel); + position.set(Position.KEY_HDOP, this.hdopProtocol); + position.set(Position.KEY_BATTERY, this.batteryVoltage); + position.set(Position.KEY_ODOMETER, this.odometer); + position.set(Position.PREFIX_ADC + 1, this.adcVoltageInMilliVolts); + position.set(Position.PREFIX_ADC + 2, this.adc2); + position.set("tag", this.rfidTagName); + position.set(Position.KEY_SATELLITES, this.satellitesNumber); + CellTower cellTower = CellTower.fromLacCid(getConfig(), this.lacInt, this.cellIdInt); + cellTower.setMobileNetworkCode(this.mobileNetworkCode); + cellTower.setSignalStrength(this.gsmSignalStrength); + position.setNetwork(new Network(cellTower)); + if (this.isOutPutOne){ position.set(Position.PREFIX_OUT + 1, this.isOutPutOne);} + if (this.isOutPutTwo){ position.set(Position.PREFIX_OUT + 2, this.isOutPutTwo);} + if (this.isInPutThree){ position.set(Position.PREFIX_IN + 2, this.isInPutThree);} + if (this.isGpsFix) { position.set("gpsFix", this.isGpsFix); } + if (!this.isLiveData) { position.set("restored", true); } + if (this.hasGpsAlert) { + position.set(Position.ALARM_GPS_ANTENNA_CUT, true); + position.set("gpsAlert", true); + } + position.set(Position.KEY_IGNITION, this.isIgnitionOff); + if (isPowerOff){ position.set(Position.ALARM_POWER_OFF, this.isPowerOff );} + position.set(Position.KEY_GPS, true); + if (this.deviceAlert > 0) { setDeviceAlert(position, this.deviceAlert); } + + return position; + } + + @Override + protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + try { + setInstanceParameters(msg); + } + catch (Exception e) { + return null; + } + + return positionHandler(channel, remoteAddress); + } + +} diff --git a/src/test/java/org/traccar/protocol/TranSyncProtocolDecoderTest.java b/src/test/java/org/traccar/protocol/TranSyncProtocolDecoderTest.java new file mode 100644 index 000000000..1b90566d4 --- /dev/null +++ b/src/test/java/org/traccar/protocol/TranSyncProtocolDecoderTest.java @@ -0,0 +1,22 @@ +package org.traccar.protocol; + + +import org.junit.jupiter.api.Test; +import org.traccar.ProtocolTest; + +public class TranSyncProtocolDecoderTest extends ProtocolTest { + + @Test + public void testDecode() throws Exception { + + var decoder = inject(new TranSyncProtocolDecoder(null)); + + + verifyPosition(decoder, binary( + "3a3a2b583f086065705154043900801017050b11190f01623ef40887dff00000c25e9ff707000007152a2d0000000105004794916902050000000000050252ee060200822323")); + + verifyAttributes(decoder, binary( + "3a3a2b583f086065705154043900801017050b11190f01623ef40887dff00000c25e9ff707000007152a2d0000000105004794916902050000000000050252ee060200822323") + ); + } +} |