diff options
Diffstat (limited to 'src/main/java')
16 files changed, 712 insertions, 35 deletions
diff --git a/src/main/java/org/traccar/api/security/PermissionsService.java b/src/main/java/org/traccar/api/security/PermissionsService.java index a494c0257..ddfaaab94 100644 --- a/src/main/java/org/traccar/api/security/PermissionsService.java +++ b/src/main/java/org/traccar/api/security/PermissionsService.java @@ -170,8 +170,10 @@ public class PermissionsService { || before.getFixedEmail() != after.getFixedEmail()) { if (userId == after.getId()) { checkAdmin(userId); - } else { + } else if (after.getId() > 0) { checkUser(userId, after.getId()); + } else { + checkManager(userId); } } if (before.getFixedEmail() && !before.getEmail().equals(after.getEmail())) { diff --git a/src/main/java/org/traccar/model/ExtendedModel.java b/src/main/java/org/traccar/model/ExtendedModel.java index 0fa1856d1..ef2e3b68f 100644 --- a/src/main/java/org/traccar/model/ExtendedModel.java +++ b/src/main/java/org/traccar/model/ExtendedModel.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 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. @@ -17,6 +17,7 @@ package org.traccar.model; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; public class ExtendedModel extends BaseModel { @@ -31,7 +32,7 @@ public class ExtendedModel extends BaseModel { } public void setAttributes(Map<String, Object> attributes) { - this.attributes = attributes; + this.attributes = Objects.requireNonNullElseGet(attributes, LinkedHashMap::new); } public void set(String key, Boolean value) { diff --git a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java index 741f4b35a..a9d77b46e 100644 --- a/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/FifotrackProtocolDecoder.java @@ -34,6 +34,10 @@ import org.traccar.model.WifiAccessPoint; import java.net.SocketAddress; import java.nio.charset.StandardCharsets; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; import java.util.regex.Pattern; public class FifotrackProtocolDecoder extends BaseProtocolDecoder { @@ -78,7 +82,7 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { .text("$$") .number("d+,") // length .number("(d+),") // imei - .number("x+,") // index + .number("(x+),") // index .text("A03,") // type .number("(d+)?,") // alarm .number("(dd)(dd)(dd)") // date (yymmdd) @@ -137,16 +141,20 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { .number("xx") .compile(); - private void requestPhoto(Channel channel, SocketAddress socketAddress, String imei, String file) { + private void sendResponse(Channel channel, SocketAddress remoteAddress, String imei, String content) { if (channel != null) { - String content = "1,D06," + file + "," + photo.writerIndex() + "," + Math.min(1024, photo.writableBytes()); int length = 1 + imei.length() + 1 + content.length(); String response = String.format("##%02d,%s,%s*", length, imei, content); response += Checksum.sum(response) + "\r\n"; - channel.writeAndFlush(new NetworkMessage(response, socketAddress)); + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); } } + private void requestPhoto(Channel channel, SocketAddress remoteAddress, String imei, String file) { + String content = "1,D06," + file + "," + photo.writerIndex() + "," + Math.min(1024, photo.writableBytes()); + sendResponse(channel, remoteAddress, imei, content); + } + private String decodeAlarm(Integer alarm) { if (alarm != null) { switch (alarm) { @@ -200,11 +208,14 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { return null; } - DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next()); + String imei = parser.next(); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, imei); if (deviceSession == null) { return null; } + String index = parser.next(); + Position position = new Position(getProtocolName()); position.setDeviceId(deviceSession.getDeviceId()); @@ -243,6 +254,11 @@ public class FifotrackProtocolDecoder extends BaseProtocolDecoder { position.setNetwork(network); + DateFormat dateFormat = new SimpleDateFormat("yyMMddHHmmss"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + String response = index + ",A03," + dateFormat.format(new Date()); + sendResponse(channel, remoteAddress, imei, response); + return position; } diff --git a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java index b65e4a4b8..7eeee5efb 100644 --- a/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -411,9 +411,12 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { case 0x11: return Position.ALARM_POWER_OFF; case 0x13: + case 0x25: return Position.ALARM_TAMPERING; case 0x14: return Position.ALARM_DOOR; + case 0x23: + return Position.ALARM_FALL_DOWN; case 0x29: return Position.ALARM_ACCELERATION; case 0x30: @@ -423,8 +426,6 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { return Position.ALARM_CORNERING; case 0x2C: return Position.ALARM_ACCIDENT; - case 0x23: - return Position.ALARM_FALL_DOWN; default: return null; } @@ -986,6 +987,13 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { } else if (subType == 0x05) { + if (buf.readableBytes() >= 6 + 1 + 6) { + DateBuilder dateBuilder = new DateBuilder((TimeZone) deviceSession.get(DeviceSession.KEY_TIMEZONE)) + .setDate(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()) + .setTime(buf.readUnsignedByte(), buf.readUnsignedByte(), buf.readUnsignedByte()); + position.setDeviceTime(dateBuilder.getDate()); + } + int flags = buf.readUnsignedByte(); position.set(Position.KEY_DOOR, BitUtil.check(flags, 0)); position.set(Position.PREFIX_IO + 1, BitUtil.check(flags, 2)); diff --git a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java index 5393c6f74..0639b9dcf 100644 --- a/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/HuabaoProtocolDecoder.java @@ -513,6 +513,18 @@ public class HuabaoProtocolDecoder extends BaseProtocolDecoder { position.set(Position.KEY_MOTION, BitUtil.check(deviceStatus, 2)); position.set("cover", BitUtil.check(deviceStatus, 3)); break; + case 0xE6: + while (buf.readerIndex() < endIndex) { + int sensorIndex = buf.readUnsignedByte(); + buf.skipBytes(6); // mac + position.set( + Position.PREFIX_TEMP + sensorIndex, + buf.readUnsignedByte() + buf.readUnsignedByte() * 0.01); + position.set( + "humidity" + sensorIndex, + buf.readUnsignedByte() + buf.readUnsignedByte() * 0.01); + } + break; case 0xEB: if (buf.getUnsignedShort(buf.readerIndex()) > 200) { Network network = new Network(); diff --git a/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java index 53631bd4e..77158b315 100644 --- a/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NavisProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 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. @@ -24,7 +24,6 @@ import org.traccar.NetworkMessage; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.Checksum; -import org.traccar.helper.Checksum.Algorithm; import org.traccar.helper.DateBuilder; import org.traccar.helper.UnitsConverter; import org.traccar.model.Position; @@ -591,10 +590,8 @@ public class NavisProtocolDecoder extends BaseProtocolDecoder { private void sendFlexReply(Channel channel, ByteBuf data) { if (channel != null) { - ByteBuf cs = Unpooled.buffer(1); - cs.writeByte(Checksum.crc8(new Algorithm(8, 0x31, 0xFF, false, false, 0x00), data.nioBuffer())); - - channel.writeAndFlush(new NetworkMessage(Unpooled.wrappedBuffer(data, cs), channel.remoteAddress())); + data.writeByte(Checksum.crc8(Checksum.CRC8_EGTS, data.nioBuffer())); + channel.writeAndFlush(new NetworkMessage(data, channel.remoteAddress())); } } diff --git a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java index 9122eb362..08b1a8d0f 100644 --- a/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/NavtelecomProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2021 Anton Tananaev (anton@traccar.org) + * Copyright 2021 - 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. @@ -196,7 +196,7 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { for (int j = 0; j < bits.length(); j++) { if (bits.get(j)) { - int value = 0; + int value; switch (j + 1) { case 1: @@ -278,11 +278,11 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { case 42: case 43: value = buf.readUnsignedShortLE(); - position.set("rs485Fuel" + (j + 2 - 38), (value < 65500) ? value : null); + position.set("fuel" + (j + 2 - 38), (value < 65500) ? value : null); break; case 44: value = buf.readUnsignedShortLE(); - position.set("rs232Fuel", (value < 65500) ? value : null); + position.set(Position.KEY_FUEL_LEVEL, (value < 65500) ? value : null); break; case 45: case 46: @@ -293,7 +293,17 @@ public class NavtelecomProtocolDecoder extends BaseProtocolDecoder { case 51: case 52: value = buf.readByte(); - position.set(Position.PREFIX_TEMP + (j + 2 - 45), (value != 0x80) ? value : null); + position.set( + Position.PREFIX_TEMP + (j + 2 - 45), + (value != (byte) 0x80) ? value : null); + break; + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + position.set("fuelTemp" + (j + 2 - 78), (int) buf.readByte()); break; default: buf.skipBytes(getItemLength(j + 1)); diff --git a/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java b/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java new file mode 100644 index 000000000..5b610013a --- /dev/null +++ b/src/main/java/org/traccar/protocol/NdtpV6FrameDecoder.java @@ -0,0 +1,32 @@ +/* + * Copyright 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. + * 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.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import org.traccar.BaseFrameDecoder; + +public class NdtpV6FrameDecoder extends BaseFrameDecoder { + + @Override + protected Object decode( + ChannelHandlerContext ctx, Channel channel, ByteBuf buf) throws Exception { + + return buf; + } + +} diff --git a/src/main/java/org/traccar/protocol/NdtpV6Protocol.java b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java new file mode 100644 index 000000000..ce0dbbef2 --- /dev/null +++ b/src/main/java/org/traccar/protocol/NdtpV6Protocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 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. + * 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.string.StringEncoder; +import org.traccar.BaseProtocol; +import org.traccar.PipelineBuilder; +import org.traccar.TrackerServer; +import org.traccar.config.Config; + +import javax.inject.Inject; + +public class NdtpV6Protocol extends BaseProtocol { + + @Inject + public NdtpV6Protocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new StringEncoder()); + pipeline.addLast(new NdtpV6ProtocolDecoder(NdtpV6Protocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java b/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java new file mode 100644 index 000000000..35cb0bae8 --- /dev/null +++ b/src/main/java/org/traccar/protocol/NdtpV6ProtocolDecoder.java @@ -0,0 +1,203 @@ +/* + * Copyright 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. + * 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.NetworkMessage; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.Checksum; +import org.traccar.helper.UnitsConverter; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.util.Date; + +public class NdtpV6ProtocolDecoder extends BaseProtocolDecoder { + + public NdtpV6ProtocolDecoder(Protocol protocol) { + super(protocol); + } + + private static final byte[] SIGNATURE = {0x7E, 0x7E}; + + private static final int NPL_FLAG_CRC = 2; + private static final int NPH_RESULT_OK = 0x00000000; + private static final int NPL_TYPE_NPH = 2; + private static final int NPL_ADDRESS_SERVER = 0; + + private static final int NPH_RESULT = 0; + + private static final int NPH_SRV_GENERIC_CONTROLS = 0; + private static final int NPH_SRV_NAVDATA = 1; + + private static final int NPH_SGC_RESULT = NPH_RESULT; + private static final int NPH_SGC_CONN_REQUEST = 100; + + private static final int NPH_SND_RESULT = NPH_RESULT; + + private void sendResponse( + Channel channel, int serviceId, long requestId) { + + ByteBuf content = Unpooled.buffer(); + content.writeShortLE(serviceId); + content.writeIntLE(NPH_SND_RESULT); + content.writeIntLE((int) requestId); + content.writeIntLE(NPH_RESULT_OK); + + ByteBuf response = Unpooled.buffer(); + response.writeBytes(SIGNATURE); + response.writeShortLE(content.readableBytes()); + response.writeShortLE(NPL_FLAG_CRC); // flags + response.writeShort(Checksum.crc16(Checksum.CRC16_MODBUS, content.nioBuffer())); + response.writeByte(NPL_TYPE_NPH); // type + response.writeIntLE(NPL_ADDRESS_SERVER); // peer address + response.writeShortLE(0); // request id + response.writeBytes(content); + content.release(); + + channel.writeAndFlush(new NetworkMessage(response, channel.remoteAddress())); + } + + private static final short MAIN_NAV_DATA = 0; + private static final short ADDITIONAL_NAV_DATA = 2; + + private void decodeData(ByteBuf buf, Position position) { + + short itemType; + short itemIndex; + + itemType = buf.readUnsignedByte(); + itemIndex = buf.readUnsignedByte(); + if (itemType == MAIN_NAV_DATA && (itemIndex == 0 || itemIndex == 1)) { + + position.setTime(new Date(buf.readUnsignedIntLE() * 1000)); + position.setLongitude(buf.readIntLE() / 10000000.0); + position.setLatitude(buf.readIntLE() / 10000000.0); + + short flags = buf.readUnsignedByte(); + position.setValid(BitUtil.check(flags, 7)); + if (BitUtil.check(flags, 1)) { + position.set(Position.KEY_ALARM, Position.ALARM_GENERAL); + } + + position.set(Position.KEY_BATTERY, buf.readUnsignedByte() * 20); + position.set(Position.KEY_OBD_SPEED, buf.readUnsignedShortLE()); + position.setSpeed(UnitsConverter.knotsFromKph(buf.readUnsignedShortLE())); + position.setCourse(buf.readUnsignedShortLE()); + + position.set(Position.KEY_ODOMETER, buf.readUnsignedShortLE()); + position.setAltitude(buf.readShortLE()); + position.set(Position.KEY_SATELLITES, buf.readUnsignedByte()); + position.set(Position.KEY_PDOP, buf.readUnsignedByte()); + } + + itemType = buf.readUnsignedByte(); + itemIndex = buf.readUnsignedByte(); + if (itemType == ADDITIONAL_NAV_DATA && itemIndex == 0) { + + position.set(Position.KEY_BATTERY_LEVEL, Math.max((buf.readUnsignedShortLE() - 3600) / 6, 100)); + position.set(Position.PREFIX_ADC + 2, buf.readUnsignedShortLE()); + position.set(Position.PREFIX_ADC + 3, buf.readUnsignedShortLE()); + position.set(Position.PREFIX_ADC + 4, buf.readUnsignedShortLE()); + + buf.readUnsignedByte(); // inputs + buf.readUnsignedByte(); // outputs + buf.readUnsignedShortLE(); // in1 count + buf.readUnsignedShortLE(); // in2 count + buf.readUnsignedShortLE(); // in3 count + buf.readUnsignedShortLE(); // in4 count + buf.readUnsignedIntLE(); // track length + + position.set(Position.KEY_ANTENNA, buf.readUnsignedByte()); + position.set(Position.KEY_GPS, buf.readUnsignedByte()); + position.set(Position.KEY_ACCELERATION, buf.readUnsignedByte()); + position.set(Position.KEY_POWER, buf.readUnsignedByte() * 200); + } + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress); + + buf.skipBytes(2); // signature + buf.readUnsignedShortLE(); // length + buf.readUnsignedShortLE(); // connection flags + buf.readUnsignedShortLE(); // checksum + buf.readUnsignedByte(); // type + buf.readUnsignedIntLE(); // address + buf.readUnsignedShortLE(); // identification + + int serviceId = buf.readUnsignedShortLE(); + int serviceType = buf.readUnsignedShortLE(); + buf.readUnsignedShortLE(); // request flags + long requestId = buf.readUnsignedIntLE(); + + if (deviceSession == null && serviceId == NPH_SRV_GENERIC_CONTROLS && serviceType == NPH_SGC_CONN_REQUEST) { + + buf.readUnsignedShortLE(); // version major + buf.readUnsignedShortLE(); // version minor + buf.readUnsignedShortLE(); // connection flags + + int deviceId = buf.readUnsignedShortLE(); + Position position = new Position(getProtocolName()); + deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(deviceId)); + position.setDeviceId(deviceSession.getDeviceId()); + + if (channel != null) { + sendResponse(channel, serviceId, requestId); + } + + position.set(Position.KEY_RESULT, String.valueOf(NPH_SGC_RESULT)); + position.setTime(new Date()); + getLastLocation(position, new Date()); + position.setValid(false); + + return position; + + } + + if (serviceId == NPH_SRV_NAVDATA) { + + deviceSession = getDeviceSession(channel, remoteAddress); + if (deviceSession == null) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + if (channel != null) { + sendResponse(channel, serviceId, requestId); + } + + decodeData(buf, position); + + return position; + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java b/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java new file mode 100644 index 000000000..7aac8658a --- /dev/null +++ b/src/main/java/org/traccar/protocol/NdtpV6ProtocolEncoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 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. + * 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 org.traccar.BaseProtocolEncoder; +import org.traccar.Protocol; +import org.traccar.model.Command; + +public class NdtpV6ProtocolEncoder extends BaseProtocolEncoder { + + public NdtpV6ProtocolEncoder(Protocol protocol) { + super(protocol); + } + + @Override + protected Object encodeCommand(Command command) { + switch (command.getType()) { + case Command.TYPE_IDENTIFICATION: + return "BB+IDNT"; + case Command.TYPE_REBOOT_DEVICE: + return "BB+RESET"; + case Command.TYPE_POSITION_SINGLE: + return "BB+RRCD"; + default: + return null; + } + } + +} diff --git a/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java index 244df6806..34c879cb8 100644 --- a/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/PiligrimProtocolDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 - 2020 Anton Tananaev (anton@traccar.org) + * Copyright 2014 - 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. @@ -22,16 +22,19 @@ 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.session.DeviceSession; import org.traccar.Protocol; import org.traccar.helper.BitUtil; import org.traccar.helper.DateBuilder; +import org.traccar.helper.Parser; +import org.traccar.helper.PatternBuilder; 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 PiligrimProtocolDecoder extends BaseHttpProtocolDecoder { @@ -47,6 +50,21 @@ public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder { public static final int MSG_GPS_SENSORS = 0xF2; public static final int MSG_EVENTS = 0xF3; + private static final Pattern PATTERN = new PatternBuilder() + .expression("[^$]+") + .text("$GPRMC,") + .number("(dd)(dd)(dd).d+,") // time (hhmmss) + .expression("([AV]),") // validity + .number("(dd)(dd.d+),") // latitude + .expression("([NS]),") + .number("(d{2,3})(dd.d+),") // longitude + .expression("([EW]),") + .number("(d+.d+),") // speed + .number("(d+.d+),") // course + .number("(dd)(dd)(dd),") // date (ddmmyy) + .any() + .compile(); + @Override protected Object decode( Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { @@ -150,6 +168,42 @@ public class PiligrimProtocolDecoder extends BaseHttpProtocolDecoder { } return positions; + + } else if (uri.startsWith("/push.do")) { + + sendResponse(channel, "PUSH.DO: OK"); + + String sentence = request.content().toString(StandardCharsets.US_ASCII); + + String[] parts = sentence.split("&"); + String phone = parts[1].substring(16); + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, phone); + if (deviceSession == null) { + return null; + } + + Parser parser = new Parser(PATTERN, parts[2]); + if (!parser.matches()) { + return null; + } + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + DateBuilder dateBuilder = new DateBuilder() + .setTime(parser.nextInt(), parser.nextInt(), parser.nextInt()); + + position.setValid(parser.next().equals("A")); + position.setLatitude(parser.nextCoordinate()); + position.setLongitude(parser.nextCoordinate()); + position.setSpeed(parser.nextDouble()); + position.setCourse(parser.nextDouble()); + + dateBuilder.setDateReverse(parser.nextInt(), parser.nextInt(), parser.nextInt()); + position.setTime(dateBuilder.getDate()); + + return position; + } return null; diff --git a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java index 89124cb22..4671a1088 100644 --- a/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java +++ b/src/main/java/org/traccar/protocol/TeltonikaProtocolDecoder.java @@ -325,6 +325,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { if (codec == CODEC_GH3000) { decodeGh3000Parameter(position, id, buf, length); } else { + int index = buf.readerIndex(); boolean decoded = false; for (var entry : PARAMETERS.getOrDefault(id, new HashMap<>()).entrySet()) { if (entry.getKey() == null || model != null && entry.getKey().contains(model)) { @@ -333,22 +334,48 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { break; } } - if (!decoded) { + if (decoded) { + buf.readerIndex(index + length); + } else { position.set(Position.PREFIX_IO + id, readValue(buf, length)); } } } - private void decodeNetwork(Position position) { - Integer cid = (Integer) position.getAttributes().remove("cid"); - Integer lac = (Integer) position.getAttributes().remove("lac"); - if (cid != null && lac != null) { - CellTower cellTower = CellTower.fromLacCid(getConfig(), lac, cid); - long operator = position.getInteger(Position.KEY_OPERATOR); - if (operator >= 1000) { - cellTower.setOperator(operator); + private void decodeCell( + Position position, Network network, String mncKey, String lacKey, String cidKey, String rssiKey) { + if (position.hasAttribute(mncKey) && position.hasAttribute(lacKey) && position.hasAttribute(cidKey)) { + CellTower cellTower = CellTower.from( + getConfig().getInteger(Keys.GEOLOCATION_MCC), + (Integer) position.getAttributes().remove(mncKey), + (Integer) position.getAttributes().remove(lacKey), + (Integer) position.getAttributes().remove(cidKey)); + cellTower.setSignalStrength((Integer) position.getAttributes().remove(rssiKey)); + network.addCellTower(cellTower); + } + } + + private void decodeNetwork(Position position, String model) { + if ("TAT100".equals(model)) { + Network network = new Network(); + decodeCell(position, network, "io1200", "io287", "io288", "io289"); + decodeCell(position, network, "io1201", "io290", "io291", "io292"); + decodeCell(position, network, "io1202", "io293", "io294", "io295"); + decodeCell(position, network, "io1203", "io296", "io297", "io298"); + if (network.getCellTowers() != null) { + position.setNetwork(network); + } + } else { + Integer cid = (Integer) position.getAttributes().remove("cid"); + Integer lac = (Integer) position.getAttributes().remove("lac"); + if (cid != null && lac != null) { + CellTower cellTower = CellTower.fromLacCid(getConfig(), lac, cid); + long operator = position.getInteger(Position.KEY_OPERATOR); + if (operator >= 1000) { + cellTower.setOperator(operator); + } + position.setNetwork(new Network(cellTower)); } - position.setNetwork(new Network(cellTower)); } } @@ -542,7 +569,7 @@ public class TeltonikaProtocolDecoder extends BaseProtocolDecoder { } } - decodeNetwork(position); + decodeNetwork(position, model); } diff --git a/src/main/java/org/traccar/protocol/ThurayaProtocol.java b/src/main/java/org/traccar/protocol/ThurayaProtocol.java new file mode 100644 index 000000000..f709a1183 --- /dev/null +++ b/src/main/java/org/traccar/protocol/ThurayaProtocol.java @@ -0,0 +1,39 @@ +/* + * Copyright 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. + * 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 javax.inject.Inject; + +public class ThurayaProtocol extends BaseProtocol { + + @Inject + public ThurayaProtocol(Config config) { + addServer(new TrackerServer(config, getName(), false) { + @Override + protected void addProtocolHandlers(PipelineBuilder pipeline, Config config) { + pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 2, 2, -4, 0)); + pipeline.addLast(new ThurayaProtocolDecoder(ThurayaProtocol.this)); + } + }); + } + +} diff --git a/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java b/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java new file mode 100644 index 000000000..a287ece34 --- /dev/null +++ b/src/main/java/org/traccar/protocol/ThurayaProtocolDecoder.java @@ -0,0 +1,195 @@ +/* + * Copyright 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. + * 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.NetworkMessage; +import org.traccar.Protocol; +import org.traccar.helper.BitUtil; +import org.traccar.helper.DateBuilder; +import org.traccar.model.Position; +import org.traccar.session.DeviceSession; + +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.LinkedList; +import java.util.List; + +public class ThurayaProtocolDecoder extends BaseProtocolDecoder { + + public ThurayaProtocolDecoder(Protocol protocol) { + super(protocol); + } + + public static final int MSG_EVENT = 0x5101; + public static final int MSG_PERIODIC_REPORT = 0x7101; + public static final int MSG_SETTING_RESPONSE = 0x8115; + public static final int MSG_ACK = 0x9901; + + private static int checksum(ByteBuffer buf) { + int crc = 0; + while (buf.hasRemaining()) { + crc += buf.get(); + } + crc = ~crc; + crc += 1; + return crc; + } + + private void sendResponse(Channel channel, SocketAddress remoteAddress, long id, int type) { + if (channel != null) { + ByteBuf response = Unpooled.buffer(); + response.writeCharSequence("#T", StandardCharsets.US_ASCII); + response.writeShort(15); // length + response.writeShort(MSG_ACK); + response.writeInt((int) id); + response.writeShort(type); + response.writeShort(1); // server ok + response.writeShort(checksum(response.nioBuffer())); + channel.writeAndFlush(new NetworkMessage(response, remoteAddress)); + } + } + + private void decodeLocation(ByteBuf buf, Position position) { + + position.setValid(true); + + DateBuilder dateBuilder = new DateBuilder(); + + int date = buf.readInt(); + dateBuilder.setDay(date % 100); + date /= 100; + dateBuilder.setMonth(date % 100); + date /= 100; + dateBuilder.setYear(date); + + int time = buf.readInt(); + dateBuilder.setSecond(time % 100); + time /= 100; + dateBuilder.setMinute(time % 100); + time /= 100; + dateBuilder.setHour(time); + + position.setTime(dateBuilder.getDate()); + + position.setLongitude(buf.readInt() / 1000000.0); + position.setLatitude(buf.readInt() / 1000000.0); + + int data = buf.readUnsignedShort(); + + int ignition = BitUtil.from(data, 12); + if (ignition == 1) { + position.set(Position.KEY_IGNITION, true); + } else if (ignition == 2) { + position.set(Position.KEY_IGNITION, false); + } + + position.setCourse(BitUtil.to(data, 12)); + position.setSpeed(buf.readShort()); + + position.set(Position.KEY_RPM, buf.readShort()); + + position.set("data", readString(buf)); + } + + private String decodeAlarm(int event) { + switch (event) { + case 10: + return Position.ALARM_VIBRATION; + case 11: + return Position.ALARM_OVERSPEED; + case 12: + return Position.ALARM_POWER_CUT; + case 13: + return Position.ALARM_LOW_BATTERY; + case 18: + return Position.ALARM_GPS_ANTENNA_CUT; + case 20: + return Position.ALARM_ACCELERATION; + case 21: + return Position.ALARM_BRAKING; + default: + return null; + } + } + + private String readString(ByteBuf buf) { + int endIndex = buf.indexOf(buf.readerIndex(), buf.writerIndex(), (byte) 0); + CharSequence value = buf.readCharSequence(endIndex - buf.readerIndex(), StandardCharsets.US_ASCII); + buf.readUnsignedByte(); // delimiter + return value.toString(); + } + + @Override + protected Object decode( + Channel channel, SocketAddress remoteAddress, Object msg) throws Exception { + + ByteBuf buf = (ByteBuf) msg; + + buf.skipBytes(2); // service + buf.readUnsignedShort(); // length + int type = buf.readUnsignedShort(); + long id = buf.readUnsignedInt(); + + sendResponse(channel, remoteAddress, id, type); + + DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, String.valueOf(id)); + if (deviceSession == null) { + return null; + } + + if (type == MSG_EVENT) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + decodeLocation(buf, position); + + int event = buf.readUnsignedByte(); + position.set(Position.KEY_ALARM, decodeAlarm(event)); + position.set(Position.KEY_EVENT, event); + position.set("eventData", readString(buf)); + + return position; + + } else if (type == MSG_PERIODIC_REPORT) { + + List<Position> positions = new LinkedList<>(); + + int count = buf.readUnsignedByte(); + for (int i = 0; i < count; i++) { + + Position position = new Position(getProtocolName()); + position.setDeviceId(deviceSession.getDeviceId()); + + decodeLocation(buf, position); + + positions.add(position); + + } + + return positions; + + } + + return null; + } + +} diff --git a/src/main/java/org/traccar/session/cache/CacheManager.java b/src/main/java/org/traccar/session/cache/CacheManager.java index 6f6b648fa..64397b368 100644 --- a/src/main/java/org/traccar/session/cache/CacheManager.java +++ b/src/main/java/org/traccar/session/cache/CacheManager.java @@ -132,7 +132,7 @@ public class CacheManager implements BroadcastInterface { lock.readLock().lock(); var users = deviceLinks.get(deviceId).get(User.class).stream() .collect(Collectors.toUnmodifiableSet()); - return notificationUsers.get(notificationId).stream() + return notificationUsers.getOrDefault(notificationId, new LinkedList<>()).stream() .filter(user -> users.contains(user.getId())) .collect(Collectors.toUnmodifiableList()); } finally { |