diff --git a/setup/default.xml b/setup/default.xml index fbe63c87378..3affcfe848d 100644 --- a/setup/default.xml +++ b/setup/default.xml @@ -296,5 +296,6 @@ 5251 5252 5253 - + 5254 + diff --git a/src/main/java/org/traccar/protocol/RadshidProtocol.java b/src/main/java/org/traccar/protocol/RadshidProtocol.java new file mode 100644 index 00000000000..082515c0efa --- /dev/null +++ b/src/main/java/org/traccar/protocol/RadshidProtocol.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 - 2021 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 io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import java.nio.ByteOrder; + +import jakarta.inject.Inject; + +public class RadshidProtocol extends BaseProtocol { + + @Inject + public RadshidProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, 1024, 0, 4, 0, 4, true)); + pipeline.addLast(new RadshidProtocolDecoder(RadshidProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/RadshidProtocolDecoder.java b/src/main/java/org/traccar/protocol/RadshidProtocolDecoder.java new file mode 100644 index 00000000000..85fb98858fc --- /dev/null +++ b/src/main/java/org/traccar/protocol/RadshidProtocolDecoder.java @@ -0,0 +1,172 @@ +/* + * Copyright 2020 - 2021 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 io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import org.traccar.BaseProtocolDecoder; +import org.traccar.session.DeviceSession; +import org.traccar.NetworkMessage; +import org.traccar.Protocol; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; +import org.traccar.helper.Checksum; +import java.net.SocketAddress; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +public class RadshidProtocolDecoder extends BaseProtocolDecoder { + + private static final Checksum.Algorithm CRC16_CCITT_A001 = new Checksum.Algorithm(16, 0xA001, 0x0000, false, false, 0x0000); + + public RadshidProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private static void sendResponse( + Channel channel, SocketAddress remoteAddress, int length, Boolean crc_status) { + + if (channel != null) { + ByteBuf buf = Unpooled.buffer(); + + buf.writeIntLE(0x07); + buf.writeByte(0x82); + buf.writeByte(0x01); + buf.writeInt(length); + buf.writeByte((crc_status==true) ? 0x01:0x00); + + channel.writeAndFlush(new NetworkMessage(buf, remoteAddress)); + } + } + + private String decodeAlarm(int event) + { + switch (event) + { + case 0xD6: // GSM_Jamming_Event + case 0xD7: // GPS_Jamming_Event + return Position.ALARM_JAMMING; + case 0xEB: // SOS_PressEvent + case 0xEC: // SOS_ReleaseEvent + return Position.ALARM_SOS; + case 0xF0: // Battry_Level_Event + return Position.ALARM_LOW_BATTERY; + case 0xDC: // GeofenceEnable_Event + return Position.ALARM_GEOFENCE_ENTER; + case 0xDD: // GeofenceDisable_Event + return Position.ALARM_GEOFENCE_EXIT; + case 0xEE: // OverSpeedEvent + return Position.ALARM_OVERSPEED; + case 0xE2: // DisconnetPowerLineEvent + return Position.ALARM_POWER_CUT; + case 0xE3: // ConnectPowerLineEvent + return Position.ALARM_POWER_RESTORED; + case 0xE5: // AccelerationEvent + return Position.ALARM_ACCELERATION; + + default: + return null; + } + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + + ByteBuf buf = (ByteBuf) msg; + int length = buf.readableBytes(); + + /* read crc from end of packet */ + buf.readerIndex(length - 2); + int packetChecksum = buf.readUnsignedShortLE(); + buf.readerIndex(0); + + /* copy data part of packet */ + ByteBuf packetData = buf.slice(0,buf.readableBytes()-2); + + /* calc data Checksum*/ + int dataChecksum = Checksum.crc16(CRC16_CCITT_A001, packetData.nioBuffer()); + + /* check data crc and packet crc */ + if(dataChecksum != packetChecksum) + { + /* send response to device */ + sendResponse(channel,remoteAddress,length,false); + return null; + } + + byte packet_tag = packetData.readByte(); // Packet Tag + long deviceSerial= (long)packetData.readUnsignedInt(); + byte dataElement = packetData.readByte(); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceSerial)); + if (deviceSession == null || packet_tag != 0x01) { + return null; + } + + /* Send response to device */ + sendResponse(channel,remoteAddress,length,true); + + List positions = new LinkedList<>(); + for (int i = 0; i < dataElement; i++) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + packetData.readByte(); // Event Version + byte event = packetData.readByte(); // Event Code + packetData.readBytes(5); // Driver Id + packetData.readUnsignedShort(); // Total Driving Tim + long timeStamp = packetData.readUnsignedInt(); // Timestamp + byte gpsSpeed = packetData.readByte(); // GPS Speed + packetData.readByte(); // Vehicle Sensor Speed + packetData.readByte(); // Max Speed (GPS) + packetData.readByte(); // Max Speed (Vehicle Sensor) + packetData.readUnsignedShort(); // Engine RPM + packetData.readUnsignedInt(); // Total Traveled Distance (GPS) + packetData.readUnsignedInt(); // otal Traveled Distance (Vehicle Sensor) + byte IOstatus = packetData.readByte(); // IO Status + byte gpsStatus = packetData.readByte(); // GPS Status + long latitude = packetData.readInt(); // Latitude + long longitude= packetData.readInt(); // Longitude + int altitude = packetData.readShort(); //Altitude + int bearing = packetData.readShort(); //Bearing + byte numberOfSatellites = packetData.readByte();// Number Of Satellites + byte PDOP = packetData.readByte(); // PDOP + byte xLen = packetData.readByte(); // Extra Data Length + packetData.readBytes(xLen); // scape extra data + + position.setValid((gpsStatus==0x00) ? true:false); + position.setTime(new Date(timeStamp * 1000L)); + position.setLatitude(latitude * 0.0000001); + position.setLongitude(longitude * 0.0000001); + position.setAltitude(altitude); + position.setSpeed(UnitsConverter.knotsFromKph(gpsSpeed)); + position.setCourse(bearing); + position.set(Position.KEY_SATELLITES,numberOfSatellites); + position.set(Position.KEY_PDOP, PDOP * 0.1); + position.set(Position.KEY_IGNITION, (IOstatus & 0x40)!=0 ? true:false); + position.set(Position.KEY_ALARM, decodeAlarm((int)event)); + + positions.add(position); + } + return positions; + } + +}